Merge commit '55d9a533b309119c8acd13061581b43ae8840823' into sync-from-ra

This commit is contained in:
Laurențiu Nicola 2024-04-20 19:17:08 +03:00
commit c8c635a3f8
237 changed files with 6273 additions and 3337 deletions

View file

@ -22,6 +22,8 @@ Otherwise please try to provide information which will help us to fix the issue
**rustc version**: (eg. output of `rustc -V`) **rustc version**: (eg. output of `rustc -V`)
**editor or extension**: (eg. VSCode, Vim, Emacs, etc. For VSCode users, specify your extension version; for users of other editors, provide the distribution if applicable)
**relevant settings**: (eg. client settings, or environment variables like `CARGO`, `RUSTC`, `RUSTUP_HOME` or `CARGO_HOME`) **relevant settings**: (eg. client settings, or environment variables like `CARGO`, `RUSTC`, `RUSTUP_HOME` or `CARGO_HOME`)
**repository link (if public, optional)**: (eg. [rust-analyzer](https://github.com/rust-lang/rust-analyzer)) **repository link (if public, optional)**: (eg. [rust-analyzer](https://github.com/rust-lang/rust-analyzer))

View file

@ -91,7 +91,7 @@ jobs:
run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }} run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }}
- name: Test - name: Test
if: matrix.os == 'ubuntu-latest' || github.event_name == 'push' if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest' || github.event_name == 'push'
run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet
- name: Switch to stable toolchain - name: Switch to stable toolchain

View file

@ -58,7 +58,7 @@ jobs:
key: ${{ runner.os }}-target-${{ github.sha }} key: ${{ runner.os }}-target-${{ github.sha }}
- name: Upload build metrics - name: Upload build metrics
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: build-${{ github.sha }} name: build-${{ github.sha }}
path: target/build.json path: target/build.json
@ -95,7 +95,7 @@ jobs:
run: cargo xtask metrics "${{ matrix.names }}" run: cargo xtask metrics "${{ matrix.names }}"
- name: Upload metrics - name: Upload metrics
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ${{ matrix.names }}-${{ github.sha }} name: ${{ matrix.names }}-${{ github.sha }}
path: target/${{ matrix.names }}.json path: target/${{ matrix.names }}.json
@ -109,32 +109,32 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Download build metrics - name: Download build metrics
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: build-${{ github.sha }} name: build-${{ github.sha }}
- name: Download self metrics - name: Download self metrics
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: self-${{ github.sha }} name: self-${{ github.sha }}
- name: Download ripgrep-13.0.0 metrics - name: Download ripgrep-13.0.0 metrics
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: ripgrep-13.0.0-${{ github.sha }} name: ripgrep-13.0.0-${{ github.sha }}
- name: Download webrender-2022 metrics - name: Download webrender-2022 metrics
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: webrender-2022-${{ github.sha }} name: webrender-2022-${{ github.sha }}
- name: Download diesel-1.4.8 metrics - name: Download diesel-1.4.8 metrics
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: diesel-1.4.8-${{ github.sha }} name: diesel-1.4.8-${{ github.sha }}
- name: Download hyper-0.14.18 metrics - name: Download hyper-0.14.18 metrics
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
with: with:
name: hyper-0.14.18-${{ github.sha }} name: hyper-0.14.18-${{ github.sha }}

30
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,30 @@
# Contributing to rust-analyzer
Thank you for your interest in contributing to rust-analyzer! There are many ways to contribute
and we appreciate all of them.
To get a quick overview of the crates and structure of the project take a look at the
[./docs/dev](./docs/dev) folder.
If you have any questions please ask them in the [rust-analyzer zulip stream](
https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer) or if unsure where
to start out when working on a concrete issue drop a comment on the related issue for mentoring
instructions (general discussions are recommended to happen on zulip though).
## Fixing a bug or improving a feature
Generally it's fine to just work on these kinds of things and put a pull-request out for it. If there
is an issue accompanying it make sure to link it in the pull request description so it can be closed
afterwards or linked for context.
If you want to find something to fix or work on keep a look out for the `C-bug` and `C-enhancement`
labels.
## Implementing a new feature
It's advised to first open an issue for any kind of new feature so the team can tell upfront whether
the feature is desirable or not before any implementation work happens. We want to minimize the
possibility of someone putting a lot of work into a feature that is then going to waste as we deem
it out of scope (be it due to generally not fitting in with rust-analyzer, or just not having the
maintenance capacity). If there already is a feature issue open but it is not clear whether it is
considered accepted feel free to just drop a comment and ask!

75
Cargo.lock generated
View file

@ -161,9 +161,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chalk-derive" name = "chalk-derive"
version = "0.96.0" version = "0.97.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5676cea088c32290fe65c82895be9d06dd21e0fa49bb97ca840529e9417ab71a" checksum = "92a0aedc4ac2adc5c0b7dc9ec38c5c816284ad28da6d4ecd01873b9683f54972"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -173,20 +173,19 @@ dependencies = [
[[package]] [[package]]
name = "chalk-ir" name = "chalk-ir"
version = "0.96.0" version = "0.97.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff550c2cdd63ff74394214dce03d06386928a641c0f08837535f04af573a966d" checksum = "db18493569b190f7266a04901e520fc3a5c00564475154287906f8a27302c119"
dependencies = [ dependencies = [
"bitflags 2.4.2", "bitflags 2.4.2",
"chalk-derive", "chalk-derive",
"lazy_static",
] ]
[[package]] [[package]]
name = "chalk-recursive" name = "chalk-recursive"
version = "0.96.0" version = "0.97.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c4559e5c9b200240453b07d893f9c3c74413b53b0d33cbe272c68b0b77aa1c3" checksum = "ae4ba8ce5bd2e1b59f1f79495bc8704db09a8285e51cc5ddf01d9baee1bf447d"
dependencies = [ dependencies = [
"chalk-derive", "chalk-derive",
"chalk-ir", "chalk-ir",
@ -197,9 +196,9 @@ dependencies = [
[[package]] [[package]]
name = "chalk-solve" name = "chalk-solve"
version = "0.96.0" version = "0.97.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0882e68ce9eb5a0a2413806538494d19df6ee520ab17d1faf489e952f32e98b8" checksum = "b2ec1b3b7f7b1ec38f099ef39c2bc3ea29335be1b8316d114baff46d96d131e9"
dependencies = [ dependencies = [
"chalk-derive", "chalk-derive",
"chalk-ir", "chalk-ir",
@ -552,6 +551,7 @@ dependencies = [
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"limit", "limit",
"mbe", "mbe",
"parser",
"rustc-hash", "rustc-hash",
"smallvec", "smallvec",
"span", "span",
@ -695,6 +695,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"base-db", "base-db",
"bitflags 2.4.2",
"cov-mark", "cov-mark",
"crossbeam-channel", "crossbeam-channel",
"either", "either",
@ -781,6 +782,7 @@ checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
"serde",
] ]
[[package]] [[package]]
@ -1594,6 +1596,7 @@ dependencies = [
"ide", "ide",
"ide-db", "ide-db",
"ide-ssr", "ide-ssr",
"indexmap",
"itertools", "itertools",
"load-cargo", "load-cargo",
"lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1622,6 +1625,7 @@ dependencies = [
"test-fixture", "test-fixture",
"test-utils", "test-utils",
"tikv-jemallocator", "tikv-jemallocator",
"toml",
"toolchain", "toolchain",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -1775,6 +1779,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "sharded-slab" name = "sharded-slab"
version = "0.1.7" version = "0.1.7"
@ -1822,6 +1835,7 @@ dependencies = [
"salsa", "salsa",
"stdx", "stdx",
"syntax", "syntax",
"text-size",
"vfs", "vfs",
] ]
@ -2025,6 +2039,40 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]] [[package]]
name = "toolchain" name = "toolchain"
version = "0.0.0" version = "0.0.0"
@ -2401,6 +2449,15 @@ version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
name = "winnow"
version = "0.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "write-json" name = "write-json"
version = "0.1.4" version = "0.1.4"

View file

@ -22,6 +22,7 @@ smol_str.opt-level = 3
text-size.opt-level = 3 text-size.opt-level = 3
# This speeds up `cargo xtask dist`. # This speeds up `cargo xtask dist`.
miniz_oxide.opt-level = 3 miniz_oxide.opt-level = 3
salsa.opt-level = 3
[profile.release] [profile.release]
incremental = true incremental = true
@ -106,10 +107,10 @@ arrayvec = "0.7.4"
bitflags = "2.4.1" bitflags = "2.4.1"
cargo_metadata = "0.18.1" cargo_metadata = "0.18.1"
camino = "1.1.6" camino = "1.1.6"
chalk-solve = { version = "0.96.0", default-features = false } chalk-solve = { version = "0.97.0", default-features = false }
chalk-ir = "0.96.0" chalk-ir = "0.97.0"
chalk-recursive = { version = "0.96.0", default-features = false } chalk-recursive = { version = "0.97.0", default-features = false }
chalk-derive = "0.96.0" chalk-derive = "0.97.0"
command-group = "2.0.1" command-group = "2.0.1"
crossbeam-channel = "0.5.8" crossbeam-channel = "0.5.8"
dissimilar = "1.0.7" dissimilar = "1.0.7"
@ -188,6 +189,8 @@ enum_variant_names = "allow"
new_ret_no_self = "allow" new_ret_no_self = "allow"
# Has a bunch of false positives # Has a bunch of false positives
useless_asref = "allow" useless_asref = "allow"
# Has false positives
assigning_clones = "allow"
## Following lints should be tackled at some point ## Following lints should be tackled at some point
too_many_arguments = "allow" too_many_arguments = "allow"

View file

@ -13,8 +13,9 @@ https://rust-analyzer.github.io/manual.html#installation
## Documentation ## Documentation
If you want to **contribute** to rust-analyzer or are just curious about how If you want to **contribute** to rust-analyzer check out the [CONTRIBUTING.md](./CONTRIBUTING.md) or
things work under the hood, check the [./docs/dev](./docs/dev) folder. if you are just curious about how things work under the hood, check the [./docs/dev](./docs/dev)
folder.
If you want to **use** rust-analyzer's language server with your editor of If you want to **use** rust-analyzer's language server with your editor of
choice, check [the manual](https://rust-analyzer.github.io/manual.html) folder. choice, check [the manual](https://rust-analyzer.github.io/manual.html) folder.

View file

@ -19,6 +19,10 @@ use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`, // Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`,
// then the crate for the proc-macro hasn't been build yet as the build data is missing. // then the crate for the proc-macro hasn't been build yet as the build data is missing.
pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>; pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SourceRootId(pub u32);
/// Files are grouped into source roots. A source root is a directory on the /// Files are grouped into source roots. A source root is a directory on the
/// file systems which is watched for changes. Typically it corresponds to a /// file systems which is watched for changes. Typically it corresponds to a
/// Rust crate. Source roots *might* be nested: in this case, a file belongs to /// Rust crate. Source roots *might* be nested: in this case, a file belongs to
@ -26,9 +30,6 @@ pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf)
/// source root, and the analyzer does not know the root path of the source root at /// source root, and the analyzer does not know the root path of the source root at
/// all. So, a file from one source root can't refer to a file in another source /// all. So, a file from one source root can't refer to a file in another source
/// root by path. /// root by path.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SourceRootId(pub u32);
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct SourceRoot { pub struct SourceRoot {
/// Sysroot or crates.io library. /// Sysroot or crates.io library.
@ -285,20 +286,39 @@ pub struct CrateData {
/// For purposes of analysis, crates are anonymous (only names in /// For purposes of analysis, crates are anonymous (only names in
/// `Dependency` matters), this name should only be used for UI. /// `Dependency` matters), this name should only be used for UI.
pub display_name: Option<CrateDisplayName>, pub display_name: Option<CrateDisplayName>,
pub cfg_options: CfgOptions, pub cfg_options: Arc<CfgOptions>,
/// The cfg options that could be used by the crate /// The cfg options that could be used by the crate
pub potential_cfg_options: Option<CfgOptions>, pub potential_cfg_options: Option<Arc<CfgOptions>>,
pub env: Env, pub env: Env,
pub dependencies: Vec<Dependency>, pub dependencies: Vec<Dependency>,
pub origin: CrateOrigin, pub origin: CrateOrigin,
pub is_proc_macro: bool, pub is_proc_macro: bool,
} }
#[derive(Default, Debug, Clone, PartialEq, Eq)] #[derive(Default, Clone, PartialEq, Eq)]
pub struct Env { pub struct Env {
entries: FxHashMap<String, String>, entries: FxHashMap<String, String>,
} }
impl fmt::Debug for Env {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct EnvDebug<'s>(Vec<(&'s String, &'s String)>);
impl fmt::Debug for EnvDebug<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.0.iter().copied()).finish()
}
}
f.debug_struct("Env")
.field("entries", &{
let mut entries: Vec<_> = self.entries.iter().collect();
entries.sort();
EnvDebug(entries)
})
.finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Dependency { pub struct Dependency {
pub crate_id: CrateId, pub crate_id: CrateId,
@ -328,12 +348,13 @@ impl CrateGraph {
edition: Edition, edition: Edition,
display_name: Option<CrateDisplayName>, display_name: Option<CrateDisplayName>,
version: Option<String>, version: Option<String>,
cfg_options: CfgOptions, cfg_options: Arc<CfgOptions>,
potential_cfg_options: Option<CfgOptions>, potential_cfg_options: Option<Arc<CfgOptions>>,
env: Env, mut env: Env,
is_proc_macro: bool, is_proc_macro: bool,
origin: CrateOrigin, origin: CrateOrigin,
) -> CrateId { ) -> CrateId {
env.entries.shrink_to_fit();
let data = CrateData { let data = CrateData {
root_file_id, root_file_id,
edition, edition,
@ -650,16 +671,24 @@ impl FromIterator<(String, String)> for Env {
} }
impl Env { impl Env {
pub fn set(&mut self, env: &str, value: String) { pub fn set(&mut self, env: &str, value: impl Into<String>) {
self.entries.insert(env.to_owned(), value); self.entries.insert(env.to_owned(), value.into());
} }
pub fn get(&self, env: &str) -> Option<String> { pub fn get(&self, env: &str) -> Option<String> {
self.entries.get(env).cloned() self.entries.get(env).cloned()
} }
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> { pub fn extend_from_other(&mut self, other: &Env) {
self.entries.iter().map(|(k, v)| (k.as_str(), v.as_str())) self.entries.extend(other.entries.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
}
}
impl From<Env> for Vec<(String, String)> {
fn from(env: Env) -> Vec<(String, String)> {
let mut entries: Vec<_> = env.entries.into_iter().collect();
entries.sort();
entries
} }
} }

View file

@ -45,7 +45,7 @@ pub trait Upcast<T: ?Sized> {
pub const DEFAULT_FILE_TEXT_LRU_CAP: usize = 16; pub const DEFAULT_FILE_TEXT_LRU_CAP: usize = 16;
pub const DEFAULT_PARSE_LRU_CAP: usize = 128; pub const DEFAULT_PARSE_LRU_CAP: usize = 128;
pub const DEFAULT_BORROWCK_LRU_CAP: usize = 1024; pub const DEFAULT_BORROWCK_LRU_CAP: usize = 2024;
pub trait FileLoader { pub trait FileLoader {
/// Text of the file. /// Text of the file.
@ -83,7 +83,8 @@ fn toolchain_channel(db: &dyn SourceDatabase, krate: CrateId) -> Option<ReleaseC
fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
let _p = tracing::span!(tracing::Level::INFO, "parse_query", ?file_id).entered(); let _p = tracing::span!(tracing::Level::INFO, "parse_query", ?file_id).entered();
let text = db.file_text(file_id); let text = db.file_text(file_id);
SourceFile::parse(&text) // FIXME: Edition based parsing
SourceFile::parse(&text, span::Edition::CURRENT)
} }
/// We don't want to give HIR knowledge of source roots, hence we extract these /// We don't want to give HIR knowledge of source roots, hence we extract these

View file

@ -58,13 +58,6 @@ impl CfgOptions {
self.enabled.insert(CfgAtom::KeyValue { key, value }); self.enabled.insert(CfgAtom::KeyValue { key, value });
} }
pub fn difference<'a>(
&'a self,
other: &'a CfgOptions,
) -> impl Iterator<Item = &'a CfgAtom> + 'a {
self.enabled.difference(&other.enabled)
}
pub fn apply_diff(&mut self, diff: CfgDiff) { pub fn apply_diff(&mut self, diff: CfgDiff) {
for atom in diff.enable { for atom in diff.enable {
self.enabled.insert(atom); self.enabled.insert(atom);

View file

@ -1,12 +1,12 @@
use arbitrary::{Arbitrary, Unstructured}; use arbitrary::{Arbitrary, Unstructured};
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY};
use syntax::{ast, AstNode}; use syntax::{ast, AstNode, Edition};
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
fn assert_parse_result(input: &str, expected: CfgExpr) { fn assert_parse_result(input: &str, expected: CfgExpr) {
let source_file = ast::SourceFile::parse(input).ok().unwrap(); let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY);
let cfg = CfgExpr::parse(&tt); let cfg = CfgExpr::parse(&tt);
@ -14,7 +14,7 @@ fn assert_parse_result(input: &str, expected: CfgExpr) {
} }
fn check_dnf(input: &str, expect: Expect) { fn check_dnf(input: &str, expect: Expect) {
let source_file = ast::SourceFile::parse(input).ok().unwrap(); let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY);
let cfg = CfgExpr::parse(&tt); let cfg = CfgExpr::parse(&tt);
@ -23,7 +23,7 @@ fn check_dnf(input: &str, expect: Expect) {
} }
fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
let source_file = ast::SourceFile::parse(input).ok().unwrap(); let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY);
let cfg = CfgExpr::parse(&tt); let cfg = CfgExpr::parse(&tt);
@ -34,7 +34,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
#[track_caller] #[track_caller]
fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
let source_file = ast::SourceFile::parse(input).ok().unwrap(); let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY);
let cfg = CfgExpr::parse(&tt); let cfg = CfgExpr::parse(&tt);

View file

@ -4,12 +4,13 @@
use std::{ use std::{
ffi::OsString, ffi::OsString,
fmt, io, fmt, io,
marker::PhantomData,
path::PathBuf, path::PathBuf,
process::{ChildStderr, ChildStdout, Command, Stdio}, process::{ChildStderr, ChildStdout, Command, Stdio},
}; };
use command_group::{CommandGroup, GroupChild}; use command_group::{CommandGroup, GroupChild};
use crossbeam_channel::{unbounded, Receiver, Sender}; use crossbeam_channel::Sender;
use stdx::process::streaming_output; use stdx::process::streaming_output;
/// Cargo output is structured as a one JSON per line. This trait abstracts parsing one line of /// Cargo output is structured as a one JSON per line. This trait abstracts parsing one line of
@ -99,10 +100,10 @@ pub(crate) struct CommandHandle<T> {
/// a read syscall dropping and therefore terminating the process is our best option. /// a read syscall dropping and therefore terminating the process is our best option.
child: JodGroupChild, child: JodGroupChild,
thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>, thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>,
pub(crate) receiver: Receiver<T>,
program: OsString, program: OsString,
arguments: Vec<OsString>, arguments: Vec<OsString>,
current_dir: Option<PathBuf>, current_dir: Option<PathBuf>,
_phantom: PhantomData<T>,
} }
impl<T> fmt::Debug for CommandHandle<T> { impl<T> fmt::Debug for CommandHandle<T> {
@ -116,7 +117,7 @@ impl<T> fmt::Debug for CommandHandle<T> {
} }
impl<T: ParseFromLine> CommandHandle<T> { impl<T: ParseFromLine> CommandHandle<T> {
pub(crate) fn spawn(mut command: Command) -> std::io::Result<Self> { pub(crate) fn spawn(mut command: Command, sender: Sender<T>) -> std::io::Result<Self> {
command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
let mut child = command.group_spawn().map(JodGroupChild)?; let mut child = command.group_spawn().map(JodGroupChild)?;
@ -127,13 +128,12 @@ impl<T: ParseFromLine> CommandHandle<T> {
let stdout = child.0.inner().stdout.take().unwrap(); let stdout = child.0.inner().stdout.take().unwrap();
let stderr = child.0.inner().stderr.take().unwrap(); let stderr = child.0.inner().stderr.take().unwrap();
let (sender, receiver) = unbounded();
let actor = CargoActor::<T>::new(sender, stdout, stderr); let actor = CargoActor::<T>::new(sender, stdout, stderr);
let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
.name("CommandHandle".to_owned()) .name("CommandHandle".to_owned())
.spawn(move || actor.run()) .spawn(move || actor.run())
.expect("failed to spawn thread"); .expect("failed to spawn thread");
Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver }) Ok(CommandHandle { program, arguments, current_dir, child, thread, _phantom: PhantomData })
} }
pub(crate) fn cancel(mut self) { pub(crate) fn cancel(mut self) {

View file

@ -41,19 +41,50 @@ pub enum InvocationLocation {
Workspace, Workspace,
} }
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CargoOptions {
pub target_triples: Vec<String>,
pub all_targets: bool,
pub no_default_features: bool,
pub all_features: bool,
pub features: Vec<String>,
pub extra_args: Vec<String>,
pub extra_env: FxHashMap<String, String>,
pub target_dir: Option<Utf8PathBuf>,
}
impl CargoOptions {
fn apply_on_command(&self, cmd: &mut Command) {
for target in &self.target_triples {
cmd.args(["--target", target.as_str()]);
}
if self.all_targets {
cmd.arg("--all-targets");
}
if self.all_features {
cmd.arg("--all-features");
} else {
if self.no_default_features {
cmd.arg("--no-default-features");
}
if !self.features.is_empty() {
cmd.arg("--features");
cmd.arg(self.features.join(" "));
}
}
if let Some(target_dir) = &self.target_dir {
cmd.arg("--target-dir").arg(target_dir);
}
cmd.envs(&self.extra_env);
}
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlycheckConfig { pub enum FlycheckConfig {
CargoCommand { CargoCommand {
command: String, command: String,
target_triples: Vec<String>, options: CargoOptions,
all_targets: bool,
no_default_features: bool,
all_features: bool,
features: Vec<String>,
extra_args: Vec<String>,
extra_env: FxHashMap<String, String>,
ansi_color_output: bool, ansi_color_output: bool,
target_dir: Option<Utf8PathBuf>,
}, },
CustomCommand { CustomCommand {
command: String, command: String,
@ -184,6 +215,8 @@ struct FlycheckActor {
/// have to wrap sub-processes output handling in a thread and pass messages /// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel. /// back over a channel.
command_handle: Option<CommandHandle<CargoCheckMessage>>, command_handle: Option<CommandHandle<CargoCheckMessage>>,
/// The receiver side of the channel mentioned above.
command_receiver: Option<Receiver<CargoCheckMessage>>,
} }
enum Event { enum Event {
@ -209,6 +242,7 @@ impl FlycheckActor {
sysroot_root, sysroot_root,
root: workspace_root, root: workspace_root,
command_handle: None, command_handle: None,
command_receiver: None,
} }
} }
@ -217,14 +251,13 @@ impl FlycheckActor {
} }
fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> { fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> {
let check_chan = self.command_handle.as_ref().map(|cargo| &cargo.receiver);
if let Ok(msg) = inbox.try_recv() { if let Ok(msg) = inbox.try_recv() {
// give restarts a preference so check outputs don't block a restart or stop // give restarts a preference so check outputs don't block a restart or stop
return Some(Event::RequestStateChange(msg)); return Some(Event::RequestStateChange(msg));
} }
select! { select! {
recv(inbox) -> msg => msg.ok().map(Event::RequestStateChange), recv(inbox) -> msg => msg.ok().map(Event::RequestStateChange),
recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())), recv(self.command_receiver.as_ref().unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
} }
} }
@ -253,10 +286,12 @@ impl FlycheckActor {
let formatted_command = format!("{:?}", command); let formatted_command = format!("{:?}", command);
tracing::debug!(?command, "will restart flycheck"); tracing::debug!(?command, "will restart flycheck");
match CommandHandle::spawn(command) { let (sender, receiver) = unbounded();
match CommandHandle::spawn(command, sender) {
Ok(command_handle) => { Ok(command_handle) => {
tracing::debug!(command = formatted_command, "did restart flycheck"); tracing::debug!(command = formatted_command, "did restart flycheck");
self.command_handle = Some(command_handle); self.command_handle = Some(command_handle);
self.command_receiver = Some(receiver);
self.report_progress(Progress::DidStart); self.report_progress(Progress::DidStart);
} }
Err(error) => { Err(error) => {
@ -272,13 +307,15 @@ impl FlycheckActor {
// Watcher finished // Watcher finished
let command_handle = self.command_handle.take().unwrap(); let command_handle = self.command_handle.take().unwrap();
self.command_receiver.take();
let formatted_handle = format!("{:?}", command_handle); let formatted_handle = format!("{:?}", command_handle);
let res = command_handle.join(); let res = command_handle.join();
if res.is_err() { if let Err(error) = &res {
tracing::error!( tracing::error!(
"Flycheck failed to run the following command: {}", "Flycheck failed to run the following command: {}, error={}",
formatted_handle formatted_handle,
error
); );
} }
self.report_progress(Progress::DidFinish(res)); self.report_progress(Progress::DidFinish(res));
@ -332,18 +369,7 @@ impl FlycheckActor {
saved_file: Option<&AbsPath>, saved_file: Option<&AbsPath>,
) -> Option<Command> { ) -> Option<Command> {
let (mut cmd, args) = match &self.config { let (mut cmd, args) = match &self.config {
FlycheckConfig::CargoCommand { FlycheckConfig::CargoCommand { command, options, ansi_color_output } => {
command,
target_triples,
no_default_features,
all_targets,
all_features,
extra_args,
features,
extra_env,
ansi_color_output,
target_dir,
} => {
let mut cmd = Command::new(Tool::Cargo.path()); let mut cmd = Command::new(Tool::Cargo.path());
if let Some(sysroot_root) = &self.sysroot_root { if let Some(sysroot_root) = &self.sysroot_root {
cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root)); cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root));
@ -365,28 +391,8 @@ impl FlycheckActor {
cmd.arg("--manifest-path"); cmd.arg("--manifest-path");
cmd.arg(self.root.join("Cargo.toml")); cmd.arg(self.root.join("Cargo.toml"));
for target in target_triples { options.apply_on_command(&mut cmd);
cmd.args(["--target", target.as_str()]); (cmd, options.extra_args.clone())
}
if *all_targets {
cmd.arg("--all-targets");
}
if *all_features {
cmd.arg("--all-features");
} else {
if *no_default_features {
cmd.arg("--no-default-features");
}
if !features.is_empty() {
cmd.arg("--features");
cmd.arg(features.join(" "));
}
}
if let Some(target_dir) = target_dir {
cmd.arg("--target-dir").arg(target_dir);
}
cmd.envs(extra_env);
(cmd, extra_args.clone())
} }
FlycheckConfig::CustomCommand { FlycheckConfig::CustomCommand {
command, command,

View file

@ -3,11 +3,15 @@
use std::process::Command; use std::process::Command;
use crossbeam_channel::Receiver; use crossbeam_channel::Sender;
use paths::AbsPath;
use serde::Deserialize; use serde::Deserialize;
use toolchain::Tool; use toolchain::Tool;
use crate::command::{CommandHandle, ParseFromLine}; use crate::{
command::{CommandHandle, ParseFromLine},
CargoOptions,
};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(tag = "event", rename_all = "camelCase")] #[serde(tag = "event", rename_all = "camelCase")]
@ -51,30 +55,34 @@ impl ParseFromLine for CargoTestMessage {
#[derive(Debug)] #[derive(Debug)]
pub struct CargoTestHandle { pub struct CargoTestHandle {
handle: CommandHandle<CargoTestMessage>, _handle: CommandHandle<CargoTestMessage>,
} }
// Example of a cargo test command: // Example of a cargo test command:
// cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json // cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json
impl CargoTestHandle { impl CargoTestHandle {
pub fn new(path: Option<&str>) -> std::io::Result<Self> { pub fn new(
path: Option<&str>,
options: CargoOptions,
root: &AbsPath,
sender: Sender<CargoTestMessage>,
) -> std::io::Result<Self> {
let mut cmd = Command::new(Tool::Cargo.path()); let mut cmd = Command::new(Tool::Cargo.path());
cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.env("RUSTC_BOOTSTRAP", "1");
cmd.arg("test"); cmd.arg("test");
cmd.arg("--workspace"); cmd.arg("--workspace");
// --no-fail-fast is needed to ensure that all requested tests will run // --no-fail-fast is needed to ensure that all requested tests will run
cmd.arg("--no-fail-fast"); cmd.arg("--no-fail-fast");
cmd.arg("--manifest-path");
cmd.arg(root.join("Cargo.toml"));
options.apply_on_command(&mut cmd);
cmd.arg("--"); cmd.arg("--");
if let Some(path) = path { if let Some(path) = path {
cmd.arg(path); cmd.arg(path);
} }
cmd.args(["-Z", "unstable-options"]); cmd.args(["-Z", "unstable-options"]);
cmd.arg("--format=json"); cmd.arg("--format=json");
Ok(Self { handle: CommandHandle::spawn(cmd)? }) Ok(Self { _handle: CommandHandle::spawn(cmd, sender)? })
}
pub fn receiver(&self) -> &Receiver<CargoTestMessage> {
&self.handle.receiver
} }
} }

View file

@ -54,7 +54,7 @@ test-utils.workspace = true
test-fixture.workspace = true test-fixture.workspace = true
[features] [features]
in-rust-tree = [] in-rust-tree = ["hir-expand/in-rust-tree"]
[lints] [lints]
workspace = true workspace = true

View file

@ -5,7 +5,7 @@ pub mod builtin;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use std::{hash::Hash, ops, slice::Iter as SliceIter}; use std::{borrow::Cow, hash::Hash, ops, slice::Iter as SliceIter};
use base_db::CrateId; use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions}; use cfg::{CfgExpr, CfgOptions};
@ -141,6 +141,10 @@ impl Attrs {
} }
} }
pub fn cfgs(&self) -> impl Iterator<Item = CfgExpr> + '_ {
self.by_key("cfg").tt_values().map(CfgExpr::parse)
}
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
match self.cfg() { match self.cfg() {
None => true, None => true,
@ -569,6 +573,10 @@ impl<'attr> AttrQuery<'attr> {
self.attrs().find_map(|attr| attr.string_value()) self.attrs().find_map(|attr| attr.string_value())
} }
pub fn string_value_unescape(self) -> Option<Cow<'attr, str>> {
self.attrs().find_map(|attr| attr.string_value_unescape())
}
pub fn exists(self) -> bool { pub fn exists(self) -> bool {
self.attrs().next().is_some() self.attrs().next().is_some()
} }

View file

@ -11,7 +11,7 @@ use syntax::{ast, AstNode, TextRange};
use crate::attr::{DocAtom, DocExpr}; use crate::attr::{DocAtom, DocExpr};
fn assert_parse_result(input: &str, expected: DocExpr) { fn assert_parse_result(input: &str, expected: DocExpr) {
let source_file = ast::SourceFile::parse(input).ok().unwrap(); let source_file = ast::SourceFile::parse(input, span::Edition::CURRENT).ok().unwrap();
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0)))); let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0))));
let tt = syntax_node_to_token_tree( let tt = syntax_node_to_token_tree(

View file

@ -510,6 +510,7 @@ pub struct ConstData {
pub type_ref: Interned<TypeRef>, pub type_ref: Interned<TypeRef>,
pub visibility: RawVisibility, pub visibility: RawVisibility,
pub rustc_allow_incoherent_impl: bool, pub rustc_allow_incoherent_impl: bool,
pub has_body: bool,
} }
impl ConstData { impl ConstData {
@ -533,6 +534,7 @@ impl ConstData {
type_ref: konst.type_ref.clone(), type_ref: konst.type_ref.clone(),
visibility, visibility,
rustc_allow_incoherent_impl, rustc_allow_incoherent_impl,
has_body: konst.has_body,
}) })
} }
} }
@ -737,7 +739,7 @@ impl<'a> AssocItemCollector<'a> {
&AstIdWithPath::new(file_id, ast_id, Clone::clone(path)), &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)),
ctxt, ctxt,
expand_to, expand_to,
self.expander.module.krate(), self.expander.krate(),
resolver, resolver,
) { ) {
Ok(Some(call_id)) => { Ok(Some(call_id)) => {

View file

@ -26,7 +26,7 @@ use crate::{
tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}, tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
type_ref::TypeRef, type_ref::TypeRef,
visibility::RawVisibility, visibility::RawVisibility,
EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId,
}; };
/// Note that we use `StructData` for unions as well! /// Note that we use `StructData` for unions as well!
@ -191,8 +191,6 @@ impl StructData {
let krate = loc.container.krate; let krate = loc.container.krate;
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
let mut flags = StructFlags::NO_FLAGS; let mut flags = StructFlags::NO_FLAGS;
@ -219,7 +217,7 @@ impl StructData {
loc.id.file_id(), loc.id.file_id(),
loc.container.local_id, loc.container.local_id,
&item_tree, &item_tree,
&cfg_options, &db.crate_graph()[krate].cfg_options,
&strukt.fields, &strukt.fields,
None, None,
); );
@ -248,8 +246,6 @@ impl StructData {
let krate = loc.container.krate; let krate = loc.container.krate;
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
let mut flags = StructFlags::NO_FLAGS; let mut flags = StructFlags::NO_FLAGS;
if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
@ -266,7 +262,7 @@ impl StructData {
loc.id.file_id(), loc.id.file_id(),
loc.container.local_id, loc.container.local_id,
&item_tree, &item_tree,
&cfg_options, &db.crate_graph()[krate].cfg_options,
&union.fields, &union.fields,
None, None,
); );
@ -338,7 +334,6 @@ impl EnumVariantData {
let container = loc.parent.lookup(db).container; let container = loc.parent.lookup(db).container;
let krate = container.krate; let krate = container.krate;
let item_tree = loc.id.item_tree(db); let item_tree = loc.id.item_tree(db);
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
let variant = &item_tree[loc.id.value]; let variant = &item_tree[loc.id.value];
let (var_data, diagnostics) = lower_fields( let (var_data, diagnostics) = lower_fields(
@ -347,7 +342,7 @@ impl EnumVariantData {
loc.id.file_id(), loc.id.file_id(),
container.local_id, container.local_id,
&item_tree, &item_tree,
&cfg_options, &db.crate_graph()[krate].cfg_options,
&variant.fields, &variant.fields,
Some(item_tree[loc.parent.lookup(db).id.value].visibility), Some(item_tree[loc.parent.lookup(db).id.value].visibility),
); );
@ -383,6 +378,15 @@ impl VariantData {
VariantData::Unit => StructKind::Unit, VariantData::Unit => StructKind::Unit,
} }
} }
#[allow(clippy::self_named_constructors)]
pub(crate) fn variant_data(db: &dyn DefDatabase, id: VariantId) -> Arc<VariantData> {
match id {
VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
VariantId::EnumVariantId(it) => db.enum_variant_data(it).variant_data.clone(),
VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
}
}
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]

View file

@ -12,7 +12,7 @@ use crate::{
attr::{Attrs, AttrsWithOwner}, attr::{Attrs, AttrsWithOwner},
body::{scope::ExprScopes, Body, BodySourceMap}, body::{scope::ExprScopes, Body, BodySourceMap},
data::{ data::{
adt::{EnumData, EnumVariantData, StructData}, adt::{EnumData, EnumVariantData, StructData, VariantData},
ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData, ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData, ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
}, },
@ -127,6 +127,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
id: EnumVariantId, id: EnumVariantId,
) -> (Arc<EnumVariantData>, DefDiagnostics); ) -> (Arc<EnumVariantData>, DefDiagnostics);
#[salsa::transparent]
#[salsa::invoke(VariantData::variant_data)]
fn variant_data(&self, id: VariantId) -> Arc<VariantData>;
#[salsa::transparent] #[salsa::transparent]
#[salsa::invoke(ImplData::impl_data_query)] #[salsa::invoke(ImplData::impl_data_query)]
fn impl_data(&self, e: ImplId) -> Arc<ImplData>; fn impl_data(&self, e: ImplId) -> Arc<ImplData>;

View file

@ -11,6 +11,7 @@ use hir_expand::{
}; };
use limit::Limit; use limit::Limit;
use syntax::{ast, Parse}; use syntax::{ast, Parse};
use triomphe::Arc;
use crate::{ use crate::{
attr::Attrs, db::DefDatabase, lower::LowerCtx, path::Path, AsMacroCall, MacroId, ModuleId, attr::Attrs, db::DefDatabase, lower::LowerCtx, path::Path, AsMacroCall, MacroId, ModuleId,
@ -19,9 +20,8 @@ use crate::{
#[derive(Debug)] #[derive(Debug)]
pub struct Expander { pub struct Expander {
cfg_options: CfgOptions, cfg_options: Arc<CfgOptions>,
span_map: OnceCell<SpanMap>, span_map: OnceCell<SpanMap>,
krate: CrateId,
current_file_id: HirFileId, current_file_id: HirFileId,
pub(crate) module: ModuleId, pub(crate) module: ModuleId,
/// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
@ -45,10 +45,13 @@ impl Expander {
recursion_limit, recursion_limit,
cfg_options: db.crate_graph()[module.krate].cfg_options.clone(), cfg_options: db.crate_graph()[module.krate].cfg_options.clone(),
span_map: OnceCell::new(), span_map: OnceCell::new(),
krate: module.krate,
} }
} }
pub fn krate(&self) -> CrateId {
self.module.krate
}
pub fn enter_expand<T: ast::AstNode>( pub fn enter_expand<T: ast::AstNode>(
&mut self, &mut self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
@ -112,7 +115,7 @@ impl Expander {
pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
Attrs::filter( Attrs::filter(
db, db,
self.krate, self.krate(),
RawAttrs::new( RawAttrs::new(
db.upcast(), db.upcast(),
owner, owner,

View file

@ -30,6 +30,8 @@ pub fn find_path(
find_path_inner(FindPathCtx { db, prefixed: None, prefer_no_std, prefer_prelude }, item, from) find_path_inner(FindPathCtx { db, prefixed: None, prefer_no_std, prefer_prelude }, item, from)
} }
/// Find a path that can be used to refer to a certain item. This can depend on
/// *from where* you're referring to the item, hence the `from` parameter.
pub fn find_path_prefixed( pub fn find_path_prefixed(
db: &dyn DefDatabase, db: &dyn DefDatabase,
item: ItemInNs, item: ItemInNs,
@ -255,7 +257,7 @@ fn find_in_scope(
item: ItemInNs, item: ItemInNs,
) -> Option<Name> { ) -> Option<Name> {
def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| { def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
def_map[local_id].scope.name_of(item).map(|(name, _, _)| name.clone()) def_map[local_id].scope.names_of(item, |name, _, _| Some(name.clone()))
}) })
} }
@ -608,7 +610,8 @@ mod tests {
) { ) {
let (db, pos) = TestDB::with_position(ra_fixture); let (db, pos) = TestDB::with_position(ra_fixture);
let module = db.module_at_position(pos); let module = db.module_at_position(pos);
let parsed_path_file = syntax::SourceFile::parse(&format!("use {path};")); let parsed_path_file =
syntax::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT);
let ast_path = let ast_path =
parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
let mod_path = ModPath::from_src(&db, ast_path, &mut |range| { let mod_path = ModPath::from_src(&db, ast_path, &mut |range| {

View file

@ -3,13 +3,15 @@
//! generic parameters. See also the `Generics` type and the `generics_of` query //! generic parameters. See also the `Generics` type and the `generics_of` query
//! in rustc. //! in rustc.
use std::ops;
use either::Either; use either::Either;
use hir_expand::{ use hir_expand::{
name::{AsName, Name}, name::{AsName, Name},
ExpandResult, ExpandResult,
}; };
use intern::Interned; use intern::Interned;
use la_arena::{Arena, Idx}; use la_arena::Arena;
use once_cell::unsync::Lazy; use once_cell::unsync::Lazy;
use stdx::impl_from; use stdx::impl_from;
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds}; use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
@ -23,12 +25,14 @@ use crate::{
nameres::{DefMap, MacroSubNs}, nameres::{DefMap, MacroSubNs},
type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId, AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId,
LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
}; };
/// Data about a generic type parameter (to a function, struct, impl, ...). /// Data about a generic type parameter (to a function, struct, impl, ...).
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct TypeParamData { pub struct TypeParamData {
/// [`None`] only if the type ref is an [`TypeRef::ImplTrait`]. FIXME: Might be better to just
/// make it always be a value, giving impl trait a special name.
pub name: Option<Name>, pub name: Option<Name>,
pub default: Option<Interned<TypeRef>>, pub default: Option<Interned<TypeRef>>,
pub provenance: TypeParamProvenance, pub provenance: TypeParamProvenance,
@ -156,6 +160,20 @@ pub struct GenericParams {
pub where_predicates: Box<[WherePredicate]>, pub where_predicates: Box<[WherePredicate]>,
} }
impl ops::Index<LocalTypeOrConstParamId> for GenericParams {
type Output = TypeOrConstParamData;
fn index(&self, index: LocalTypeOrConstParamId) -> &TypeOrConstParamData {
&self.type_or_consts[index]
}
}
impl ops::Index<LocalLifetimeParamId> for GenericParams {
type Output = LifetimeParamData;
fn index(&self, index: LocalLifetimeParamId) -> &LifetimeParamData {
&self.lifetimes[index]
}
}
/// A single predicate from a where clause, i.e. `where Type: Trait`. Combined /// A single predicate from a where clause, i.e. `where Type: Trait`. Combined
/// where clauses like `where T: Foo + Bar` are turned into multiple of these. /// where clauses like `where T: Foo + Bar` are turned into multiple of these.
/// It might still result in multiple actual predicates though, because of /// It might still result in multiple actual predicates though, because of
@ -197,7 +215,7 @@ impl GenericParamsCollector {
lower_ctx: &LowerCtx<'_>, lower_ctx: &LowerCtx<'_>,
node: &dyn HasGenericParams, node: &dyn HasGenericParams,
add_param_attrs: impl FnMut( add_param_attrs: impl FnMut(
Either<Idx<TypeOrConstParamData>, Idx<LifetimeParamData>>, Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
ast::GenericParam, ast::GenericParam,
), ),
) { ) {
@ -225,7 +243,7 @@ impl GenericParamsCollector {
lower_ctx: &LowerCtx<'_>, lower_ctx: &LowerCtx<'_>,
params: ast::GenericParamList, params: ast::GenericParamList,
mut add_param_attrs: impl FnMut( mut add_param_attrs: impl FnMut(
Either<Idx<TypeOrConstParamData>, Idx<LifetimeParamData>>, Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
ast::GenericParam, ast::GenericParam,
), ),
) { ) {
@ -414,16 +432,16 @@ impl GenericParams {
} }
/// Iterator of type_or_consts field /// Iterator of type_or_consts field
pub fn iter( pub fn iter_type_or_consts(
&self, &self,
) -> impl DoubleEndedIterator<Item = (Idx<TypeOrConstParamData>, &TypeOrConstParamData)> { ) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> {
self.type_or_consts.iter() self.type_or_consts.iter()
} }
/// Iterator of lifetimes field /// Iterator of lifetimes field
pub fn iter_lt( pub fn iter_lt(
&self, &self,
) -> impl DoubleEndedIterator<Item = (Idx<LifetimeParamData>, &LifetimeParamData)> { ) -> impl DoubleEndedIterator<Item = (LocalLifetimeParamId, &LifetimeParamData)> {
self.lifetimes.iter() self.lifetimes.iter()
} }

View file

@ -277,13 +277,43 @@ impl ItemScope {
ItemInNs::Types(def) => self.types.iter().find_map(|(name, &(other_def, vis, i))| { ItemInNs::Types(def) => self.types.iter().find_map(|(name, &(other_def, vis, i))| {
(other_def == def).then_some((name, vis, i.is_none())) (other_def == def).then_some((name, vis, i.is_none()))
}), }),
ItemInNs::Values(def) => self.values.iter().find_map(|(name, &(other_def, vis, i))| { ItemInNs::Values(def) => self.values.iter().find_map(|(name, &(other_def, vis, i))| {
(other_def == def).then_some((name, vis, i.is_none())) (other_def == def).then_some((name, vis, i.is_none()))
}), }),
} }
} }
/// XXX: this is O(N) rather than O(1), try to not introduce new usages.
pub(crate) fn names_of<T>(
&self,
item: ItemInNs,
mut cb: impl FnMut(&Name, Visibility, bool) -> Option<T>,
) -> Option<T> {
match item {
ItemInNs::Macros(def) => self
.macros
.iter()
.filter_map(|(name, &(other_def, vis, i))| {
(other_def == def).then_some((name, vis, i.is_none()))
})
.find_map(|(a, b, c)| cb(a, b, c)),
ItemInNs::Types(def) => self
.types
.iter()
.filter_map(|(name, &(other_def, vis, i))| {
(other_def == def).then_some((name, vis, i.is_none()))
})
.find_map(|(a, b, c)| cb(a, b, c)),
ItemInNs::Values(def) => self
.values
.iter()
.filter_map(|(name, &(other_def, vis, i))| {
(other_def == def).then_some((name, vis, i.is_none()))
})
.find_map(|(a, b, c)| cb(a, b, c)),
}
}
pub(crate) fn traits(&self) -> impl Iterator<Item = TraitId> + '_ { pub(crate) fn traits(&self) -> impl Iterator<Item = TraitId> + '_ {
self.types self.types
.values() .values()

View file

@ -716,6 +716,7 @@ pub struct Const {
pub visibility: RawVisibilityId, pub visibility: RawVisibilityId,
pub type_ref: Interned<TypeRef>, pub type_ref: Interned<TypeRef>,
pub ast_id: FileAstId<ast::Const>, pub ast_id: FileAstId<ast::Const>,
pub has_body: bool,
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]

View file

@ -446,7 +446,7 @@ impl<'a> Ctx<'a> {
let type_ref = self.lower_type_ref_opt(konst.ty()); let type_ref = self.lower_type_ref_opt(konst.ty());
let visibility = self.lower_visibility(konst); let visibility = self.lower_visibility(konst);
let ast_id = self.source_ast_id_map.ast_id(konst); let ast_id = self.source_ast_id_map.ast_id(konst);
let res = Const { name, visibility, type_ref, ast_id }; let res = Const { name, visibility, type_ref, ast_id, has_body: konst.body().is_some() };
id(self.data().consts.alloc(res)) id(self.data().consts.alloc(res))
} }

View file

@ -357,7 +357,7 @@ impl Printer<'_> {
wln!(self, "}}"); wln!(self, "}}");
} }
ModItem::Const(it) => { ModItem::Const(it) => {
let Const { name, visibility, type_ref, ast_id } = &self.tree[it]; let Const { name, visibility, type_ref, ast_id, has_body: _ } = &self.tree[it];
self.print_ast_id(ast_id.erase()); self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "const "); w!(self, "const ");

View file

@ -422,6 +422,10 @@ impl ModuleId {
} }
} }
pub fn crate_def_map(self, db: &dyn DefDatabase) -> Arc<DefMap> {
db.crate_def_map(self.krate)
}
pub fn krate(self) -> CrateId { pub fn krate(self) -> CrateId {
self.krate self.krate
} }
@ -438,6 +442,8 @@ impl ModuleId {
}) })
} }
/// Returns the module containing `self`, either the parent `mod`, or the module (or block) containing
/// the block, if `self` corresponds to a block expression.
pub fn containing_module(self, db: &dyn DefDatabase) -> Option<ModuleId> { pub fn containing_module(self, db: &dyn DefDatabase) -> Option<ModuleId> {
self.def_map(db).containing_module(self.local_id) self.def_map(db).containing_module(self.local_id)
} }
@ -929,6 +935,18 @@ impl GenericDefId {
GenericDefId::EnumVariantId(_) => (FileId::BOGUS.into(), None), GenericDefId::EnumVariantId(_) => (FileId::BOGUS.into(), None),
} }
} }
pub fn assoc_trait_container(self, db: &dyn DefDatabase) -> Option<TraitId> {
match match self {
GenericDefId::FunctionId(f) => f.lookup(db).container,
GenericDefId::TypeAliasId(t) => t.lookup(db).container,
GenericDefId::ConstId(c) => c.lookup(db).container,
_ => return None,
} {
ItemContainerId::TraitId(trait_) => Some(trait_),
_ => None,
}
}
} }
impl From<AssocItemId> for GenericDefId { impl From<AssocItemId> for GenericDefId {

View file

@ -610,6 +610,10 @@ struct Foo {
field1: i32, field1: i32,
#[cfg(never)] #[cfg(never)]
field2: (), field2: (),
#[cfg(feature = "never")]
field3: (),
#[cfg(not(feature = "never"))]
field4: (),
} }
#[derive(Default)] #[derive(Default)]
enum Bar { enum Bar {
@ -618,12 +622,16 @@ enum Bar {
Bar, Bar,
} }
"#, "#,
expect![[r#" expect![[r##"
#[derive(Default)] #[derive(Default)]
struct Foo { struct Foo {
field1: i32, field1: i32,
#[cfg(never)] #[cfg(never)]
field2: (), field2: (),
#[cfg(feature = "never")]
field3: (),
#[cfg(not(feature = "never"))]
field4: (),
} }
#[derive(Default)] #[derive(Default)]
enum Bar { enum Bar {
@ -635,7 +643,7 @@ enum Bar {
impl < > $crate::default::Default for Foo< > where { impl < > $crate::default::Default for Foo< > where {
fn default() -> Self { fn default() -> Self {
Foo { Foo {
field1: $crate::default::Default::default(), field1: $crate::default::Default::default(), field4: $crate::default::Default::default(),
} }
} }
} }
@ -643,6 +651,6 @@ impl < > $crate::default::Default for Bar< > where {
fn default() -> Self { fn default() -> Self {
Bar::Bar Bar::Bar
} }
}"#]], }"##]],
); );
} }

View file

@ -316,8 +316,11 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
_: Span, _: Span,
_: Span, _: Span,
) -> Result<Subtree, ProcMacroExpansionError> { ) -> Result<Subtree, ProcMacroExpansionError> {
let (parse, _) = let (parse, _) = ::mbe::token_tree_to_syntax_node(
::mbe::token_tree_to_syntax_node(subtree, ::mbe::TopEntryPoint::MacroItems); subtree,
::mbe::TopEntryPoint::MacroItems,
span::Edition::CURRENT,
);
if parse.errors().is_empty() { if parse.errors().is_empty() {
Ok(subtree.clone()) Ok(subtree.clone())
} else { } else {

View file

@ -534,8 +534,7 @@ impl DefCollector<'_> {
Edition::Edition2015 => name![rust_2015], Edition::Edition2015 => name![rust_2015],
Edition::Edition2018 => name![rust_2018], Edition::Edition2018 => name![rust_2018],
Edition::Edition2021 => name![rust_2021], Edition::Edition2021 => name![rust_2021],
// FIXME: update this when rust_2024 exists Edition::Edition2024 => name![rust_2024],
Edition::Edition2024 => name![rust_2021],
}; };
let path_kind = match self.def_map.data.edition { let path_kind = match self.def_map.data.edition {
@ -1918,7 +1917,7 @@ impl ModCollector<'_, '_> {
} }
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) { fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
let path_attr = attrs.by_key("path").string_value(); let path_attr = attrs.by_key("path").string_value_unescape();
let is_macro_use = attrs.by_key("macro_use").exists(); let is_macro_use = attrs.by_key("macro_use").exists();
let module = &self.item_tree[module_id]; let module = &self.item_tree[module_id];
match &module.kind { match &module.kind {
@ -1932,7 +1931,8 @@ impl ModCollector<'_, '_> {
module_id, module_id,
); );
let Some(mod_dir) = self.mod_dir.descend_into_definition(&module.name, path_attr) let Some(mod_dir) =
self.mod_dir.descend_into_definition(&module.name, path_attr.as_deref())
else { else {
return; return;
}; };
@ -1953,8 +1953,12 @@ impl ModCollector<'_, '_> {
ModKind::Outline => { ModKind::Outline => {
let ast_id = AstId::new(self.file_id(), module.ast_id); let ast_id = AstId::new(self.file_id(), module.ast_id);
let db = self.def_collector.db; let db = self.def_collector.db;
match self.mod_dir.resolve_declaration(db, self.file_id(), &module.name, path_attr) match self.mod_dir.resolve_declaration(
{ db,
self.file_id(),
&module.name,
path_attr.as_deref(),
) {
Ok((file_id, is_mod_rs, mod_dir)) => { Ok((file_id, is_mod_rs, mod_dir)) => {
let item_tree = db.file_item_tree(file_id.into()); let item_tree = db.file_item_tree(file_id.into());
let krate = self.def_collector.def_map.krate; let krate = self.def_collector.def_map.krate;

View file

@ -32,9 +32,13 @@ tt.workspace = true
mbe.workspace = true mbe.workspace = true
limit.workspace = true limit.workspace = true
span.workspace = true span.workspace = true
parser.workspace = true
[dev-dependencies] [dev-dependencies]
expect-test = "1.4.0" expect-test = "1.4.0"
[features]
in-rust-tree = ["syntax/in-rust-tree"]
[lints] [lints]
workspace = true workspace = true

View file

@ -1,5 +1,5 @@
//! A higher level attributes based on TokenTree, with also some shortcuts. //! A higher level attributes based on TokenTree, with also some shortcuts.
use std::{fmt, ops}; use std::{borrow::Cow, fmt, ops};
use base_db::CrateId; use base_db::CrateId;
use cfg::CfgExpr; use cfg::CfgExpr;
@ -8,6 +8,7 @@ use intern::Interned;
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use span::{Span, SyntaxContextId}; use span::{Span, SyntaxContextId};
use syntax::unescape;
use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
use triomphe::ThinArc; use triomphe::ThinArc;
@ -54,8 +55,7 @@ impl RawAttrs {
Attr { Attr {
id, id,
input: Some(Interned::new(AttrInput::Literal(tt::Literal { input: Some(Interned::new(AttrInput::Literal(tt::Literal {
// FIXME: Escape quotes from comment content text: SmolStr::new(format_smolstr!("\"{}\"", Self::escape_chars(doc))),
text: SmolStr::new(format_smolstr!("\"{doc}\"",)),
span, span,
}))), }))),
path: Interned::new(ModPath::from(crate::name!(doc))), path: Interned::new(ModPath::from(crate::name!(doc))),
@ -74,6 +74,10 @@ impl RawAttrs {
RawAttrs { entries } RawAttrs { entries }
} }
fn escape_chars(s: &str) -> String {
s.replace('\\', r#"\\"#).replace('"', r#"\""#)
}
pub fn from_attrs_owner( pub fn from_attrs_owner(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
owner: InFile<&dyn ast::HasAttrs>, owner: InFile<&dyn ast::HasAttrs>,
@ -297,6 +301,18 @@ impl Attr {
} }
} }
pub fn string_value_unescape(&self) -> Option<Cow<'_, str>> {
match self.input.as_deref()? {
AttrInput::Literal(it) => match it.text.strip_prefix('r') {
Some(it) => {
it.trim_matches('#').strip_prefix('"')?.strip_suffix('"').map(Cow::Borrowed)
}
None => it.text.strip_prefix('"')?.strip_suffix('"').and_then(unescape),
},
_ => None,
}
}
/// #[path(ident)] /// #[path(ident)]
pub fn single_ident_value(&self) -> Option<&tt::Ident> { pub fn single_ident_value(&self) -> Option<&tt::Ident> {
match self.input.as_deref()? { match self.input.as_deref()? {
@ -346,6 +362,33 @@ impl Attr {
} }
} }
fn unescape(s: &str) -> Option<Cow<'_, str>> {
let mut buf = String::new();
let mut prev_end = 0;
let mut has_error = false;
unescape::unescape_unicode(s, unescape::Mode::Str, &mut |char_range, unescaped_char| match (
unescaped_char,
buf.capacity() == 0,
) {
(Ok(c), false) => buf.push(c),
(Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
prev_end = char_range.end
}
(Ok(c), true) => {
buf.reserve_exact(s.len());
buf.push_str(&s[..prev_end]);
buf.push(c);
}
(Err(_), _) => has_error = true,
});
match (has_error, buf.capacity() == 0) {
(true, _) => None,
(false, false) => Some(Cow::Owned(buf)),
(false, true) => Some(Cow::Borrowed(s)),
}
}
pub fn collect_attrs( pub fn collect_attrs(
owner: &dyn ast::HasAttrs, owner: &dyn ast::HasAttrs,
) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> { ) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {

View file

@ -204,7 +204,11 @@ struct BasicAdtInfo {
} }
fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandError> { fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandError> {
let (parsed, tm) = &mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems); let (parsed, tm) = &mbe::token_tree_to_syntax_node(
tt,
mbe::TopEntryPoint::MacroItems,
parser::Edition::CURRENT,
);
let macro_items = ast::MacroItems::cast(parsed.syntax_node()) let macro_items = ast::MacroItems::cast(parsed.syntax_node())
.ok_or_else(|| ExpandError::other("invalid item definition"))?; .ok_or_else(|| ExpandError::other("invalid item definition"))?;
let item = macro_items.items().next().ok_or_else(|| ExpandError::other("no item found"))?; let item = macro_items.items().next().ok_or_else(|| ExpandError::other("no item found"))?;

View file

@ -219,7 +219,7 @@ fn assert_expand(
span: Span, span: Span,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let call_site_span = span_with_call_site_ctxt(db, span, id); let call_site_span = span_with_call_site_ctxt(db, span, id);
let args = parse_exprs_with_sep(tt, ',', call_site_span); let args = parse_exprs_with_sep(tt, ',', call_site_span, Edition::CURRENT);
let dollar_crate = dollar_crate(span); let dollar_crate = dollar_crate(span);
let expanded = match &*args { let expanded = match &*args {
[cond, panic_args @ ..] => { [cond, panic_args @ ..] => {

View file

@ -1,57 +1,59 @@
//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro //! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro
use std::iter::Peekable; use std::iter::Peekable;
use base_db::CrateId;
use cfg::{CfgAtom, CfgExpr}; use cfg::{CfgAtom, CfgExpr};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use syntax::{ use syntax::{
ast::{self, Attr, HasAttrs, Meta, VariantList}, ast::{self, Attr, HasAttrs, Meta, VariantList},
AstNode, NodeOrToken, SyntaxElement, SyntaxNode, T, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T,
}; };
use tracing::{debug, warn}; use tracing::{debug, warn};
use tt::SmolStr; use tt::SmolStr;
use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind}; use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind};
fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option<bool> { fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<bool> {
if !attr.simple_name().as_deref().map(|v| v == "cfg")? { if !attr.simple_name().as_deref().map(|v| v == "cfg")? {
return None; return None;
} }
debug!("Evaluating cfg {}", attr);
let cfg = parse_from_attr_meta(attr.meta()?)?; let cfg = parse_from_attr_meta(attr.meta()?)?;
debug!("Checking cfg {:?}", cfg); let enabled = db.crate_graph()[krate].cfg_options.check(&cfg) != Some(false);
let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg) != Some(false);
Some(enabled) Some(enabled)
} }
fn check_cfg_attr_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option<bool> { fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option<bool> {
if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? { if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? {
return None; return None;
} }
debug!("Evaluating cfg_attr {}", attr);
let cfg_expr = parse_from_attr_meta(attr.meta()?)?; let cfg_expr = parse_from_attr_meta(attr.meta()?)?;
debug!("Checking cfg_attr {:?}", cfg_expr); let enabled = db.crate_graph()[krate].cfg_options.check(&cfg_expr) != Some(false);
let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg_expr) != Some(false);
Some(enabled) Some(enabled)
} }
fn process_has_attrs_with_possible_comma<I: HasAttrs>( fn process_has_attrs_with_possible_comma<I: HasAttrs>(
items: impl Iterator<Item = I>,
loc: &MacroCallLoc,
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
items: impl Iterator<Item = I>,
krate: CrateId,
remove: &mut FxHashSet<SyntaxElement>, remove: &mut FxHashSet<SyntaxElement>,
) -> Option<()> { ) -> Option<()> {
for item in items { for item in items {
let field_attrs = item.attrs(); let field_attrs = item.attrs();
'attrs: for attr in field_attrs { 'attrs: for attr in field_attrs {
if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { if let Some(enabled) = check_cfg(db, &attr, krate) {
debug!("censoring type {:?}", item.syntax()); if enabled {
remove.insert(item.syntax().clone().into()); debug!("censoring {:?}", attr.syntax());
// We need to remove the , as well remove.insert(attr.syntax().clone().into());
remove_possible_comma(&item, remove); } else {
break 'attrs; debug!("censoring {:?}", item.syntax());
remove.insert(item.syntax().clone().into());
// We need to remove the , as well
remove_possible_comma(&item, remove);
break 'attrs;
}
} }
if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { if let Some(enabled) = check_cfg_attr(db, &attr, krate) {
if enabled { if enabled {
debug!("Removing cfg_attr tokens {:?}", attr); debug!("Removing cfg_attr tokens {:?}", attr);
let meta = attr.meta()?; let meta = attr.meta()?;
@ -60,13 +62,13 @@ fn process_has_attrs_with_possible_comma<I: HasAttrs>(
} else { } else {
debug!("censoring type cfg_attr {:?}", item.syntax()); debug!("censoring type cfg_attr {:?}", item.syntax());
remove.insert(attr.syntax().clone().into()); remove.insert(attr.syntax().clone().into());
continue;
} }
} }
} }
} }
Some(()) Some(())
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum CfgExprStage { enum CfgExprStage {
/// Stripping the CFGExpr part of the attribute /// Stripping the CFGExpr part of the attribute
@ -78,6 +80,7 @@ enum CfgExprStage {
// Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110 // Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110
EverythingElse, EverythingElse,
} }
/// This function creates its own set of tokens to remove. To help prevent malformed syntax as input. /// This function creates its own set of tokens to remove. To help prevent malformed syntax as input.
fn remove_tokens_within_cfg_attr(meta: Meta) -> Option<FxHashSet<SyntaxElement>> { fn remove_tokens_within_cfg_attr(meta: Meta) -> Option<FxHashSet<SyntaxElement>> {
let mut remove: FxHashSet<SyntaxElement> = FxHashSet::default(); let mut remove: FxHashSet<SyntaxElement> = FxHashSet::default();
@ -131,23 +134,28 @@ fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet<SyntaxElement>
} }
} }
fn process_enum( fn process_enum(
variants: VariantList,
loc: &MacroCallLoc,
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
variants: VariantList,
krate: CrateId,
remove: &mut FxHashSet<SyntaxElement>, remove: &mut FxHashSet<SyntaxElement>,
) -> Option<()> { ) -> Option<()> {
'variant: for variant in variants.variants() { 'variant: for variant in variants.variants() {
for attr in variant.attrs() { for attr in variant.attrs() {
if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { if let Some(enabled) = check_cfg(db, &attr, krate) {
// Rustc does not strip the attribute if it is enabled. So we will leave it if enabled {
debug!("censoring type {:?}", variant.syntax()); debug!("censoring {:?}", attr.syntax());
remove.insert(variant.syntax().clone().into()); remove.insert(attr.syntax().clone().into());
// We need to remove the , as well } else {
remove_possible_comma(&variant, remove); // Rustc does not strip the attribute if it is enabled. So we will leave it
continue 'variant; debug!("censoring type {:?}", variant.syntax());
}; remove.insert(variant.syntax().clone().into());
// We need to remove the , as well
remove_possible_comma(&variant, remove);
continue 'variant;
}
}
if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { if let Some(enabled) = check_cfg_attr(db, &attr, krate) {
if enabled { if enabled {
debug!("Removing cfg_attr tokens {:?}", attr); debug!("Removing cfg_attr tokens {:?}", attr);
let meta = attr.meta()?; let meta = attr.meta()?;
@ -156,17 +164,16 @@ fn process_enum(
} else { } else {
debug!("censoring type cfg_attr {:?}", variant.syntax()); debug!("censoring type cfg_attr {:?}", variant.syntax());
remove.insert(attr.syntax().clone().into()); remove.insert(attr.syntax().clone().into());
continue;
} }
} }
} }
if let Some(fields) = variant.field_list() { if let Some(fields) = variant.field_list() {
match fields { match fields {
ast::FieldList::RecordFieldList(fields) => { ast::FieldList::RecordFieldList(fields) => {
process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?; process_has_attrs_with_possible_comma(db, fields.fields(), krate, remove)?;
} }
ast::FieldList::TupleFieldList(fields) => { ast::FieldList::TupleFieldList(fields) => {
process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?; process_has_attrs_with_possible_comma(db, fields.fields(), krate, remove)?;
} }
} }
} }
@ -175,9 +182,9 @@ fn process_enum(
} }
pub(crate) fn process_cfg_attrs( pub(crate) fn process_cfg_attrs(
db: &dyn ExpandDatabase,
node: &SyntaxNode, node: &SyntaxNode,
loc: &MacroCallLoc, loc: &MacroCallLoc,
db: &dyn ExpandDatabase,
) -> Option<FxHashSet<SyntaxElement>> { ) -> Option<FxHashSet<SyntaxElement>> {
// FIXME: #[cfg_eval] is not implemented. But it is not stable yet // FIXME: #[cfg_eval] is not implemented. But it is not stable yet
let is_derive = match loc.def.kind { let is_derive = match loc.def.kind {
@ -193,36 +200,35 @@ pub(crate) fn process_cfg_attrs(
let item = ast::Item::cast(node.clone())?; let item = ast::Item::cast(node.clone())?;
for attr in item.attrs() { for attr in item.attrs() {
if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { if let Some(enabled) = check_cfg_attr(db, &attr, loc.krate) {
if enabled { if enabled {
debug!("Removing cfg_attr tokens {:?}", attr); debug!("Removing cfg_attr tokens {:?}", attr);
let meta = attr.meta()?; let meta = attr.meta()?;
let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?;
remove.extend(removes_from_cfg_attr); remove.extend(removes_from_cfg_attr);
} else { } else {
debug!("censoring type cfg_attr {:?}", item.syntax()); debug!("Removing type cfg_attr {:?}", item.syntax());
remove.insert(attr.syntax().clone().into()); remove.insert(attr.syntax().clone().into());
continue;
} }
} }
} }
match item { match item {
ast::Item::Struct(it) => match it.field_list()? { ast::Item::Struct(it) => match it.field_list()? {
ast::FieldList::RecordFieldList(fields) => { ast::FieldList::RecordFieldList(fields) => {
process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?; process_has_attrs_with_possible_comma(db, fields.fields(), loc.krate, &mut remove)?;
} }
ast::FieldList::TupleFieldList(fields) => { ast::FieldList::TupleFieldList(fields) => {
process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?; process_has_attrs_with_possible_comma(db, fields.fields(), loc.krate, &mut remove)?;
} }
}, },
ast::Item::Enum(it) => { ast::Item::Enum(it) => {
process_enum(it.variant_list()?, loc, db, &mut remove)?; process_enum(db, it.variant_list()?, loc.krate, &mut remove)?;
} }
ast::Item::Union(it) => { ast::Item::Union(it) => {
process_has_attrs_with_possible_comma( process_has_attrs_with_possible_comma(
it.record_field_list()?.fields(),
loc,
db, db,
it.record_field_list()?.fields(),
loc.krate,
&mut remove, &mut remove,
)?; )?;
} }
@ -234,10 +240,22 @@ pub(crate) fn process_cfg_attrs(
/// Parses a `cfg` attribute from the meta /// Parses a `cfg` attribute from the meta
fn parse_from_attr_meta(meta: Meta) -> Option<CfgExpr> { fn parse_from_attr_meta(meta: Meta) -> Option<CfgExpr> {
let tt = meta.token_tree()?; let tt = meta.token_tree()?;
let mut iter = tt.token_trees_and_tokens().skip(1).peekable(); let mut iter = tt
.token_trees_and_tokens()
.filter(is_not_whitespace)
.skip(1)
.take_while(is_not_closing_paren)
.peekable();
next_cfg_expr_from_syntax(&mut iter) next_cfg_expr_from_syntax(&mut iter)
} }
fn is_not_closing_paren(element: &NodeOrToken<ast::TokenTree, syntax::SyntaxToken>) -> bool {
!matches!(element, NodeOrToken::Token(token) if (token.kind() == syntax::T![')']))
}
fn is_not_whitespace(element: &NodeOrToken<ast::TokenTree, syntax::SyntaxToken>) -> bool {
!matches!(element, NodeOrToken::Token(token) if (token.kind() == SyntaxKind::WHITESPACE))
}
fn next_cfg_expr_from_syntax<I>(iter: &mut Peekable<I>) -> Option<CfgExpr> fn next_cfg_expr_from_syntax<I>(iter: &mut Peekable<I>) -> Option<CfgExpr>
where where
I: Iterator<Item = NodeOrToken<ast::TokenTree, syntax::SyntaxToken>>, I: Iterator<Item = NodeOrToken<ast::TokenTree, syntax::SyntaxToken>>,
@ -256,14 +274,13 @@ where
let Some(NodeOrToken::Node(tree)) = iter.next() else { let Some(NodeOrToken::Node(tree)) = iter.next() else {
return Some(CfgExpr::Invalid); return Some(CfgExpr::Invalid);
}; };
let mut tree_iter = tree.token_trees_and_tokens().skip(1).peekable(); let mut tree_iter = tree
while tree_iter .token_trees_and_tokens()
.peek() .filter(is_not_whitespace)
.filter( .skip(1)
|element| matches!(element, NodeOrToken::Token(token) if (token.kind() != syntax::T![')'])), .take_while(is_not_closing_paren)
) .peekable();
.is_some() while tree_iter.peek().is_some() {
{
let pred = next_cfg_expr_from_syntax(&mut tree_iter); let pred = next_cfg_expr_from_syntax(&mut tree_iter);
if let Some(pred) = pred { if let Some(pred) = pred {
preds.push(pred); preds.push(pred);
@ -310,7 +327,7 @@ mod tests {
use crate::cfg_process::parse_from_attr_meta; use crate::cfg_process::parse_from_attr_meta;
fn check_dnf_from_syntax(input: &str, expect: Expect) { fn check_dnf_from_syntax(input: &str, expect: Expect) {
let parse = SourceFile::parse(input); let parse = SourceFile::parse(input, span::Edition::CURRENT);
let node = match parse.tree().syntax().descendants().find_map(Attr::cast) { let node = match parse.tree().syntax().descendants().find_map(Attr::cast) {
Some(it) => it, Some(it) => it,
None => { None => {

View file

@ -3,7 +3,7 @@
use base_db::{salsa, CrateId, FileId, SourceDatabase}; use base_db::{salsa, CrateId, FileId, SourceDatabase};
use either::Either; use either::Either;
use limit::Limit; use limit::Limit;
use mbe::syntax_node_to_token_tree; use mbe::{syntax_node_to_token_tree, MatchedArmIndex};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId}; use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId};
use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T}; use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
@ -175,7 +175,7 @@ pub fn expand_speculative(
}; };
let censor_cfg = let censor_cfg =
cfg_process::process_cfg_attrs(speculative_args, &loc, db).unwrap_or_default(); cfg_process::process_cfg_attrs(db, speculative_args, &loc).unwrap_or_default();
let mut fixups = fixup::fixup_syntax(span_map, speculative_args, span); let mut fixups = fixup::fixup_syntax(span_map, speculative_args, span);
fixups.append.retain(|it, _| match it { fixups.append.retain(|it, _| match it {
syntax::NodeOrToken::Token(_) => true, syntax::NodeOrToken::Token(_) => true,
@ -225,43 +225,45 @@ pub fn expand_speculative(
// Do the actual expansion, we need to directly expand the proc macro due to the attribute args // Do the actual expansion, we need to directly expand the proc macro due to the attribute args
// Otherwise the expand query will fetch the non speculative attribute args and pass those instead. // Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
let mut speculative_expansion = match loc.def.kind { let mut speculative_expansion =
MacroDefKind::ProcMacro(expander, _, ast) => { match loc.def.kind {
let span = db.proc_macro_span(ast); MacroDefKind::ProcMacro(expander, _, ast) => {
tt.delimiter = tt::Delimiter::invisible_spanned(span); let span = db.proc_macro_span(ast);
expander.expand( tt.delimiter = tt::Delimiter::invisible_spanned(span);
db, expander.expand(
loc.def.krate, db,
loc.krate, loc.def.krate,
&tt, loc.krate,
attr_arg.as_ref(), &tt,
span_with_def_site_ctxt(db, span, actual_macro_call), attr_arg.as_ref(),
span_with_call_site_ctxt(db, span, actual_macro_call), span_with_def_site_ctxt(db, span, actual_macro_call),
span_with_mixed_site_ctxt(db, span, actual_macro_call), span_with_call_site_ctxt(db, span, actual_macro_call),
) span_with_mixed_site_ctxt(db, span, actual_macro_call),
} )
MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => { }
pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span) MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
} pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
MacroDefKind::Declarative(it) => { }
db.decl_macro_expander(loc.krate, it).expand_unhygienic(db, tt, loc.def.krate, span) MacroDefKind::Declarative(it) => db
} .decl_macro_expander(loc.krate, it)
MacroDefKind::BuiltIn(it, _) => { .expand_unhygienic(db, tt, loc.def.krate, span, loc.def.edition),
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) MacroDefKind::BuiltIn(it, _) => {
} it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
MacroDefKind::BuiltInDerive(it, ..) => { }
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) MacroDefKind::BuiltInDerive(it, ..) => {
} it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
MacroDefKind::BuiltInEager(it, _) => { }
it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) MacroDefKind::BuiltInEager(it, _) => {
} it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span), }
}; MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span),
};
let expand_to = loc.expand_to(); let expand_to = loc.expand_to();
fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info); fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info);
let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to); let (node, rev_tmap) =
token_tree_to_syntax_node(&speculative_expansion.value, expand_to, loc.def.edition);
let syntax_node = node.syntax_node(); let syntax_node = node.syntax_node();
let token = rev_tmap let token = rev_tmap
@ -309,16 +311,20 @@ fn parse_macro_expansion(
) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> { ) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> {
let _p = tracing::span!(tracing::Level::INFO, "parse_macro_expansion").entered(); let _p = tracing::span!(tracing::Level::INFO, "parse_macro_expansion").entered();
let loc = db.lookup_intern_macro_call(macro_file.macro_call_id); let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let edition = loc.def.edition;
let expand_to = loc.expand_to(); let expand_to = loc.expand_to();
let mbe::ValueResult { value: tt, err } = macro_expand(db, macro_file.macro_call_id, loc); let mbe::ValueResult { value: (tt, matched_arm), err } =
macro_expand(db, macro_file.macro_call_id, loc);
let (parse, rev_token_map) = token_tree_to_syntax_node( let (parse, mut rev_token_map) = token_tree_to_syntax_node(
match &tt { match &tt {
CowArc::Arc(it) => it, CowArc::Arc(it) => it,
CowArc::Owned(it) => it, CowArc::Owned(it) => it,
}, },
expand_to, expand_to,
edition,
); );
rev_token_map.matched_arm = matched_arm;
ExpandResult { value: (parse, Arc::new(rev_token_map)), err } ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
} }
@ -462,7 +468,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
let (mut tt, undo_info) = { let (mut tt, undo_info) = {
let syntax = item_node.syntax(); let syntax = item_node.syntax();
let censor_cfg = cfg_process::process_cfg_attrs(syntax, &loc, db).unwrap_or_default(); let censor_cfg = cfg_process::process_cfg_attrs(db, syntax, &loc).unwrap_or_default();
let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, span); let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, span);
fixups.append.retain(|it, _| match it { fixups.append.retain(|it, _| match it {
syntax::NodeOrToken::Token(_) => true, syntax::NodeOrToken::Token(_) => true,
@ -540,11 +546,13 @@ fn macro_expand(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
macro_call_id: MacroCallId, macro_call_id: MacroCallId,
loc: MacroCallLoc, loc: MacroCallLoc,
) -> ExpandResult<CowArc<tt::Subtree>> { ) -> ExpandResult<(CowArc<tt::Subtree>, MatchedArmIndex)> {
let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered(); let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered();
let (ExpandResult { value: tt, err }, span) = match loc.def.kind { let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind {
MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc), MacroDefKind::ProcMacro(..) => {
return db.expand_proc_macro(macro_call_id).map(CowArc::Arc).zip_val(None)
}
_ => { _ => {
let (macro_arg, undo_info, span) = let (macro_arg, undo_info, span) =
db.macro_arg_considering_derives(macro_call_id, &loc.kind); db.macro_arg_considering_derives(macro_call_id, &loc.kind);
@ -556,10 +564,10 @@ fn macro_expand(
.decl_macro_expander(loc.def.krate, id) .decl_macro_expander(loc.def.krate, id)
.expand(db, arg.clone(), macro_call_id, span), .expand(db, arg.clone(), macro_call_id, span),
MacroDefKind::BuiltIn(it, _) => { MacroDefKind::BuiltIn(it, _) => {
it.expand(db, macro_call_id, arg, span).map_err(Into::into) it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None)
} }
MacroDefKind::BuiltInDerive(it, _) => { MacroDefKind::BuiltInDerive(it, _) => {
it.expand(db, macro_call_id, arg, span).map_err(Into::into) it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None)
} }
MacroDefKind::BuiltInEager(it, _) => { MacroDefKind::BuiltInEager(it, _) => {
// This might look a bit odd, but we do not expand the inputs to eager macros here. // This might look a bit odd, but we do not expand the inputs to eager macros here.
@ -570,7 +578,8 @@ fn macro_expand(
// As such we just return the input subtree here. // As such we just return the input subtree here.
let eager = match &loc.kind { let eager = match &loc.kind {
MacroCallKind::FnLike { eager: None, .. } => { MacroCallKind::FnLike { eager: None, .. } => {
return ExpandResult::ok(CowArc::Arc(macro_arg.clone())); return ExpandResult::ok(CowArc::Arc(macro_arg.clone()))
.zip_val(None);
} }
MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager), MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
_ => None, _ => None,
@ -582,12 +591,12 @@ fn macro_expand(
// FIXME: We should report both errors! // FIXME: We should report both errors!
res.err = error.clone().or(res.err); res.err = error.clone().or(res.err);
} }
res res.zip_val(None)
} }
MacroDefKind::BuiltInAttr(it, _) => { MacroDefKind::BuiltInAttr(it, _) => {
let mut res = it.expand(db, macro_call_id, arg, span); let mut res = it.expand(db, macro_call_id, arg, span);
fixup::reverse_fixups(&mut res.value, &undo_info); fixup::reverse_fixups(&mut res.value, &undo_info);
res res.zip_val(None)
} }
_ => unreachable!(), _ => unreachable!(),
}; };
@ -599,16 +608,18 @@ fn macro_expand(
if !loc.def.is_include() { if !loc.def.is_include() {
// Set a hard limit for the expanded tt // Set a hard limit for the expanded tt
if let Err(value) = check_tt_count(&tt) { if let Err(value) = check_tt_count(&tt) {
return value.map(|()| { return value
CowArc::Owned(tt::Subtree { .map(|()| {
delimiter: tt::Delimiter::invisible_spanned(span), CowArc::Owned(tt::Subtree {
token_trees: Box::new([]), delimiter: tt::Delimiter::invisible_spanned(span),
token_trees: Box::new([]),
})
}) })
}); .zip_val(matched_arm);
} }
} }
ExpandResult { value: CowArc::Owned(tt), err } ExpandResult { value: (CowArc::Owned(tt), matched_arm), err }
} }
fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span { fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {
@ -668,6 +679,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A
fn token_tree_to_syntax_node( fn token_tree_to_syntax_node(
tt: &tt::Subtree, tt: &tt::Subtree,
expand_to: ExpandTo, expand_to: ExpandTo,
edition: parser::Edition,
) -> (Parse<SyntaxNode>, ExpansionSpanMap) { ) -> (Parse<SyntaxNode>, ExpansionSpanMap) {
let entry_point = match expand_to { let entry_point = match expand_to {
ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts, ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts,
@ -676,7 +688,7 @@ fn token_tree_to_syntax_node(
ExpandTo::Type => mbe::TopEntryPoint::Type, ExpandTo::Type => mbe::TopEntryPoint::Type,
ExpandTo::Expr => mbe::TopEntryPoint::Expr, ExpandTo::Expr => mbe::TopEntryPoint::Expr,
}; };
mbe::token_tree_to_syntax_node(tt, entry_point) mbe::token_tree_to_syntax_node(tt, entry_point, edition)
} }
fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> { fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> {

View file

@ -2,7 +2,8 @@
use std::sync::OnceLock; use std::sync::OnceLock;
use base_db::{CrateId, VersionReq}; use base_db::{CrateId, VersionReq};
use span::{MacroCallId, Span, SyntaxContextId}; use span::{Edition, MacroCallId, Span, SyntaxContextId};
use stdx::TupleExt;
use syntax::{ast, AstNode}; use syntax::{ast, AstNode};
use triomphe::Arc; use triomphe::Arc;
@ -30,7 +31,7 @@ impl DeclarativeMacroExpander {
tt: tt::Subtree, tt: tt::Subtree,
call_id: MacroCallId, call_id: MacroCallId,
span: Span, span: Span,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<(tt::Subtree, Option<u32>)> {
let loc = db.lookup_intern_macro_call(call_id); let loc = db.lookup_intern_macro_call(call_id);
let toolchain = db.toolchain(loc.def.krate); let toolchain = db.toolchain(loc.def.krate);
let new_meta_vars = toolchain.as_ref().map_or(false, |version| { let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
@ -46,7 +47,7 @@ impl DeclarativeMacroExpander {
}); });
match self.mac.err() { match self.mac.err() {
Some(_) => ExpandResult::new( Some(_) => ExpandResult::new(
tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), (tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), None),
ExpandError::MacroDefinition, ExpandError::MacroDefinition,
), ),
None => self None => self
@ -56,6 +57,7 @@ impl DeclarativeMacroExpander {
|s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency), |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency),
new_meta_vars, new_meta_vars,
span, span,
loc.def.edition,
) )
.map_err(Into::into), .map_err(Into::into),
} }
@ -67,6 +69,7 @@ impl DeclarativeMacroExpander {
tt: tt::Subtree, tt: tt::Subtree,
krate: CrateId, krate: CrateId,
call_site: Span, call_site: Span,
def_site_edition: Edition,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let toolchain = db.toolchain(krate); let toolchain = db.toolchain(krate);
let new_meta_vars = toolchain.as_ref().map_or(false, |version| { let new_meta_vars = toolchain.as_ref().map_or(false, |version| {
@ -85,7 +88,11 @@ impl DeclarativeMacroExpander {
tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }),
ExpandError::MacroDefinition, ExpandError::MacroDefinition,
), ),
None => self.mac.expand(&tt, |_| (), new_meta_vars, call_site).map_err(Into::into), None => self
.mac
.expand(&tt, |_| (), new_meta_vars, call_site, def_site_edition)
.map(TupleExt::head)
.map_err(Into::into),
} }
} }

View file

@ -396,7 +396,7 @@ mod tests {
#[track_caller] #[track_caller]
fn check(ra_fixture: &str, mut expect: Expect) { fn check(ra_fixture: &str, mut expect: Expect) {
let parsed = syntax::SourceFile::parse(ra_fixture); let parsed = syntax::SourceFile::parse(ra_fixture, span::Edition::CURRENT);
let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0)))); let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0))));
let fixups = super::fixup_syntax( let fixups = super::fixup_syntax(
span_map.as_ref(), span_map.as_ref(),
@ -417,7 +417,11 @@ mod tests {
expect.assert_eq(&actual); expect.assert_eq(&actual);
// the fixed-up tree should be syntactically valid // the fixed-up tree should be syntactically valid
let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems); let (parse, _) = mbe::token_tree_to_syntax_node(
&tt,
::mbe::TopEntryPoint::MacroItems,
parser::Edition::CURRENT,
);
assert!( assert!(
parse.errors().is_empty(), parse.errors().is_empty(),
"parse has syntax errors. parse tree:\n{:#?}", "parse has syntax errors. parse tree:\n{:#?}",

View file

@ -3,7 +3,7 @@
//! Specifically, it implements a concept of `MacroFile` -- a file whose syntax //! Specifically, it implements a concept of `MacroFile` -- a file whose syntax
//! tree originates not from the text of some `FileId`, but from some macro //! tree originates not from the text of some `FileId`, but from some macro
//! expansion. //! expansion.
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
#![warn(rust_2018_idioms, unused_lifetimes)] #![warn(rust_2018_idioms, unused_lifetimes)]
pub mod attrs; pub mod attrs;

View file

@ -303,6 +303,7 @@ pub mod known {
rust_2015, rust_2015,
rust_2018, rust_2018,
rust_2021, rust_2021,
rust_2024,
v1, v1,
new_display, new_display,
new_debug, new_debug,

View file

@ -61,7 +61,7 @@ test-utils.workspace = true
test-fixture.workspace = true test-fixture.workspace = true
[features] [features]
in-rust-tree = [] in-rust-tree = ["hir-expand/in-rust-tree"]
[lints] [lints]
workspace = true workspace = true

View file

@ -74,6 +74,10 @@ impl<D> TyBuilder<D> {
(self.data, subst) (self.data, subst)
} }
pub fn build_into_subst(self) -> Substitution {
self.build_internal().1
}
pub fn push(mut self, arg: impl CastTo<GenericArg>) -> Self { pub fn push(mut self, arg: impl CastTo<GenericArg>) -> Self {
assert!(self.remaining() > 0); assert!(self.remaining() > 0);
let arg = arg.cast(Interner); let arg = arg.cast(Interner);
@ -291,7 +295,6 @@ impl TyBuilder<hir_def::AdtId> {
) -> Self { ) -> Self {
// Note that we're building ADT, so we never have parent generic parameters. // Note that we're building ADT, so we never have parent generic parameters.
let defaults = db.generic_defaults(self.data.into()); let defaults = db.generic_defaults(self.data.into());
let dummy_ty = TyKind::Error.intern(Interner).cast(Interner);
for default_ty in defaults.iter().skip(self.vec.len()) { for default_ty in defaults.iter().skip(self.vec.len()) {
// NOTE(skip_binders): we only check if the arg type is error type. // NOTE(skip_binders): we only check if the arg type is error type.
if let Some(x) = default_ty.skip_binders().ty(Interner) { if let Some(x) = default_ty.skip_binders().ty(Interner) {
@ -301,13 +304,16 @@ impl TyBuilder<hir_def::AdtId> {
} }
} }
// Each default can only depend on the previous parameters. // Each default can only depend on the previous parameters.
// FIXME: we don't handle const generics here.
let subst_so_far = Substitution::from_iter( let subst_so_far = Substitution::from_iter(
Interner, Interner,
self.vec self.vec
.iter() .iter()
.cloned() .cloned()
.chain(iter::repeat(dummy_ty.clone())) .chain(self.param_kinds[self.vec.len()..].iter().map(|it| match it {
ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
ParamKind::Lifetime => error_lifetime().cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}))
.take(self.param_kinds.len()), .take(self.param_kinds.len()),
); );
self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner)); self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));

View file

@ -1,6 +1,8 @@
//! Various extensions traits for Chalk types. //! Various extensions traits for Chalk types.
use chalk_ir::{cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy}; use chalk_ir::{
cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, TypeOutlives, UintTy,
};
use hir_def::{ use hir_def::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint}, builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
generics::TypeOrConstParamData, generics::TypeOrConstParamData,
@ -312,7 +314,7 @@ impl TyExt for Ty {
.generic_predicates(id.parent) .generic_predicates(id.parent)
.iter() .iter()
.map(|pred| pred.clone().substitute(Interner, &substs)) .map(|pred| pred.clone().substitute(Interner, &substs))
.filter(|wc| match &wc.skip_binders() { .filter(|wc| match wc.skip_binders() {
WhereClause::Implemented(tr) => { WhereClause::Implemented(tr) => {
&tr.self_type_parameter(Interner) == self &tr.self_type_parameter(Interner) == self
} }
@ -320,6 +322,9 @@ impl TyExt for Ty {
alias: AliasTy::Projection(proj), alias: AliasTy::Projection(proj),
ty: _, ty: _,
}) => &proj.self_type_parameter(db) == self, }) => &proj.self_type_parameter(db) == self,
WhereClause::TypeOutlives(TypeOutlives { ty, lifetime: _ }) => {
ty == self
}
_ => false, _ => false,
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View file

@ -77,30 +77,32 @@ pub(crate) fn path_to_const(
resolver: &Resolver, resolver: &Resolver,
path: &Path, path: &Path,
mode: ParamLoweringMode, mode: ParamLoweringMode,
args_lazy: impl FnOnce() -> Generics, args: impl FnOnce() -> Option<Generics>,
debruijn: DebruijnIndex, debruijn: DebruijnIndex,
expected_ty: Ty, expected_ty: Ty,
) -> Option<Const> { ) -> Option<Const> {
match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) { match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
Some(ValueNs::GenericParam(p)) => { Some(ValueNs::GenericParam(p)) => {
let ty = db.const_param_ty(p); let ty = db.const_param_ty(p);
let args = args_lazy();
let value = match mode { let value = match mode {
ParamLoweringMode::Placeholder => { ParamLoweringMode::Placeholder => {
ConstValue::Placeholder(to_placeholder_idx(db, p.into())) ConstValue::Placeholder(to_placeholder_idx(db, p.into()))
} }
ParamLoweringMode::Variable => match args.param_idx(p.into()) { ParamLoweringMode::Variable => {
Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), let args = args();
None => { match args.as_ref().and_then(|args| args.type_or_const_param_idx(p.into())) {
never!( Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)),
"Generic list doesn't contain this param: {:?}, {:?}, {:?}", None => {
args, never!(
path, "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
p args,
); path,
return None; p
);
return None;
}
} }
}, }
}; };
Some(ConstData { ty, value }.intern(Interner)) Some(ConstData { ty, value }.intern(Interner))
} }
@ -285,7 +287,6 @@ pub(crate) fn eval_to_const(
expr: ExprId, expr: ExprId,
mode: ParamLoweringMode, mode: ParamLoweringMode,
ctx: &mut InferenceContext<'_>, ctx: &mut InferenceContext<'_>,
args: impl FnOnce() -> Generics,
debruijn: DebruijnIndex, debruijn: DebruijnIndex,
) -> Const { ) -> Const {
let db = ctx.db; let db = ctx.db;
@ -304,7 +305,9 @@ pub(crate) fn eval_to_const(
} }
if let Expr::Path(p) = &ctx.body.exprs[expr] { if let Expr::Path(p) = &ctx.body.exprs[expr] {
let resolver = &ctx.resolver; let resolver = &ctx.resolver;
if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) { if let Some(c) =
path_to_const(db, resolver, p, mode, || ctx.generics(), debruijn, infer[expr].clone())
{
return c; return c;
} }
} }

View file

@ -43,7 +43,7 @@ mod allow {
} }
pub fn incorrect_case(db: &dyn HirDatabase, owner: ModuleDefId) -> Vec<IncorrectCase> { pub fn incorrect_case(db: &dyn HirDatabase, owner: ModuleDefId) -> Vec<IncorrectCase> {
let _p = tracing::span!(tracing::Level::INFO, "validate_module_item").entered(); let _p = tracing::span!(tracing::Level::INFO, "incorrect_case").entered();
let mut validator = DeclValidator::new(db); let mut validator = DeclValidator::new(db);
validator.validate_item(owner); validator.validate_item(owner);
validator.sink validator.sink

View file

@ -11,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup};
use hir_expand::name; use hir_expand::name;
use itertools::Itertools; use itertools::Itertools;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use rustc_pattern_analysis::constructor::Constructor;
use syntax::{ast, AstNode}; use syntax::{ast, AstNode};
use tracing::debug; use tracing::debug;
use triomphe::Arc; use triomphe::Arc;
@ -190,45 +191,45 @@ impl ExprValidator {
let pattern_arena = Arena::new(); let pattern_arena = Arena::new();
let mut m_arms = Vec::with_capacity(arms.len()); let mut m_arms = Vec::with_capacity(arms.len());
let mut has_lowering_errors = false; let mut has_lowering_errors = false;
// Note: Skipping the entire diagnostic rather than just not including a faulty match arm is
// preferred to avoid the chance of false positives.
for arm in arms { for arm in arms {
if let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) { let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) else {
// We only include patterns whose type matches the type return;
// of the scrutinee expression. If we had an InvalidMatchArmPattern };
// diagnostic or similar we could raise that in an else
// block here. // We only include patterns whose type matches the type
// // of the scrutinee expression. If we had an InvalidMatchArmPattern
// When comparing the types, we also have to consider that rustc // diagnostic or similar we could raise that in an else
// will automatically de-reference the scrutinee expression type if // block here.
// necessary. //
// // When comparing the types, we also have to consider that rustc
// FIXME we should use the type checker for this. // will automatically de-reference the scrutinee expression type if
if (pat_ty == scrut_ty // necessary.
|| scrut_ty //
.as_reference() // FIXME we should use the type checker for this.
.map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) if (pat_ty == scrut_ty
.unwrap_or(false)) || scrut_ty
&& types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer) .as_reference()
{ .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
// If we had a NotUsefulMatchArm diagnostic, we could .unwrap_or(false))
// check the usefulness of each pattern as we added it && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer)
// to the matrix here. {
let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors); // If we had a NotUsefulMatchArm diagnostic, we could
let m_arm = pat_analysis::MatchArm { // check the usefulness of each pattern as we added it
pat: pattern_arena.alloc(pat), // to the matrix here.
has_guard: arm.guard.is_some(), let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors);
arm_data: (), let m_arm = pat_analysis::MatchArm {
}; pat: pattern_arena.alloc(pat),
m_arms.push(m_arm); has_guard: arm.guard.is_some(),
if !has_lowering_errors { arm_data: (),
continue; };
} m_arms.push(m_arm);
if !has_lowering_errors {
continue;
} }
} }
// If the pattern type doesn't fit the match expression, we skip this diagnostic.
// If we can't resolve the type of a pattern, or the pattern type doesn't
// fit the match expression, we skip this diagnostic. Skipping the entire
// diagnostic rather than just not including this match arm is preferred
// to avoid the chance of false positives.
cov_mark::hit!(validate_match_bailed_out); cov_mark::hit!(validate_match_bailed_out);
return; return;
} }
@ -266,15 +267,17 @@ impl ExprValidator {
let mut have_errors = false; let mut have_errors = false;
let deconstructed_pat = self.lower_pattern(&cx, pat, db, &mut have_errors); let deconstructed_pat = self.lower_pattern(&cx, pat, db, &mut have_errors);
// optimization, wildcard trivially hold
if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) {
continue;
}
let match_arm = rustc_pattern_analysis::MatchArm { let match_arm = rustc_pattern_analysis::MatchArm {
pat: pattern_arena.alloc(deconstructed_pat), pat: pattern_arena.alloc(deconstructed_pat),
has_guard: false, has_guard: false,
arm_data: (), arm_data: (),
}; };
if have_errors {
continue;
}
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) { let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
@ -531,8 +534,16 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) { fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) {
match infer.type_mismatch_for_pat(pat) { match infer.type_mismatch_for_pat(pat) {
Some(_) => *has_type_mismatches = true, Some(_) => *has_type_mismatches = true,
None if *has_type_mismatches => (),
None => { None => {
body[pat].walk_child_pats(|subpat| walk(subpat, body, infer, has_type_mismatches)) let pat = &body[pat];
if let Pat::ConstBlock(expr) | Pat::Lit(expr) = *pat {
*has_type_mismatches |= infer.type_mismatch_for_expr(expr).is_some();
if *has_type_mismatches {
return;
}
}
pat.walk_child_pats(|subpat| walk(subpat, body, infer, has_type_mismatches))
} }
} }
} }

View file

@ -1,9 +1,9 @@
//! Interface with `rustc_pattern_analysis`. //! Interface with `rustc_pattern_analysis`.
use std::fmt; use std::fmt;
use tracing::debug;
use hir_def::{DefWithBodyId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
use once_cell::unsync::Lazy;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use rustc_pattern_analysis::{ use rustc_pattern_analysis::{
constructor::{Constructor, ConstructorSet, VariantVisibility}, constructor::{Constructor, ConstructorSet, VariantVisibility},
@ -36,6 +36,24 @@ pub(crate) type WitnessPat<'p> = rustc_pattern_analysis::pat::WitnessPat<MatchCh
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum Void {} pub(crate) enum Void {}
/// An index type for enum variants. This ranges from 0 to `variants.len()`, whereas `EnumVariantId`
/// can take arbitrary large values (and hence mustn't be used with `IndexVec`/`BitSet`).
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct EnumVariantContiguousIndex(usize);
impl EnumVariantContiguousIndex {
fn from_enum_variant_id(db: &dyn HirDatabase, target_evid: EnumVariantId) -> Self {
// Find the index of this variant in the list of variants.
use hir_def::Lookup;
let i = target_evid.lookup(db.upcast()).index as usize;
EnumVariantContiguousIndex(i)
}
fn to_enum_variant_id(self, db: &dyn HirDatabase, eid: EnumId) -> EnumVariantId {
db.enum_data(eid).variants[self.0].0
}
}
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct MatchCheckCtx<'p> { pub(crate) struct MatchCheckCtx<'p> {
module: ModuleId, module: ModuleId,
@ -73,25 +91,27 @@ impl<'p> MatchCheckCtx<'p> {
} }
fn is_uninhabited(&self, ty: &Ty) -> bool { fn is_uninhabited(&self, ty: &Ty) -> bool {
is_ty_uninhabited_from(ty, self.module, self.db) is_ty_uninhabited_from(self.db, ty, self.module)
} }
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
fn is_foreign_non_exhaustive_enum(&self, ty: &Ty) -> bool { fn is_foreign_non_exhaustive(&self, adt: hir_def::AdtId) -> bool {
match ty.as_adt() { let is_local = adt.krate(self.db.upcast()) == self.module.krate();
Some((adt @ hir_def::AdtId::EnumId(_), _)) => { !is_local && self.db.attrs(adt.into()).by_key("non_exhaustive").exists()
let has_non_exhaustive_attr =
self.db.attrs(adt.into()).by_key("non_exhaustive").exists();
let is_local = adt.module(self.db.upcast()).krate() == self.module.krate();
has_non_exhaustive_attr && !is_local
}
_ => false,
}
} }
fn variant_id_for_adt(ctor: &Constructor<Self>, adt: hir_def::AdtId) -> Option<VariantId> { fn variant_id_for_adt(
db: &'p dyn HirDatabase,
ctor: &Constructor<Self>,
adt: hir_def::AdtId,
) -> Option<VariantId> {
match ctor { match ctor {
&Variant(id) => Some(id.into()), Variant(id) => {
let hir_def::AdtId::EnumId(eid) = adt else {
panic!("bad constructor {ctor:?} for adt {adt:?}")
};
Some(id.to_enum_variant_id(db, eid).into())
}
Struct | UnionField => match adt { Struct | UnionField => match adt {
hir_def::AdtId::EnumId(_) => None, hir_def::AdtId::EnumId(_) => None,
hir_def::AdtId::StructId(id) => Some(id.into()), hir_def::AdtId::StructId(id) => Some(id.into()),
@ -175,19 +195,24 @@ impl<'p> MatchCheckCtx<'p> {
ctor = Struct; ctor = Struct;
arity = 1; arity = 1;
} }
&TyKind::Adt(adt, _) => { &TyKind::Adt(AdtId(adt), _) => {
ctor = match pat.kind.as_ref() { ctor = match pat.kind.as_ref() {
PatKind::Leaf { .. } if matches!(adt.0, hir_def::AdtId::UnionId(_)) => { PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => {
UnionField UnionField
} }
PatKind::Leaf { .. } => Struct, PatKind::Leaf { .. } => Struct,
PatKind::Variant { enum_variant, .. } => Variant(*enum_variant), PatKind::Variant { enum_variant, .. } => {
Variant(EnumVariantContiguousIndex::from_enum_variant_id(
self.db,
*enum_variant,
))
}
_ => { _ => {
never!(); never!();
Wildcard Wildcard
} }
}; };
let variant = Self::variant_id_for_adt(&ctor, adt.0).unwrap(); let variant = Self::variant_id_for_adt(self.db, &ctor, adt).unwrap();
arity = variant.variant_data(self.db.upcast()).fields().len(); arity = variant.variant_data(self.db.upcast()).fields().len();
} }
_ => { _ => {
@ -239,7 +264,7 @@ impl<'p> MatchCheckCtx<'p> {
PatKind::Deref { subpattern: subpatterns.next().unwrap() } PatKind::Deref { subpattern: subpatterns.next().unwrap() }
} }
TyKind::Adt(adt, substs) => { TyKind::Adt(adt, substs) => {
let variant = Self::variant_id_for_adt(pat.ctor(), adt.0).unwrap(); let variant = Self::variant_id_for_adt(self.db, pat.ctor(), adt.0).unwrap();
let subpatterns = self let subpatterns = self
.list_variant_fields(pat.ty(), variant) .list_variant_fields(pat.ty(), variant)
.zip(subpatterns) .zip(subpatterns)
@ -277,7 +302,7 @@ impl<'p> MatchCheckCtx<'p> {
impl<'p> PatCx for MatchCheckCtx<'p> { impl<'p> PatCx for MatchCheckCtx<'p> {
type Error = (); type Error = ();
type Ty = Ty; type Ty = Ty;
type VariantIdx = EnumVariantId; type VariantIdx = EnumVariantContiguousIndex;
type StrLit = Void; type StrLit = Void;
type ArmData = (); type ArmData = ();
type PatData = PatData<'p>; type PatData = PatData<'p>;
@ -303,7 +328,7 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
// patterns. If we're here we can assume this is a box pattern. // patterns. If we're here we can assume this is a box pattern.
1 1
} else { } else {
let variant = Self::variant_id_for_adt(ctor, adt).unwrap(); let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
variant.variant_data(self.db.upcast()).fields().len() variant.variant_data(self.db.upcast()).fields().len()
} }
} }
@ -343,25 +368,22 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
single(subst_ty) single(subst_ty)
} else { } else {
let variant = Self::variant_id_for_adt(ctor, adt).unwrap(); let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
let (adt, _) = ty.as_adt().unwrap();
let adt_is_local =
variant.module(self.db.upcast()).krate() == self.module.krate();
// Whether we must not match the fields of this variant exhaustively. // Whether we must not match the fields of this variant exhaustively.
let is_non_exhaustive = let is_non_exhaustive = Lazy::new(|| self.is_foreign_non_exhaustive(adt));
self.db.attrs(variant.into()).by_key("non_exhaustive").exists() let visibilities = Lazy::new(|| self.db.field_visibilities(variant));
&& !adt_is_local;
let visibilities = self.db.field_visibilities(variant);
self.list_variant_fields(ty, variant) self.list_variant_fields(ty, variant)
.map(move |(fid, ty)| { .map(move |(fid, ty)| {
let is_visible = matches!(adt, hir_def::AdtId::EnumId(..)) let is_visible = || {
|| visibilities[fid] matches!(adt, hir_def::AdtId::EnumId(..))
.is_visible_from(self.db.upcast(), self.module); || visibilities[fid]
.is_visible_from(self.db.upcast(), self.module)
};
let is_uninhabited = self.is_uninhabited(&ty); let is_uninhabited = self.is_uninhabited(&ty);
let private_uninhabited = let private_uninhabited =
is_uninhabited && (!is_visible || is_non_exhaustive); is_uninhabited && (!is_visible() || *is_non_exhaustive);
(ty, PrivateUninhabitedField(private_uninhabited)) (ty, PrivateUninhabitedField(private_uninhabited))
}) })
.collect() .collect()
@ -413,23 +435,26 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
TyKind::Scalar(Scalar::Char) => unhandled(), TyKind::Scalar(Scalar::Char) => unhandled(),
TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(), TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(),
TyKind::Array(..) | TyKind::Slice(..) => unhandled(), TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), subst) => { &TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => {
let enum_data = cx.db.enum_data(*enum_id); let enum_data = cx.db.enum_data(enum_id);
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty); let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt);
if enum_data.variants.is_empty() && !is_declared_nonexhaustive { if enum_data.variants.is_empty() && !is_declared_nonexhaustive {
ConstructorSet::NoConstructors ConstructorSet::NoConstructors
} else { } else {
let mut variants = FxHashMap::default(); let mut variants = FxHashMap::with_capacity_and_hasher(
for &(variant, _) in enum_data.variants.iter() { enum_data.variants.len(),
Default::default(),
);
for (i, &(variant, _)) in enum_data.variants.iter().enumerate() {
let is_uninhabited = let is_uninhabited =
is_enum_variant_uninhabited_from(variant, subst, cx.module, cx.db); is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module);
let visibility = if is_uninhabited { let visibility = if is_uninhabited {
VariantVisibility::Empty VariantVisibility::Empty
} else { } else {
VariantVisibility::Visible VariantVisibility::Visible
}; };
variants.insert(variant, visibility); variants.insert(EnumVariantContiguousIndex(i), visibility);
} }
ConstructorSet::Variants { ConstructorSet::Variants {
@ -453,10 +478,10 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
f: &mut fmt::Formatter<'_>, f: &mut fmt::Formatter<'_>,
pat: &rustc_pattern_analysis::pat::DeconstructedPat<Self>, pat: &rustc_pattern_analysis::pat::DeconstructedPat<Self>,
) -> fmt::Result { ) -> fmt::Result {
let variant =
pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(pat.ctor(), adt));
let db = pat.data().db; let db = pat.data().db;
let variant =
pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(db, pat.ctor(), adt));
if let Some(variant) = variant { if let Some(variant) = variant {
match variant { match variant {
VariantId::EnumVariantId(v) => { VariantId::EnumVariantId(v) => {
@ -474,7 +499,7 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
} }
fn bug(&self, fmt: fmt::Arguments<'_>) { fn bug(&self, fmt: fmt::Arguments<'_>) {
debug!("{}", fmt) never!("{}", fmt)
} }
fn complexity_exceeded(&self) -> Result<(), Self::Error> { fn complexity_exceeded(&self) -> Result<(), Self::Error> {

View file

@ -4,7 +4,7 @@
use hir_def::{ use hir_def::{
body::Body, body::Body,
hir::{Expr, ExprId, UnaryOp}, hir::{Expr, ExprId, UnaryOp},
resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
DefWithBodyId, DefWithBodyId,
}; };
@ -13,9 +13,9 @@ use crate::{
}; };
pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> { pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
let infer = db.infer(def); let _p = tracing::span!(tracing::Level::INFO, "missing_unsafe").entered();
let mut res = Vec::new();
let mut res = Vec::new();
let is_unsafe = match def { let is_unsafe = match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(), DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
DefWithBodyId::StaticId(_) DefWithBodyId::StaticId(_)
@ -28,6 +28,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
} }
let body = db.body(def); let body = db.body(def);
let infer = db.infer(def);
unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| { unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| {
if !expr.inside_unsafe_block { if !expr.inside_unsafe_block {
res.push(expr.expr); res.push(expr.expr);
@ -51,14 +52,24 @@ pub fn unsafe_expressions(
current: ExprId, current: ExprId,
unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr), unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
) { ) {
walk_unsafe(db, infer, def, body, current, false, unsafe_expr_cb) walk_unsafe(
db,
infer,
body,
&mut resolver_for_expr(db.upcast(), def, current),
def,
current,
false,
unsafe_expr_cb,
)
} }
fn walk_unsafe( fn walk_unsafe(
db: &dyn HirDatabase, db: &dyn HirDatabase,
infer: &InferenceResult, infer: &InferenceResult,
def: DefWithBodyId,
body: &Body, body: &Body,
resolver: &mut Resolver,
def: DefWithBodyId,
current: ExprId, current: ExprId,
inside_unsafe_block: bool, inside_unsafe_block: bool,
unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr), unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
@ -73,13 +84,14 @@ fn walk_unsafe(
} }
} }
Expr::Path(path) => { Expr::Path(path) => {
let resolver = resolver_for_expr(db.upcast(), def, current); let g = resolver.update_to_inner_scope(db.upcast(), def, current);
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path); let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
if db.static_data(id).mutable { if db.static_data(id).mutable {
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
} }
} }
resolver.reset_to_guard(g);
} }
Expr::MethodCall { .. } => { Expr::MethodCall { .. } => {
if infer if infer
@ -97,13 +109,13 @@ fn walk_unsafe(
} }
Expr::Unsafe { .. } => { Expr::Unsafe { .. } => {
return expr.walk_child_exprs(|child| { return expr.walk_child_exprs(|child| {
walk_unsafe(db, infer, def, body, child, true, unsafe_expr_cb); walk_unsafe(db, infer, body, resolver, def, child, true, unsafe_expr_cb);
}); });
} }
_ => {} _ => {}
} }
expr.walk_child_exprs(|child| { expr.walk_child_exprs(|child| {
walk_unsafe(db, infer, def, body, child, inside_unsafe_block, unsafe_expr_cb); walk_unsafe(db, infer, body, resolver, def, child, inside_unsafe_block, unsafe_expr_cb);
}); });
} }

View file

@ -9,6 +9,7 @@ use std::{
use base_db::CrateId; use base_db::CrateId;
use chalk_ir::{BoundVar, Safety, TyKind}; use chalk_ir::{BoundVar, Safety, TyKind};
use either::Either;
use hir_def::{ use hir_def::{
data::adt::VariantData, data::adt::VariantData,
db::DefDatabase, db::DefDatabase,
@ -27,7 +28,7 @@ use intern::{Internable, Interned};
use itertools::Itertools; use itertools::Itertools;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use stdx::never; use stdx::{never, IsNoneOr};
use triomphe::Arc; use triomphe::Arc;
use crate::{ use crate::{
@ -40,10 +41,11 @@ use crate::{
mir::pad16, mir::pad16,
primitive, to_assoc_type_id, primitive, to_assoc_type_id,
utils::{self, detect_variant_from_bytes, generics, ClosureSubst}, utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue, AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const,
DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime,
MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt,
Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty,
TyExt, WhereClause,
}; };
pub trait HirWrite: fmt::Write { pub trait HirWrite: fmt::Write {
@ -58,11 +60,18 @@ impl HirWrite for String {}
impl HirWrite for fmt::Formatter<'_> {} impl HirWrite for fmt::Formatter<'_> {}
pub struct HirFormatter<'a> { pub struct HirFormatter<'a> {
/// The database handle
pub db: &'a dyn HirDatabase, pub db: &'a dyn HirDatabase,
/// The sink to write into
fmt: &'a mut dyn HirWrite, fmt: &'a mut dyn HirWrite,
/// A buffer to intercept writes with, this allows us to track the overall size of the formatted output.
buf: String, buf: String,
/// The current size of the formatted output.
curr_size: usize, curr_size: usize,
pub(crate) max_size: Option<usize>, /// Size from which we should truncate the output.
max_size: Option<usize>,
/// When rendering something that has a concept of "children" (like fields in a struct), this limits
/// how many should be rendered.
pub entity_limit: Option<usize>, pub entity_limit: Option<usize>,
omit_verbose_types: bool, omit_verbose_types: bool,
closure_style: ClosureStyle, closure_style: ClosureStyle,
@ -302,7 +311,6 @@ impl DisplayTarget {
#[derive(Debug)] #[derive(Debug)]
pub enum DisplaySourceCodeError { pub enum DisplaySourceCodeError {
PathNotFound, PathNotFound,
UnknownType,
Coroutine, Coroutine,
OpaqueType, OpaqueType,
} }
@ -414,12 +422,7 @@ impl HirDisplay for ProjectionTy {
let proj_params_count = let proj_params_count =
self.substitution.len(Interner) - trait_ref.substitution.len(Interner); self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count]; let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
if !proj_params.is_empty() { hir_fmt_generics(f, proj_params, None)
write!(f, "<")?;
f.write_joined(proj_params, ", ")?;
write!(f, ">")?;
}
Ok(())
} }
} }
@ -452,7 +455,7 @@ impl HirDisplay for Const {
ConstValue::Placeholder(idx) => { ConstValue::Placeholder(idx) => {
let id = from_placeholder_idx(f.db, *idx); let id = from_placeholder_idx(f.db, *idx);
let generics = generics(f.db.upcast(), id.parent); let generics = generics(f.db.upcast(), id.parent);
let param_data = &generics.params.type_or_consts[id.local_id]; let param_data = &generics.params[id.local_id];
write!(f, "{}", param_data.name().unwrap().display(f.db.upcast()))?; write!(f, "{}", param_data.name().unwrap().display(f.db.upcast()))?;
Ok(()) Ok(())
} }
@ -460,7 +463,11 @@ impl HirDisplay for Const {
ConstScalar::Bytes(b, m) => render_const_scalar(f, b, m, &data.ty), ConstScalar::Bytes(b, m) => render_const_scalar(f, b, m, &data.ty),
ConstScalar::UnevaluatedConst(c, parameters) => { ConstScalar::UnevaluatedConst(c, parameters) => {
write!(f, "{}", c.name(f.db.upcast()))?; write!(f, "{}", c.name(f.db.upcast()))?;
hir_fmt_generics(f, parameters, c.generic_def(f.db.upcast()))?; hir_fmt_generics(
f,
parameters.as_slice(Interner),
c.generic_def(f.db.upcast()),
)?;
Ok(()) Ok(())
} }
ConstScalar::Unknown => f.write_char('_'), ConstScalar::Unknown => f.write_char('_'),
@ -936,36 +943,31 @@ impl HirDisplay for Ty {
} }
}; };
f.end_location_link(); f.end_location_link();
if parameters.len(Interner) > 0 { if parameters.len(Interner) > 0 {
let generics = generics(db.upcast(), def.into()); let generics = generics(db.upcast(), def.into());
let ( let (parent_len, self_, type_, const_, impl_, lifetime) =
parent_params, generics.provenance_split();
self_param, let parameters = parameters.as_slice(Interner);
type_params,
const_params,
_impl_trait_params,
lifetime_params,
) = generics.provenance_split();
let total_len =
parent_params + self_param + type_params + const_params + lifetime_params;
// We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
if total_len > 0 { if parameters.len() - impl_ > 0 {
// `parameters` are in the order of fn's params (including impl traits), fn's lifetimes // `parameters` are in the order of fn's params (including impl traits), fn's lifetimes
// parent's params (those from enclosing impl or trait, if any). // parent's params (those from enclosing impl or trait, if any).
let parameters = parameters.as_slice(Interner); let (fn_params, other) =
let fn_params_len = self_param + type_params + const_params; parameters.split_at(self_ + type_ + const_ + lifetime);
// This will give slice till last type or const let (_impl, parent_params) = other.split_at(impl_);
let fn_params = parameters.get(..fn_params_len); debug_assert_eq!(parent_params.len(), parent_len);
let fn_lt_params =
parameters.get(fn_params_len..(fn_params_len + lifetime_params)); let parent_params =
let parent_params = parameters.get(parameters.len() - parent_params..); generic_args_sans_defaults(f, Some(def.into()), parent_params);
let params = parent_params let fn_params = generic_args_sans_defaults(f, Some(def.into()), fn_params);
.into_iter()
.chain(fn_lt_params)
.chain(fn_params)
.flatten();
write!(f, "<")?; write!(f, "<")?;
f.write_joined(params, ", ")?; hir_fmt_generic_arguments(f, parent_params)?;
if !parent_params.is_empty() && !fn_params.is_empty() {
write!(f, ", ")?;
}
hir_fmt_generic_arguments(f, fn_params)?;
write!(f, ">")?; write!(f, ">")?;
} }
} }
@ -1009,7 +1011,7 @@ impl HirDisplay for Ty {
let generic_def = self.as_generic_def(db); let generic_def = self.as_generic_def(db);
hir_fmt_generics(f, parameters, generic_def)?; hir_fmt_generics(f, parameters.as_slice(Interner), generic_def)?;
} }
TyKind::AssociatedType(assoc_type_id, parameters) => { TyKind::AssociatedType(assoc_type_id, parameters) => {
let type_alias = from_assoc_type_id(*assoc_type_id); let type_alias = from_assoc_type_id(*assoc_type_id);
@ -1032,20 +1034,15 @@ impl HirDisplay for Ty {
f.end_location_link(); f.end_location_link();
// Note that the generic args for the associated type come before those for the // Note that the generic args for the associated type come before those for the
// trait (including the self type). // trait (including the self type).
// FIXME: reconsider the generic args order upon formatting? hir_fmt_generics(f, parameters.as_slice(Interner), None)
if parameters.len(Interner) > 0 {
write!(f, "<")?;
f.write_joined(parameters.as_slice(Interner), ", ")?;
write!(f, ">")?;
}
} else { } else {
let projection_ty = ProjectionTy { let projection_ty = ProjectionTy {
associated_ty_id: to_assoc_type_id(type_alias), associated_ty_id: to_assoc_type_id(type_alias),
substitution: parameters.clone(), substitution: parameters.clone(),
}; };
projection_ty.hir_fmt(f)?; projection_ty.hir_fmt(f)
} }?;
} }
TyKind::Foreign(type_alias) => { TyKind::Foreign(type_alias) => {
let alias = from_foreign_def_id(*type_alias); let alias = from_foreign_def_id(*type_alias);
@ -1072,6 +1069,7 @@ impl HirDisplay for Ty {
write_bounds_like_dyn_trait_with_prefix( write_bounds_like_dyn_trait_with_prefix(
f, f,
"impl", "impl",
Either::Left(self),
bounds.skip_binders(), bounds.skip_binders(),
SizedByDefault::Sized { anchor: krate }, SizedByDefault::Sized { anchor: krate },
)?; )?;
@ -1087,6 +1085,7 @@ impl HirDisplay for Ty {
write_bounds_like_dyn_trait_with_prefix( write_bounds_like_dyn_trait_with_prefix(
f, f,
"impl", "impl",
Either::Left(self),
bounds.skip_binders(), bounds.skip_binders(),
SizedByDefault::Sized { anchor: krate }, SizedByDefault::Sized { anchor: krate },
)?; )?;
@ -1137,7 +1136,7 @@ impl HirDisplay for Ty {
} }
ClosureStyle::ClosureWithSubst => { ClosureStyle::ClosureWithSubst => {
write!(f, "{{closure#{:?}}}", id.0.as_u32())?; write!(f, "{{closure#{:?}}}", id.0.as_u32())?;
return hir_fmt_generics(f, substs, None); return hir_fmt_generics(f, substs.as_slice(Interner), None);
} }
_ => (), _ => (),
} }
@ -1173,7 +1172,7 @@ impl HirDisplay for Ty {
TyKind::Placeholder(idx) => { TyKind::Placeholder(idx) => {
let id = from_placeholder_idx(db, *idx); let id = from_placeholder_idx(db, *idx);
let generics = generics(db.upcast(), id.parent); let generics = generics(db.upcast(), id.parent);
let param_data = &generics.params.type_or_consts[id.local_id]; let param_data = &generics.params[id.local_id];
match param_data { match param_data {
TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeOrConstParamData::TypeParamData(p) => match p.provenance {
TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
@ -1189,21 +1188,24 @@ impl HirDisplay for Ty {
.generic_predicates(id.parent) .generic_predicates(id.parent)
.iter() .iter()
.map(|pred| pred.clone().substitute(Interner, &substs)) .map(|pred| pred.clone().substitute(Interner, &substs))
.filter(|wc| match &wc.skip_binders() { .filter(|wc| match wc.skip_binders() {
WhereClause::Implemented(tr) => { WhereClause::Implemented(tr) => {
&tr.self_type_parameter(Interner) == self tr.self_type_parameter(Interner) == *self
} }
WhereClause::AliasEq(AliasEq { WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(proj), alias: AliasTy::Projection(proj),
ty: _, ty: _,
}) => &proj.self_type_parameter(db) == self, }) => proj.self_type_parameter(db) == *self,
_ => false, WhereClause::AliasEq(_) => false,
WhereClause::TypeOutlives(to) => to.ty == *self,
WhereClause::LifetimeOutlives(_) => false,
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let krate = id.parent.module(db.upcast()).krate(); let krate = id.parent.module(db.upcast()).krate();
write_bounds_like_dyn_trait_with_prefix( write_bounds_like_dyn_trait_with_prefix(
f, f,
"impl", "impl",
Either::Left(self),
&bounds, &bounds,
SizedByDefault::Sized { anchor: krate }, SizedByDefault::Sized { anchor: krate },
)?; )?;
@ -1229,6 +1231,7 @@ impl HirDisplay for Ty {
write_bounds_like_dyn_trait_with_prefix( write_bounds_like_dyn_trait_with_prefix(
f, f,
"dyn", "dyn",
Either::Left(self),
&bounds, &bounds,
SizedByDefault::NotSized, SizedByDefault::NotSized,
)?; )?;
@ -1252,6 +1255,7 @@ impl HirDisplay for Ty {
write_bounds_like_dyn_trait_with_prefix( write_bounds_like_dyn_trait_with_prefix(
f, f,
"impl", "impl",
Either::Left(self),
bounds.skip_binders(), bounds.skip_binders(),
SizedByDefault::Sized { anchor: krate }, SizedByDefault::Sized { anchor: krate },
)?; )?;
@ -1266,6 +1270,7 @@ impl HirDisplay for Ty {
write_bounds_like_dyn_trait_with_prefix( write_bounds_like_dyn_trait_with_prefix(
f, f,
"impl", "impl",
Either::Left(self),
bounds.skip_binders(), bounds.skip_binders(),
SizedByDefault::Sized { anchor: krate }, SizedByDefault::Sized { anchor: krate },
)?; )?;
@ -1277,11 +1282,10 @@ impl HirDisplay for Ty {
} }
TyKind::Error => { TyKind::Error => {
if f.display_target.is_source_code() { if f.display_target.is_source_code() {
return Err(HirDisplayError::DisplaySourceCodeError( f.write_char('_')?;
DisplaySourceCodeError::UnknownType, } else {
)); write!(f, "{{unknown}}")?;
} }
write!(f, "{{unknown}}")?;
} }
TyKind::InferenceVar(..) => write!(f, "_")?, TyKind::InferenceVar(..) => write!(f, "_")?,
TyKind::Coroutine(_, subst) => { TyKind::Coroutine(_, subst) => {
@ -1318,93 +1322,92 @@ impl HirDisplay for Ty {
fn hir_fmt_generics( fn hir_fmt_generics(
f: &mut HirFormatter<'_>, f: &mut HirFormatter<'_>,
parameters: &Substitution, parameters: &[GenericArg],
generic_def: Option<hir_def::GenericDefId>, generic_def: Option<hir_def::GenericDefId>,
) -> Result<(), HirDisplayError> { ) -> Result<(), HirDisplayError> {
let db = f.db; if parameters.is_empty() {
if parameters.len(Interner) > 0 { return Ok(());
use std::cmp::Ordering; }
let param_compare =
|a: &GenericArg, b: &GenericArg| match (a.data(Interner), b.data(Interner)) {
(crate::GenericArgData::Lifetime(_), crate::GenericArgData::Lifetime(_)) => {
Ordering::Equal
}
(crate::GenericArgData::Lifetime(_), _) => Ordering::Less,
(_, crate::GenericArgData::Lifetime(_)) => Ordering::Less,
(_, _) => Ordering::Equal,
};
let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
match generic_def
.map(|generic_def_id| db.generic_defaults(generic_def_id))
.filter(|defaults| !defaults.is_empty())
{
None => parameters.as_slice(Interner),
Some(default_parameters) => {
fn should_show(
parameter: &GenericArg,
default_parameters: &[Binders<GenericArg>],
i: usize,
parameters: &Substitution,
) -> bool {
if parameter.ty(Interner).map(|it| it.kind(Interner))
== Some(&TyKind::Error)
{
return true;
}
if let Some(ConstValue::Concrete(c)) =
parameter.constant(Interner).map(|it| &it.data(Interner).value)
{
if c.interned == ConstScalar::Unknown {
return true;
}
}
if parameter.lifetime(Interner).map(|it| it.data(Interner))
== Some(&crate::LifetimeData::Static)
{
return true;
}
let default_parameter = match default_parameters.get(i) {
Some(it) => it,
None => return true,
};
let actual_default =
default_parameter.clone().substitute(Interner, &parameters);
parameter != &actual_default
}
let mut default_from = 0;
for (i, parameter) in parameters.iter(Interner).enumerate() {
if should_show(parameter, &default_parameters, i, parameters) {
default_from = i + 1;
}
}
&parameters.as_slice(Interner)[0..default_from]
}
}
} else {
parameters.as_slice(Interner)
};
//FIXME: Should handle the ordering of lifetimes when creating substitutions
let mut parameters_to_write = parameters_to_write.to_vec();
parameters_to_write.sort_by(param_compare);
if !parameters_to_write.is_empty() {
write!(f, "<")?;
let mut first = true;
for generic_arg in parameters_to_write {
if !first {
write!(f, ", ")?;
}
first = false;
if f.display_target.is_source_code()
&& generic_arg.ty(Interner).map(|ty| ty.kind(Interner)) == Some(&TyKind::Error)
{
write!(f, "_")?;
} else {
generic_arg.hir_fmt(f)?;
}
}
write!(f, ">")?; let parameters_to_write = generic_args_sans_defaults(f, generic_def, parameters);
if !parameters_to_write.is_empty() {
write!(f, "<")?;
hir_fmt_generic_arguments(f, parameters_to_write)?;
write!(f, ">")?;
}
Ok(())
}
fn generic_args_sans_defaults<'ga>(
f: &mut HirFormatter<'_>,
generic_def: Option<hir_def::GenericDefId>,
parameters: &'ga [GenericArg],
) -> &'ga [GenericArg] {
if f.display_target.is_source_code() || f.omit_verbose_types() {
match generic_def
.map(|generic_def_id| f.db.generic_defaults(generic_def_id))
.filter(|it| !it.is_empty())
{
None => parameters,
Some(default_parameters) => {
let should_show = |arg: &GenericArg, i: usize| {
let is_err = |arg: &GenericArg| match arg.data(Interner) {
chalk_ir::GenericArgData::Lifetime(it) => {
*it.data(Interner) == LifetimeData::Error
}
chalk_ir::GenericArgData::Ty(it) => *it.kind(Interner) == TyKind::Error,
chalk_ir::GenericArgData::Const(it) => matches!(
it.data(Interner).value,
ConstValue::Concrete(ConcreteConst {
interned: ConstScalar::Unknown,
..
})
),
};
// if the arg is error like, render it to inform the user
if is_err(arg) {
return true;
}
// otherwise, if the arg is equal to the param default, hide it (unless the
// default is an error which can happen for the trait Self type)
default_parameters.get(i).is_none_or(|default_parameter| {
// !is_err(default_parameter.skip_binders())
// &&
arg != &default_parameter.clone().substitute(Interner, &parameters)
})
};
let mut default_from = 0;
for (i, parameter) in parameters.iter().enumerate() {
if should_show(parameter, i) {
default_from = i + 1;
}
}
&parameters[0..default_from]
}
} }
} else {
parameters
}
}
fn hir_fmt_generic_arguments(
f: &mut HirFormatter<'_>,
parameters: &[GenericArg],
) -> Result<(), HirDisplayError> {
let mut first = true;
let lifetime_offset = parameters.iter().position(|arg| arg.lifetime(Interner).is_some());
let (ty_or_const, lifetimes) = match lifetime_offset {
Some(offset) => parameters.split_at(offset),
None => (parameters, &[][..]),
};
for generic_arg in lifetimes.iter().chain(ty_or_const) {
if !first {
write!(f, ", ")?;
}
first = false;
generic_arg.hir_fmt(f)?;
} }
Ok(()) Ok(())
} }
@ -1468,6 +1471,7 @@ impl SizedByDefault {
pub fn write_bounds_like_dyn_trait_with_prefix( pub fn write_bounds_like_dyn_trait_with_prefix(
f: &mut HirFormatter<'_>, f: &mut HirFormatter<'_>,
prefix: &str, prefix: &str,
this: Either<&Ty, &Lifetime>,
predicates: &[QuantifiedWhereClause], predicates: &[QuantifiedWhereClause],
default_sized: SizedByDefault, default_sized: SizedByDefault,
) -> Result<(), HirDisplayError> { ) -> Result<(), HirDisplayError> {
@ -1476,7 +1480,7 @@ pub fn write_bounds_like_dyn_trait_with_prefix(
|| predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. }) || predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. })
{ {
write!(f, " ")?; write!(f, " ")?;
write_bounds_like_dyn_trait(f, predicates, default_sized) write_bounds_like_dyn_trait(f, this, predicates, default_sized)
} else { } else {
Ok(()) Ok(())
} }
@ -1484,6 +1488,7 @@ pub fn write_bounds_like_dyn_trait_with_prefix(
fn write_bounds_like_dyn_trait( fn write_bounds_like_dyn_trait(
f: &mut HirFormatter<'_>, f: &mut HirFormatter<'_>,
this: Either<&Ty, &Lifetime>,
predicates: &[QuantifiedWhereClause], predicates: &[QuantifiedWhereClause],
default_sized: SizedByDefault, default_sized: SizedByDefault,
) -> Result<(), HirDisplayError> { ) -> Result<(), HirDisplayError> {
@ -1524,23 +1529,54 @@ fn write_bounds_like_dyn_trait(
f.start_location_link(trait_.into()); f.start_location_link(trait_.into());
write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?; write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
f.end_location_link(); f.end_location_link();
if let [_, params @ ..] = trait_ref.substitution.as_slice(Interner) { if is_fn_trait {
if is_fn_trait { if let [_self, params @ ..] = trait_ref.substitution.as_slice(Interner) {
if let Some(args) = if let Some(args) =
params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple()) params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple())
{ {
write!(f, "(")?; write!(f, "(")?;
f.write_joined(args.as_slice(Interner), ", ")?; hir_fmt_generic_arguments(f, args.as_slice(Interner))?;
write!(f, ")")?; write!(f, ")")?;
} }
} else if !params.is_empty() { }
write!(f, "<")?; } else {
f.write_joined(params, ", ")?; let params = generic_args_sans_defaults(
// there might be assoc type bindings, so we leave the angle brackets open f,
angle_open = true; Some(trait_.into()),
trait_ref.substitution.as_slice(Interner),
);
if let [_self, params @ ..] = params {
if !params.is_empty() {
write!(f, "<")?;
hir_fmt_generic_arguments(f, params)?;
// there might be assoc type bindings, so we leave the angle brackets open
angle_open = true;
}
} }
} }
} }
WhereClause::TypeOutlives(to) if Either::Left(&to.ty) == this => {
if !is_fn_trait && angle_open {
write!(f, ">")?;
angle_open = false;
}
if !first {
write!(f, " + ")?;
}
to.lifetime.hir_fmt(f)?;
}
WhereClause::TypeOutlives(_) => {}
WhereClause::LifetimeOutlives(lo) if Either::Right(&lo.a) == this => {
if !is_fn_trait && angle_open {
write!(f, ">")?;
angle_open = false;
}
if !first {
write!(f, " + ")?;
}
lo.b.hir_fmt(f)?;
}
WhereClause::LifetimeOutlives(_) => {}
WhereClause::AliasEq(alias_eq) if is_fn_trait => { WhereClause::AliasEq(alias_eq) if is_fn_trait => {
is_fn_trait = false; is_fn_trait = false;
if !alias_eq.ty.is_unit() { if !alias_eq.ty.is_unit() {
@ -1567,9 +1603,9 @@ fn write_bounds_like_dyn_trait(
let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self(); let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
if proj_arg_count > 0 { if proj_arg_count > 0 {
write!(f, "<")?; write!(f, "<")?;
f.write_joined( hir_fmt_generic_arguments(
f,
&proj.substitution.as_slice(Interner)[..proj_arg_count], &proj.substitution.as_slice(Interner)[..proj_arg_count],
", ",
)?; )?;
write!(f, ">")?; write!(f, ">")?;
} }
@ -1577,10 +1613,6 @@ fn write_bounds_like_dyn_trait(
} }
ty.hir_fmt(f)?; ty.hir_fmt(f)?;
} }
// FIXME implement these
WhereClause::LifetimeOutlives(_) => {}
WhereClause::TypeOutlives(_) => {}
} }
first = false; first = false;
} }
@ -1630,12 +1662,7 @@ fn fmt_trait_ref(
f.start_location_link(trait_.into()); f.start_location_link(trait_.into());
write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?; write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
f.end_location_link(); f.end_location_link();
if tr.substitution.len(Interner) > 1 { hir_fmt_generics(f, &tr.substitution.as_slice(Interner)[1..], None)
write!(f, "<")?;
f.write_joined(&tr.substitution.as_slice(Interner)[1..], ", ")?;
write!(f, ">")?;
}
Ok(())
} }
impl HirDisplay for TraitRef { impl HirDisplay for TraitRef {
@ -1690,16 +1717,18 @@ impl HirDisplay for Lifetime {
impl HirDisplay for LifetimeData { impl HirDisplay for LifetimeData {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
match self { match self {
LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
LifetimeData::InferenceVar(_) => write!(f, "_"),
LifetimeData::Placeholder(idx) => { LifetimeData::Placeholder(idx) => {
let id = lt_from_placeholder_idx(f.db, *idx); let id = lt_from_placeholder_idx(f.db, *idx);
let generics = generics(f.db.upcast(), id.parent); let generics = generics(f.db.upcast(), id.parent);
let param_data = &generics.params.lifetimes[id.local_id]; let param_data = &generics.params[id.local_id];
write!(f, "{}", param_data.name.display(f.db.upcast()))?; write!(f, "{}", param_data.name.display(f.db.upcast()))?;
Ok(()) Ok(())
} }
_ if f.display_target.is_source_code() => write!(f, "'_"),
LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
LifetimeData::InferenceVar(_) => write!(f, "_"),
LifetimeData::Static => write!(f, "'static"), LifetimeData::Static => write!(f, "'static"),
LifetimeData::Error => write!(f, "'{{error}}"),
LifetimeData::Erased => Ok(()), LifetimeData::Erased => Ok(()),
LifetimeData::Phantom(_, _) => Ok(()), LifetimeData::Phantom(_, _) => Ok(()),
} }

View file

@ -55,12 +55,12 @@ use triomphe::Arc;
use crate::{ use crate::{
db::HirDatabase, db::HirDatabase,
fold_tys, error_lifetime, fold_tys,
infer::{coerce::CoerceMany, unify::InferenceTable}, infer::{coerce::CoerceMany, unify::InferenceTable},
lower::ImplTraitLoweringMode, lower::ImplTraitLoweringMode,
static_lifetime, to_assoc_type_id, to_assoc_type_id,
traits::FnTrait, traits::FnTrait,
utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, utils::{Generics, InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution, ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution,
TraitEnvironment, Ty, TyBuilder, TyExt, TraitEnvironment, Ty, TyBuilder, TyExt,
@ -326,7 +326,7 @@ pub struct Adjustment {
impl Adjustment { impl Adjustment {
pub fn borrow(m: Mutability, ty: Ty) -> Self { pub fn borrow(m: Mutability, ty: Ty) -> Self {
let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner); let ty = TyKind::Ref(m, error_lifetime(), ty).intern(Interner);
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty } Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
} }
} }
@ -429,7 +429,10 @@ pub struct InferenceResult {
/// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop. /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
pub type_of_for_iterator: FxHashMap<ExprId, Ty>, pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
/// Whether there are any type-mismatching errors in the result.
pub(crate) has_errors: bool,
/// Interned common types to return references to. /// Interned common types to return references to.
// FIXME: Move this into `InferenceContext`
standard_types: InternedStandardTypes, standard_types: InternedStandardTypes,
/// Stores the types which were implicitly dereferenced in pattern binding modes. /// Stores the types which were implicitly dereferenced in pattern binding modes.
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>, pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
@ -628,6 +631,10 @@ impl<'a> InferenceContext<'a> {
} }
} }
pub(crate) fn generics(&self) -> Option<Generics> {
Some(crate::utils::generics(self.db.upcast(), self.resolver.generic_def()?))
}
// FIXME: This function should be private in module. It is currently only used in the consteval, since we need // FIXME: This function should be private in module. It is currently only used in the consteval, since we need
// `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
// used this function for another workaround, mention it here. If you really need this function and believe that // used this function for another workaround, mention it here. If you really need this function and believe that
@ -654,6 +661,7 @@ impl<'a> InferenceContext<'a> {
type_of_rpit, type_of_rpit,
type_of_for_iterator, type_of_for_iterator,
type_mismatches, type_mismatches,
has_errors,
standard_types: _, standard_types: _,
pat_adjustments, pat_adjustments,
binding_modes: _, binding_modes: _,
@ -695,6 +703,9 @@ impl<'a> InferenceContext<'a> {
for ty in type_of_for_iterator.values_mut() { for ty in type_of_for_iterator.values_mut() {
*ty = table.resolve_completely(ty.clone()); *ty = table.resolve_completely(ty.clone());
} }
*has_errors = !type_mismatches.is_empty();
type_mismatches.retain(|_, mismatch| { type_mismatches.retain(|_, mismatch| {
mismatch.expected = table.resolve_completely(mismatch.expected.clone()); mismatch.expected = table.resolve_completely(mismatch.expected.clone());
mismatch.actual = table.resolve_completely(mismatch.actual.clone()); mismatch.actual = table.resolve_completely(mismatch.actual.clone());
@ -1646,9 +1657,11 @@ impl std::ops::BitOrAssign for Diverges {
*self = *self | other; *self = *self | other;
} }
} }
/// A zipper that checks for unequal `{unknown}` occurrences in the two types. Used to filter out
/// mismatch diagnostics that only differ in `{unknown}`. These mismatches are usually not helpful. /// A zipper that checks for unequal occurrences of `{unknown}` and unresolved projections
/// As the cause is usually an underlying name resolution problem. /// in the two types. Used to filter out mismatch diagnostics that only differ in
/// `{unknown}` and unresolved projections. These mismatches are usually not helpful.
/// As the cause is usually an underlying name resolution problem
struct UnknownMismatch<'db>(&'db dyn HirDatabase); struct UnknownMismatch<'db>(&'db dyn HirDatabase);
impl chalk_ir::zip::Zipper<Interner> for UnknownMismatch<'_> { impl chalk_ir::zip::Zipper<Interner> for UnknownMismatch<'_> {
fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> chalk_ir::Fallible<()> { fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> chalk_ir::Fallible<()> {
@ -1721,7 +1734,12 @@ impl chalk_ir::zip::Zipper<Interner> for UnknownMismatch<'_> {
zip_substs(self, None, &fn_ptr_a.substitution.0, &fn_ptr_b.substitution.0)? zip_substs(self, None, &fn_ptr_a.substitution.0, &fn_ptr_b.substitution.0)?
} }
(TyKind::Error, TyKind::Error) => (), (TyKind::Error, TyKind::Error) => (),
(TyKind::Error, _) | (_, TyKind::Error) => return Err(chalk_ir::NoSolution), (TyKind::Error, _)
| (_, TyKind::Error)
| (TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _), _)
| (_, TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _)) => {
return Err(chalk_ir::NoSolution)
}
_ => (), _ => (),
} }

View file

@ -19,10 +19,6 @@ impl CastCheck {
let expr_ty = table.resolve_ty_shallow(&self.expr_ty); let expr_ty = table.resolve_ty_shallow(&self.expr_ty);
let cast_ty = table.resolve_ty_shallow(&self.cast_ty); let cast_ty = table.resolve_ty_shallow(&self.cast_ty);
if expr_ty.contains_unknown() || cast_ty.contains_unknown() {
return;
}
if table.coerce(&expr_ty, &cast_ty).is_ok() { if table.coerce(&expr_ty, &cast_ty).is_ok() {
return; return;
} }

View file

@ -22,11 +22,11 @@ use stdx::never;
use crate::{ use crate::{
db::{HirDatabase, InternedClosure}, db::{HirDatabase, InternedClosure},
from_chalk_trait_id, from_placeholder_idx, make_binders, error_lifetime, from_chalk_trait_id, from_placeholder_idx, make_binders,
mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
static_lifetime, to_chalk_trait_id, to_chalk_trait_id,
traits::FnTrait, traits::FnTrait,
utils::{self, elaborate_clause_supertraits, generics, Generics}, utils::{self, elaborate_clause_supertraits, Generics},
Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy,
DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTyExt, Substitution, Ty, DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTyExt, Substitution, Ty,
TyExt, WhereClause, TyExt, WhereClause,
@ -324,21 +324,17 @@ impl CapturedItemWithoutTy {
BorrowKind::Mut { .. } => Mutability::Mut, BorrowKind::Mut { .. } => Mutability::Mut,
_ => Mutability::Not, _ => Mutability::Not,
}; };
TyKind::Ref(m, static_lifetime(), ty).intern(Interner) TyKind::Ref(m, error_lifetime(), ty).intern(Interner)
} }
}; };
return CapturedItem { return CapturedItem {
place: self.place, place: self.place,
kind: self.kind, kind: self.kind,
span: self.span, span: self.span,
ty: replace_placeholder_with_binder(ctx.db, ctx.owner, ty), ty: replace_placeholder_with_binder(ctx, ty),
}; };
fn replace_placeholder_with_binder( fn replace_placeholder_with_binder(ctx: &mut InferenceContext<'_>, ty: Ty) -> Binders<Ty> {
db: &dyn HirDatabase,
owner: DefWithBodyId,
ty: Ty,
) -> Binders<Ty> {
struct Filler<'a> { struct Filler<'a> {
db: &'a dyn HirDatabase, db: &'a dyn HirDatabase,
generics: Generics, generics: Generics,
@ -361,7 +357,7 @@ impl CapturedItemWithoutTy {
outer_binder: DebruijnIndex, outer_binder: DebruijnIndex,
) -> Result<chalk_ir::Const<Interner>, Self::Error> { ) -> Result<chalk_ir::Const<Interner>, Self::Error> {
let x = from_placeholder_idx(self.db, idx); let x = from_placeholder_idx(self.db, idx);
let Some(idx) = self.generics.param_idx(x) else { let Some(idx) = self.generics.type_or_const_param_idx(x) else {
return Err(()); return Err(());
}; };
Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty)) Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty))
@ -373,18 +369,18 @@ impl CapturedItemWithoutTy {
outer_binder: DebruijnIndex, outer_binder: DebruijnIndex,
) -> std::result::Result<Ty, Self::Error> { ) -> std::result::Result<Ty, Self::Error> {
let x = from_placeholder_idx(self.db, idx); let x = from_placeholder_idx(self.db, idx);
let Some(idx) = self.generics.param_idx(x) else { let Some(idx) = self.generics.type_or_const_param_idx(x) else {
return Err(()); return Err(());
}; };
Ok(BoundVar::new(outer_binder, idx).to_ty(Interner)) Ok(BoundVar::new(outer_binder, idx).to_ty(Interner))
} }
} }
let Some(generic_def) = owner.as_generic_def_id() else { let Some(generics) = ctx.generics() else {
return Binders::empty(Interner, ty); return Binders::empty(Interner, ty);
}; };
let filler = &mut Filler { db, generics: generics(db.upcast(), generic_def) }; let filler = &mut Filler { db: ctx.db, generics };
let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty); let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty);
make_binders(db, &filler.generics, result) make_binders(ctx.db, &filler.generics, result)
} }
} }
} }

View file

@ -18,11 +18,11 @@ use triomphe::Arc;
use crate::{ use crate::{
autoderef::{Autoderef, AutoderefKind}, autoderef::{Autoderef, AutoderefKind},
db::HirDatabase, db::HirDatabase,
error_lifetime,
infer::{ infer::{
Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
TypeError, TypeMismatch, TypeError, TypeMismatch,
}, },
static_lifetime,
utils::ClosureSubst, utils::ClosureSubst,
Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution, Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
@ -427,7 +427,7 @@ impl InferenceTable<'_> {
// compare those. Note that this means we use the target // compare those. Note that this means we use the target
// mutability [1], since it may be that we are coercing // mutability [1], since it may be that we are coercing
// from `&mut T` to `&U`. // from `&mut T` to `&U`.
let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc let lt = error_lifetime(); // FIXME: handle lifetimes correctly, see rustc
let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(Interner); let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(Interner);
match autoderef.table.try_unify(&derefd_from_ty, to_ty) { match autoderef.table.try_unify(&derefd_from_ty, to_ty) {
Ok(result) => { Ok(result) => {
@ -621,7 +621,7 @@ impl InferenceTable<'_> {
(TyKind::Ref(from_mt, _, from_inner), &TyKind::Ref(to_mt, _, _)) => { (TyKind::Ref(from_mt, _, from_inner), &TyKind::Ref(to_mt, _, _)) => {
coerce_mutabilities(*from_mt, to_mt)?; coerce_mutabilities(*from_mt, to_mt)?;
let lt = static_lifetime(); let lt = error_lifetime();
Some(( Some((
Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() }, Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() },
Adjustment { Adjustment {

View file

@ -23,6 +23,7 @@ use crate::{
autoderef::{builtin_deref, deref_by_trait, Autoderef}, autoderef::{builtin_deref, deref_by_trait, Autoderef},
consteval, consteval,
db::{InternedClosure, InternedCoroutine}, db::{InternedClosure, InternedCoroutine},
error_lifetime,
infer::{ infer::{
coerce::{CoerceMany, CoercionCause}, coerce::{CoerceMany, CoercionCause},
find_continuable, find_continuable,
@ -630,7 +631,7 @@ impl InferenceContext<'_> {
let inner_ty = self.infer_expr_inner(*expr, &expectation); let inner_ty = self.infer_expr_inner(*expr, &expectation);
match rawness { match rawness {
Rawness::RawPtr => TyKind::Raw(mutability, inner_ty), Rawness::RawPtr => TyKind::Raw(mutability, inner_ty),
Rawness::Ref => TyKind::Ref(mutability, static_lifetime(), inner_ty), Rawness::Ref => TyKind::Ref(mutability, error_lifetime(), inner_ty),
} }
.intern(Interner) .intern(Interner)
} }
@ -1039,18 +1040,12 @@ impl InferenceContext<'_> {
( (
elem_ty, elem_ty,
if let Some(g_def) = self.owner.as_generic_def_id() { consteval::eval_to_const(
let generics = generics(self.db.upcast(), g_def); repeat,
consteval::eval_to_const( ParamLoweringMode::Placeholder,
repeat, self,
ParamLoweringMode::Placeholder, DebruijnIndex::INNERMOST,
self, ),
|| generics,
DebruijnIndex::INNERMOST,
)
} else {
consteval::usize_const(self.db, None, krate)
},
) )
} }
}; };
@ -1851,7 +1846,7 @@ impl InferenceContext<'_> {
ty, ty,
c, c,
ParamLoweringMode::Placeholder, ParamLoweringMode::Placeholder,
|| generics(this.db.upcast(), this.resolver.generic_def().unwrap()), || this.generics(),
DebruijnIndex::INNERMOST, DebruijnIndex::INNERMOST,
) )
}, },

View file

@ -2,21 +2,22 @@
use std::iter::repeat_with; use std::iter::repeat_with;
use chalk_ir::Mutability;
use hir_def::{ use hir_def::{
body::Body, body::Body,
hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId}, hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
path::Path, path::Path,
}; };
use hir_expand::name::Name; use hir_expand::name::Name;
use stdx::TupleExt;
use crate::{ use crate::{
consteval::{try_const_usize, usize_const}, consteval::{try_const_usize, usize_const},
error_lifetime,
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch}, infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
lower::lower_to_chalk_mutability, lower::lower_to_chalk_mutability,
primitive::UintTy, primitive::UintTy,
static_lifetime, InferenceDiagnostic, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
TyKind, TyBuilder, TyExt, TyKind,
}; };
/// Used to generalize patterns and assignee expressions. /// Used to generalize patterns and assignee expressions.
@ -89,9 +90,6 @@ impl InferenceContext<'_> {
self.unify(&ty, expected); self.unify(&ty, expected);
let substs =
ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
match def { match def {
_ if subs.is_empty() => {} _ if subs.is_empty() => {}
Some(def) => { Some(def) => {
@ -108,8 +106,10 @@ impl InferenceContext<'_> {
let pre_iter = pre.iter().enumerate(); let pre_iter = pre.iter().enumerate();
let post_iter = (post_idx_offset..).zip(post.iter()); let post_iter = (post_idx_offset..).zip(post.iter());
let substs = ty.as_adt().map(TupleExt::tail);
for (i, &subpat) in pre_iter.chain(post_iter) { for (i, &subpat) in pre_iter.chain(post_iter) {
let field_def = { let expected_ty = {
match variant_data.field(&Name::new_tuple_field(i)) { match variant_data.field(&Name::new_tuple_field(i)) {
Some(local_id) => { Some(local_id) => {
if !visibilities[local_id] if !visibilities[local_id]
@ -117,17 +117,17 @@ impl InferenceContext<'_> {
{ {
// FIXME(DIAGNOSE): private tuple field // FIXME(DIAGNOSE): private tuple field
} }
Some(local_id) let f = field_types[local_id].clone();
let expected_ty = match substs {
Some(substs) => f.substitute(Interner, substs),
None => f.substitute(Interner, &Substitution::empty(Interner)),
};
self.normalize_associated_types_in(expected_ty)
} }
None => None, None => self.err_ty(),
} }
}; };
let expected_ty = field_def.map_or(self.err_ty(), |f| {
field_types[f].clone().substitute(Interner, &substs)
});
let expected_ty = self.normalize_associated_types_in(expected_ty);
T::infer(self, subpat, &expected_ty, default_bm); T::infer(self, subpat, &expected_ty, default_bm);
} }
} }
@ -149,7 +149,7 @@ impl InferenceContext<'_> {
expected: &Ty, expected: &Ty,
default_bm: T::BindingMode, default_bm: T::BindingMode,
id: T, id: T,
subs: impl Iterator<Item = (Name, T)> + ExactSizeIterator, subs: impl ExactSizeIterator<Item = (Name, T)>,
) -> Ty { ) -> Ty {
let (ty, def) = self.resolve_variant(path, false); let (ty, def) = self.resolve_variant(path, false);
if let Some(variant) = def { if let Some(variant) = def {
@ -158,9 +158,6 @@ impl InferenceContext<'_> {
self.unify(&ty, expected); self.unify(&ty, expected);
let substs =
ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
match def { match def {
_ if subs.len() == 0 => {} _ if subs.len() == 0 => {}
Some(def) => { Some(def) => {
@ -168,8 +165,10 @@ impl InferenceContext<'_> {
let variant_data = def.variant_data(self.db.upcast()); let variant_data = def.variant_data(self.db.upcast());
let visibilities = self.db.field_visibilities(def); let visibilities = self.db.field_visibilities(def);
let substs = ty.as_adt().map(TupleExt::tail);
for (name, inner) in subs { for (name, inner) in subs {
let field_def = { let expected_ty = {
match variant_data.field(&name) { match variant_data.field(&name) {
Some(local_id) => { Some(local_id) => {
if !visibilities[local_id] if !visibilities[local_id]
@ -180,23 +179,23 @@ impl InferenceContext<'_> {
private: true, private: true,
}); });
} }
Some(local_id) let f = field_types[local_id].clone();
let expected_ty = match substs {
Some(substs) => f.substitute(Interner, substs),
None => f.substitute(Interner, &Substitution::empty(Interner)),
};
self.normalize_associated_types_in(expected_ty)
} }
None => { None => {
self.push_diagnostic(InferenceDiagnostic::NoSuchField { self.push_diagnostic(InferenceDiagnostic::NoSuchField {
field: inner.into(), field: inner.into(),
private: false, private: false,
}); });
None self.err_ty()
} }
} }
}; };
let expected_ty = field_def.map_or(self.err_ty(), |f| {
field_types[f].clone().substitute(Interner, &substs)
});
let expected_ty = self.normalize_associated_types_in(expected_ty);
T::infer(self, inner, &expected_ty, default_bm); T::infer(self, inner, &expected_ty, default_bm);
} }
} }
@ -396,14 +395,14 @@ impl InferenceContext<'_> {
None => { None => {
let inner_ty = self.table.new_type_var(); let inner_ty = self.table.new_type_var();
let ref_ty = let ref_ty =
TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner); TyKind::Ref(mutability, error_lifetime(), inner_ty.clone()).intern(Interner);
// Unification failure will be reported by the caller. // Unification failure will be reported by the caller.
self.unify(&ref_ty, expected); self.unify(&ref_ty, expected);
inner_ty inner_ty
} }
}; };
let subty = self.infer_pat(inner_pat, &expectation, default_bm); let subty = self.infer_pat(inner_pat, &expectation, default_bm);
TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner) TyKind::Ref(mutability, error_lifetime(), subty).intern(Interner)
} }
fn infer_bind_pat( fn infer_bind_pat(
@ -430,7 +429,7 @@ impl InferenceContext<'_> {
let bound_ty = match mode { let bound_ty = match mode {
BindingMode::Ref(mutability) => { BindingMode::Ref(mutability) => {
TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner) TyKind::Ref(mutability, error_lifetime(), inner_ty.clone()).intern(Interner)
} }
BindingMode::Move => inner_ty.clone(), BindingMode::Move => inner_ty.clone(),
}; };

View file

@ -16,8 +16,8 @@ use triomphe::Arc;
use super::{InferOk, InferResult, InferenceContext, TypeError}; use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{ use crate::{
consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, consteval::unknown_const, db::HirDatabase, error_lifetime, fold_generic_args,
static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, fold_tys_and_consts, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical,
Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData,
Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy,
ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
@ -43,40 +43,21 @@ impl InferenceContext<'_> {
let obligations = pending_obligations let obligations = pending_obligations
.iter() .iter()
.filter_map(|obligation| match obligation.value.value.goal.data(Interner) { .filter_map(|obligation| match obligation.value.value.goal.data(Interner) {
GoalData::DomainGoal(DomainGoal::Holds( GoalData::DomainGoal(DomainGoal::Holds(clause)) => {
clause @ WhereClause::AliasEq(AliasEq { let ty = match clause {
alias: AliasTy::Projection(projection), WhereClause::AliasEq(AliasEq {
.. alias: AliasTy::Projection(projection),
}), ..
)) => { }) => projection.self_type_parameter(self.db),
let projection_self = projection.self_type_parameter(self.db); WhereClause::Implemented(trait_ref) => {
let uncanonical = chalk_ir::Substitute::apply( trait_ref.self_type_parameter(Interner)
&obligation.free_vars, }
projection_self, WhereClause::TypeOutlives(to) => to.ty.clone(),
Interner, _ => return None,
); };
if matches!(
self.resolve_ty_shallow(&uncanonical).kind(Interner), let uncanonical =
TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root, chalk_ir::Substitute::apply(&obligation.free_vars, ty, Interner);
) {
Some(chalk_ir::Substitute::apply(
&obligation.free_vars,
clause.clone(),
Interner,
))
} else {
None
}
}
GoalData::DomainGoal(DomainGoal::Holds(
clause @ WhereClause::Implemented(trait_ref),
)) => {
let trait_ref_self = trait_ref.self_type_parameter(Interner);
let uncanonical = chalk_ir::Substitute::apply(
&obligation.free_vars,
trait_ref_self,
Interner,
);
if matches!( if matches!(
self.resolve_ty_shallow(&uncanonical).kind(Interner), self.resolve_ty_shallow(&uncanonical).kind(Interner),
TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root, TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root,
@ -121,8 +102,9 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(Interner), VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(Interner),
VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(Interner), VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(Interner),
VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(Interner), VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(Interner),
// Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere // Chalk can sometimes return new lifetime variables. We just replace them by errors
VariableKind::Lifetime => static_lifetime().cast(Interner), // for now.
VariableKind::Lifetime => error_lifetime().cast(Interner),
VariableKind::Const(ty) => ctx.new_const_var(ty.clone()).cast(Interner), VariableKind::Const(ty) => ctx.new_const_var(ty.clone()).cast(Interner),
}), }),
); );
@ -1020,11 +1002,11 @@ mod resolve {
_var: InferenceVar, _var: InferenceVar,
_outer_binder: DebruijnIndex, _outer_binder: DebruijnIndex,
) -> Lifetime { ) -> Lifetime {
// fall back all lifetimes to 'static -- currently we don't deal // fall back all lifetimes to 'error -- currently we don't deal
// with any lifetimes, but we can sometimes get some lifetime // with any lifetimes, but we can sometimes get some lifetime
// variables through Chalk's unification, and this at least makes // variables through Chalk's unification, and this at least makes
// sure we don't leak them outside of inference // sure we don't leak them outside of inference
crate::static_lifetime() crate::error_lifetime()
} }
} }
} }

View file

@ -5,42 +5,36 @@ use chalk_ir::{
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
DebruijnIndex, DebruijnIndex,
}; };
use hir_def::{ use hir_def::{visibility::Visibility, AdtId, EnumVariantId, HasModule, ModuleId, VariantId};
attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule,
ModuleId, VariantId,
};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use crate::{ use crate::{
consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind, consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
}; };
// FIXME: Turn this into a query, it can be quite slow
/// Checks whether a type is visibly uninhabited from a particular module. /// Checks whether a type is visibly uninhabited from a particular module.
pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool { pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool {
let _p = tracing::span!(tracing::Level::INFO, "is_ty_uninhabited_from", ?ty).entered();
let mut uninhabited_from = let mut uninhabited_from =
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() }; UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST); let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
inhabitedness == BREAK_VISIBLY_UNINHABITED inhabitedness == BREAK_VISIBLY_UNINHABITED
} }
// FIXME: Turn this into a query, it can be quite slow
/// Checks whether a variant is visibly uninhabited from a particular module. /// Checks whether a variant is visibly uninhabited from a particular module.
pub(crate) fn is_enum_variant_uninhabited_from( pub(crate) fn is_enum_variant_uninhabited_from(
db: &dyn HirDatabase,
variant: EnumVariantId, variant: EnumVariantId,
subst: &Substitution, subst: &Substitution,
target_mod: ModuleId, target_mod: ModuleId,
db: &dyn HirDatabase,
) -> bool { ) -> bool {
let is_local = variant.module(db.upcast()).krate() == target_mod.krate(); let _p = tracing::span!(tracing::Level::INFO, "is_enum_variant_uninhabited_from").entered();
let mut uninhabited_from = let mut uninhabited_from =
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() }; UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
let inhabitedness = uninhabited_from.visit_variant( let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst);
variant.into(),
&db.enum_variant_data(variant).variant_data,
subst,
&db.attrs(variant.into()),
is_local,
);
inhabitedness == BREAK_VISIBLY_UNINHABITED inhabitedness == BREAK_VISIBLY_UNINHABITED
} }
@ -98,34 +92,18 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
impl UninhabitedFrom<'_> { impl UninhabitedFrom<'_> {
fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> { fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> {
let attrs = self.db.attrs(adt.into());
let adt_non_exhaustive = attrs.by_key("non_exhaustive").exists();
let is_local = adt.module(self.db.upcast()).krate() == self.target_mod.krate();
if adt_non_exhaustive && !is_local {
return CONTINUE_OPAQUELY_INHABITED;
}
// An ADT is uninhabited iff all its variants uninhabited. // An ADT is uninhabited iff all its variants uninhabited.
match adt { match adt {
// rustc: For now, `union`s are never considered uninhabited. // rustc: For now, `union`s are never considered uninhabited.
AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED, AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
AdtId::StructId(s) => { AdtId::StructId(s) => self.visit_variant(s.into(), subst),
let struct_data = self.db.struct_data(s);
self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local)
}
AdtId::EnumId(e) => { AdtId::EnumId(e) => {
let enum_data = self.db.enum_data(e); let enum_data = self.db.enum_data(e);
for &(variant, _) in enum_data.variants.iter() { for &(variant, _) in enum_data.variants.iter() {
let variant_inhabitedness = self.visit_variant( let variant_inhabitedness = self.visit_variant(variant.into(), subst);
variant.into(),
&self.db.enum_variant_data(variant).variant_data,
subst,
&self.db.attrs(variant.into()),
is_local,
);
match variant_inhabitedness { match variant_inhabitedness {
Break(VisiblyUninhabited) => continue, Break(VisiblyUninhabited) => (),
Continue(()) => return CONTINUE_OPAQUELY_INHABITED, Continue(()) => return CONTINUE_OPAQUELY_INHABITED,
} }
} }
@ -137,34 +115,36 @@ impl UninhabitedFrom<'_> {
fn visit_variant( fn visit_variant(
&mut self, &mut self,
variant: VariantId, variant: VariantId,
variant_data: &VariantData,
subst: &Substitution, subst: &Substitution,
attrs: &Attrs,
is_local: bool,
) -> ControlFlow<VisiblyUninhabited> { ) -> ControlFlow<VisiblyUninhabited> {
let non_exhaustive_field_list = attrs.by_key("non_exhaustive").exists(); let is_local = variant.krate(self.db.upcast()) == self.target_mod.krate();
if non_exhaustive_field_list && !is_local { if !is_local && self.db.attrs(variant.into()).by_key("non_exhaustive").exists() {
return CONTINUE_OPAQUELY_INHABITED;
}
let variant_data = self.db.variant_data(variant);
let fields = variant_data.fields();
if fields.is_empty() {
return CONTINUE_OPAQUELY_INHABITED; return CONTINUE_OPAQUELY_INHABITED;
} }
let is_enum = matches!(variant, VariantId::EnumVariantId(..)); let is_enum = matches!(variant, VariantId::EnumVariantId(..));
let field_tys = self.db.field_types(variant); let field_tys = self.db.field_types(variant);
let field_vis = self.db.field_visibilities(variant); let field_vis = if is_enum { None } else { Some(self.db.field_visibilities(variant)) };
for (fid, _) in variant_data.fields().iter() { for (fid, _) in fields.iter() {
self.visit_field(field_vis[fid], &field_tys[fid], subst, is_enum)?; self.visit_field(field_vis.as_ref().map(|it| it[fid]), &field_tys[fid], subst)?;
} }
CONTINUE_OPAQUELY_INHABITED CONTINUE_OPAQUELY_INHABITED
} }
fn visit_field( fn visit_field(
&mut self, &mut self,
vis: Visibility, vis: Option<Visibility>,
ty: &Binders<Ty>, ty: &Binders<Ty>,
subst: &Substitution, subst: &Substitution,
is_enum: bool,
) -> ControlFlow<VisiblyUninhabited> { ) -> ControlFlow<VisiblyUninhabited> {
if is_enum || vis.is_visible_from(self.db.upcast(), self.target_mod) { if vis.map_or(true, |it| it.is_visible_from(self.db.upcast(), self.target_mod)) {
let ty = ty.clone().substitute(Interner, subst); let ty = ty.clone().substitute(Interner, subst);
ty.visit_with(self, DebruijnIndex::INNERMOST) ty.visit_with(self, DebruijnIndex::INNERMOST)
} else { } else {

View file

@ -56,7 +56,6 @@ use base_db::salsa::impl_intern_value_trivial;
use chalk_ir::{ use chalk_ir::{
fold::{Shift, TypeFoldable}, fold::{Shift, TypeFoldable},
interner::HasInterner, interner::HasInterner,
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
NoSolution, NoSolution,
}; };
use either::Either; use either::Either;
@ -98,7 +97,9 @@ pub use traits::TraitEnvironment;
pub use utils::{all_super_traits, is_fn_unsafe_to_call}; pub use utils::{all_super_traits, is_fn_unsafe_to_call};
pub use chalk_ir::{ pub use chalk_ir::{
cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind, cast::Cast,
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
}; };
pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>; pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>;
@ -288,7 +289,7 @@ impl Hash for ConstScalar {
/// Return an index of a parameter in the generic type parameter list by it's id. /// Return an index of a parameter in the generic type parameter list by it's id.
pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> { pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
generics(db.upcast(), id.parent).param_idx(id) generics(db.upcast(), id.parent).type_or_const_param_idx(id)
} }
pub(crate) fn wrap_empty_binders<T>(value: T) -> Binders<T> pub(crate) fn wrap_empty_binders<T>(value: T) -> Binders<T>
@ -603,14 +604,14 @@ pub enum ImplTraitId {
} }
impl_intern_value_trivial!(ImplTraitId); impl_intern_value_trivial!(ImplTraitId);
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(PartialEq, Eq, Debug, Hash)]
pub struct ImplTraits { pub struct ImplTraits {
pub(crate) impl_traits: Arena<ImplTrait>, pub(crate) impl_traits: Arena<ImplTrait>,
} }
has_interner!(ImplTraits); has_interner!(ImplTraits);
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(PartialEq, Eq, Debug, Hash)]
pub struct ImplTrait { pub struct ImplTrait {
pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>, pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>,
} }
@ -622,7 +623,7 @@ pub fn static_lifetime() -> Lifetime {
} }
pub fn error_lifetime() -> Lifetime { pub fn error_lifetime() -> Lifetime {
static_lifetime() LifetimeData::Error.intern(Interner)
} }
pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>( pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
@ -861,7 +862,7 @@ where
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
Err(NoSolution) Err(NoSolution)
} else { } else {
Ok(static_lifetime()) Ok(error_lifetime())
} }
} }
@ -873,7 +874,7 @@ where
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
Err(NoSolution) Err(NoSolution)
} else { } else {
Ok(static_lifetime()) Ok(error_lifetime())
} }
} }
} }

View file

@ -15,7 +15,10 @@ use base_db::{
CrateId, CrateId,
}; };
use chalk_ir::{ use chalk_ir::{
cast::Cast, fold::Shift, fold::TypeFoldable, interner::HasInterner, Mutability, Safety, cast::Cast,
fold::{Shift, TypeFoldable},
interner::HasInterner,
Mutability, Safety, TypeOutlives,
}; };
use either::Either; use either::Either;
@ -59,14 +62,14 @@ use crate::{
mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk}, mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
utils::{ utils::{
all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics, self, all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
InTypeConstIdMetadata, Generics, InTypeConstIdMetadata,
}, },
AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
LifetimeData, ParamKind, PolyFnSig, ProjectionTy, QuantifiedWhereClause, LifetimeData, LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, ProjectionTy,
QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef,
TyKind, WhereClause, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -242,13 +245,8 @@ impl<'a> TyLoweringContext<'a> {
) )
} }
fn generics(&self) -> Generics { fn generics(&self) -> Option<Generics> {
generics( Some(generics(self.db.upcast(), self.resolver.generic_def()?))
self.db.upcast(),
self.resolver
.generic_def()
.expect("there should be generics if there's a generic param"),
)
} }
pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) { pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
@ -282,7 +280,7 @@ impl<'a> TyLoweringContext<'a> {
let inner_ty = self.lower_ty(inner); let inner_ty = self.lower_ty(inner);
// FIXME: It should infer the eldided lifetimes instead of stubbing with static // FIXME: It should infer the eldided lifetimes instead of stubbing with static
let lifetime = let lifetime =
lifetime.as_ref().map_or_else(static_lifetime, |lr| self.lower_lifetime(lr)); lifetime.as_ref().map_or_else(error_lifetime, |lr| self.lower_lifetime(lr));
TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty) TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty)
.intern(Interner) .intern(Interner)
} }
@ -318,7 +316,7 @@ impl<'a> TyLoweringContext<'a> {
// place even if we encounter more opaque types while // place even if we encounter more opaque types while
// lowering the bounds // lowering the bounds
let idx = opaque_type_data.borrow_mut().alloc(ImplTrait { let idx = opaque_type_data.borrow_mut().alloc(ImplTrait {
bounds: crate::make_single_type_binders(Vec::new()), bounds: crate::make_single_type_binders(Vec::default()),
}); });
// We don't want to lower the bounds inside the binders // We don't want to lower the bounds inside the binders
// we're currently in, because they don't end up inside // we're currently in, because they don't end up inside
@ -349,8 +347,7 @@ impl<'a> TyLoweringContext<'a> {
let idx = counter.get(); let idx = counter.get();
// FIXME we're probably doing something wrong here // FIXME we're probably doing something wrong here
counter.set(idx + count_impl_traits(type_ref) as u16); counter.set(idx + count_impl_traits(type_ref) as u16);
if let Some(def) = self.resolver.generic_def() { if let Some(generics) = self.generics() {
let generics = generics(self.db.upcast(), def);
let param = generics let param = generics
.iter() .iter()
.filter(|(_, data)| { .filter(|(_, data)| {
@ -385,8 +382,7 @@ impl<'a> TyLoweringContext<'a> {
const_params, const_params,
_impl_trait_params, _impl_trait_params,
_lifetime_params, _lifetime_params,
) = if let Some(def) = self.resolver.generic_def() { ) = if let Some(generics) = self.generics() {
let generics = generics(self.db.upcast(), def);
generics.provenance_split() generics.provenance_split()
} else { } else {
(0, 0, 0, 0, 0, 0) (0, 0, 0, 0, 0, 0)
@ -574,44 +570,40 @@ impl<'a> TyLoweringContext<'a> {
// FIXME(trait_alias): Implement trait alias. // FIXME(trait_alias): Implement trait alias.
return (TyKind::Error.intern(Interner), None); return (TyKind::Error.intern(Interner), None);
} }
TypeNs::GenericParam(param_id) => { TypeNs::GenericParam(param_id) => match self.type_param_mode {
let generics = generics( ParamLoweringMode::Placeholder => {
self.db.upcast(), TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
self.resolver.generic_def().expect("generics in scope"), }
); ParamLoweringMode::Variable => {
match self.type_param_mode { let idx = match self
ParamLoweringMode::Placeholder => { .generics()
TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into())) .expect("generics in scope")
} .type_or_const_param_idx(param_id.into())
ParamLoweringMode::Variable => { {
let idx = match generics.param_idx(param_id.into()) { None => {
None => { never!("no matching generics");
never!("no matching generics"); return (TyKind::Error.intern(Interner), None);
return (TyKind::Error.intern(Interner), None); }
} Some(idx) => idx,
Some(idx) => idx, };
};
TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
}
} }
.intern(Interner)
} }
.intern(Interner),
TypeNs::SelfType(impl_id) => { TypeNs::SelfType(impl_id) => {
let def = let generics = self.generics().expect("impl should have generic param scope");
self.resolver.generic_def().expect("impl should have generic param scope");
let generics = generics(self.db.upcast(), def);
match self.type_param_mode { match self.type_param_mode {
ParamLoweringMode::Placeholder => { ParamLoweringMode::Placeholder => {
// `def` can be either impl itself or item within, and we need impl itself // `def` can be either impl itself or item within, and we need impl itself
// now. // now.
let generics = generics.parent_generics().unwrap_or(&generics); let generics = generics.parent_or_self();
let subst = generics.placeholder_subst(self.db); let subst = generics.placeholder_subst(self.db);
self.db.impl_self_ty(impl_id).substitute(Interner, &subst) self.db.impl_self_ty(impl_id).substitute(Interner, &subst)
} }
ParamLoweringMode::Variable => { ParamLoweringMode::Variable => {
let starting_from = match def { let starting_from = match generics.def() {
GenericDefId::ImplId(_) => 0, GenericDefId::ImplId(_) => 0,
// `def` is an item within impl. We need to substitute `BoundVar`s but // `def` is an item within impl. We need to substitute `BoundVar`s but
// remember that they are for parent (i.e. impl) generic params so they // remember that they are for parent (i.e. impl) generic params so they
@ -679,12 +671,12 @@ impl<'a> TyLoweringContext<'a> {
} }
fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty { fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
let Some((def, res)) = self.resolver.generic_def().zip(res) else { let Some((generics, res)) = self.generics().zip(res) else {
return TyKind::Error.intern(Interner); return TyKind::Error.intern(Interner);
}; };
let ty = named_associated_type_shorthand_candidates( let ty = named_associated_type_shorthand_candidates(
self.db, self.db,
def, generics.def(),
res, res,
Some(segment.name.clone()), Some(segment.name.clone()),
move |name, t, associated_ty| { move |name, t, associated_ty| {
@ -696,7 +688,6 @@ impl<'a> TyLoweringContext<'a> {
let parent_subst = match self.type_param_mode { let parent_subst = match self.type_param_mode {
ParamLoweringMode::Placeholder => { ParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put them in now. // if we're lowering to placeholders, we have to put them in now.
let generics = generics(self.db.upcast(), def);
let s = generics.placeholder_subst(self.db); let s = generics.placeholder_subst(self.db);
s.apply(parent_subst, Interner) s.apply(parent_subst, Interner)
} }
@ -718,7 +709,7 @@ impl<'a> TyLoweringContext<'a> {
None, None,
); );
let len_self = generics(self.db.upcast(), associated_ty.into()).len_self(); let len_self = utils::generics(self.db.upcast(), associated_ty.into()).len_self();
let substs = Substitution::from_iter( let substs = Substitution::from_iter(
Interner, Interner,
@ -1016,40 +1007,43 @@ impl<'a> TyLoweringContext<'a> {
self.substs_from_path_segment(segment, Some(resolved.into()), false, explicit_self_ty) self.substs_from_path_segment(segment, Some(resolved.into()), false, explicit_self_ty)
} }
pub(crate) fn lower_where_predicate( pub(crate) fn lower_where_predicate<'b>(
&self, &'b self,
where_predicate: &WherePredicate, where_predicate: &'b WherePredicate,
ignore_bindings: bool, ignore_bindings: bool,
) -> impl Iterator<Item = QuantifiedWhereClause> { ) -> impl Iterator<Item = QuantifiedWhereClause> + 'b {
match where_predicate { match where_predicate {
WherePredicate::ForLifetime { target, bound, .. } WherePredicate::ForLifetime { target, bound, .. }
| WherePredicate::TypeBound { target, bound } => { | WherePredicate::TypeBound { target, bound } => {
let self_ty = match target { let self_ty = match target {
WherePredicateTypeTarget::TypeRef(type_ref) => self.lower_ty(type_ref), WherePredicateTypeTarget::TypeRef(type_ref) => self.lower_ty(type_ref),
WherePredicateTypeTarget::TypeOrConstParam(param_id) => { &WherePredicateTypeTarget::TypeOrConstParam(local_id) => {
let generic_def = self.resolver.generic_def().expect("generics in scope"); let def = self.resolver.generic_def().expect("generics in scope");
let generics = generics(self.db.upcast(), generic_def); let param_id = hir_def::TypeOrConstParamId { parent: def, local_id };
let param_id = hir_def::TypeOrConstParamId {
parent: generic_def,
local_id: *param_id,
};
let placeholder = to_placeholder_idx(self.db, param_id);
match self.type_param_mode { match self.type_param_mode {
ParamLoweringMode::Placeholder => TyKind::Placeholder(placeholder), ParamLoweringMode::Placeholder => {
TyKind::Placeholder(to_placeholder_idx(self.db, param_id))
}
ParamLoweringMode::Variable => { ParamLoweringMode::Variable => {
let idx = generics.param_idx(param_id).expect("matching generics"); let idx = generics(self.db.upcast(), def)
.type_or_const_param_idx(param_id)
.expect("matching generics");
TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, idx)) TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, idx))
} }
} }
.intern(Interner) .intern(Interner)
} }
}; };
self.lower_type_bound(bound, self_ty, ignore_bindings) Either::Left(self.lower_type_bound(bound, self_ty, ignore_bindings))
.collect::<Vec<_>>()
.into_iter()
} }
WherePredicate::Lifetime { .. } => vec![].into_iter(), WherePredicate::Lifetime { bound, target } => Either::Right(iter::once(
crate::wrap_empty_binders(WhereClause::LifetimeOutlives(LifetimeOutlives {
a: self.lower_lifetime(bound),
b: self.lower_lifetime(target),
})),
)),
} }
.into_iter()
} }
pub(crate) fn lower_type_bound( pub(crate) fn lower_type_bound(
@ -1058,11 +1052,11 @@ impl<'a> TyLoweringContext<'a> {
self_ty: Ty, self_ty: Ty,
ignore_bindings: bool, ignore_bindings: bool,
) -> impl Iterator<Item = QuantifiedWhereClause> + 'a { ) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
let mut bindings = None; let mut trait_ref = None;
let trait_ref = match bound.as_ref() { let clause = match bound.as_ref() {
TypeBound::Path(path, TraitBoundModifier::None) => { TypeBound::Path(path, TraitBoundModifier::None) => {
bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty));
bindings trait_ref
.clone() .clone()
.filter(|tr| { .filter(|tr| {
// ignore `T: Drop` or `T: Destruct` bounds. // ignore `T: Drop` or `T: Destruct` bounds.
@ -1098,14 +1092,20 @@ impl<'a> TyLoweringContext<'a> {
} }
TypeBound::ForLifetime(_, path) => { TypeBound::ForLifetime(_, path) => {
// FIXME Don't silently drop the hrtb lifetimes here // FIXME Don't silently drop the hrtb lifetimes here
bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty));
bindings.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
}
TypeBound::Lifetime(l) => {
let lifetime = self.lower_lifetime(l);
Some(crate::wrap_empty_binders(WhereClause::TypeOutlives(TypeOutlives {
ty: self_ty,
lifetime,
})))
} }
TypeBound::Lifetime(_) => None,
TypeBound::Error => None, TypeBound::Error => None,
}; };
trait_ref.into_iter().chain( clause.into_iter().chain(
bindings trait_ref
.into_iter() .into_iter()
.filter(move |_| !ignore_bindings) .filter(move |_| !ignore_bindings)
.flat_map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)), .flat_map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)),
@ -1203,8 +1203,8 @@ impl<'a> TyLoweringContext<'a> {
}); });
if let Some(target_param_idx) = target_param_idx { if let Some(target_param_idx) = target_param_idx {
let mut counter = 0; let mut counter = 0;
for (idx, data) in self.generics().params.type_or_consts.iter() let generics = self.generics().expect("generics in scope");
{ for (idx, data) in generics.params.type_or_consts.iter() {
// Count the number of `impl Trait` things that appear before // Count the number of `impl Trait` things that appear before
// the target of our `bound`. // the target of our `bound`.
// Our counter within `impl_trait_mode` should be that number // Our counter within `impl_trait_mode` should be that number
@ -1264,10 +1264,19 @@ impl<'a> TyLoweringContext<'a> {
// bounds in the input. // bounds in the input.
// INVARIANT: If this function returns `DynTy`, there should be at least one trait bound. // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound.
// These invariants are utilized by `TyExt::dyn_trait()` and chalk. // These invariants are utilized by `TyExt::dyn_trait()` and chalk.
let mut lifetime = None;
let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
let mut bounds: Vec<_> = bounds let mut bounds: Vec<_> = bounds
.iter() .iter()
.flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)) .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
.filter(|b| match b.skip_binders() {
WhereClause::Implemented(_) | WhereClause::AliasEq(_) => true,
WhereClause::LifetimeOutlives(_) => false,
WhereClause::TypeOutlives(t) => {
lifetime = Some(t.lifetime.clone());
false
}
})
.collect(); .collect();
let mut multiple_regular_traits = false; let mut multiple_regular_traits = false;
@ -1305,7 +1314,7 @@ impl<'a> TyLoweringContext<'a> {
_ => unreachable!(), _ => unreachable!(),
} }
} }
// We don't produce `WhereClause::{TypeOutlives, LifetimeOutlives}` yet. // `WhereClause::{TypeOutlives, LifetimeOutlives}` have been filtered out
_ => unreachable!(), _ => unreachable!(),
} }
}); });
@ -1325,7 +1334,21 @@ impl<'a> TyLoweringContext<'a> {
if let Some(bounds) = bounds { if let Some(bounds) = bounds {
let bounds = crate::make_single_type_binders(bounds); let bounds = crate::make_single_type_binders(bounds);
TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner) TyKind::Dyn(DynTy {
bounds,
lifetime: match lifetime {
Some(it) => match it.bound_var(Interner) {
Some(bound_var) => LifetimeData::BoundVar(BoundVar::new(
DebruijnIndex::INNERMOST,
bound_var.index,
))
.intern(Interner),
None => it,
},
None => static_lifetime(),
},
})
.intern(Interner)
} else { } else {
// FIXME: report error // FIXME: report error
// (additional non-auto traits, associated type rebound, or no resolved trait) // (additional non-auto traits, associated type rebound, or no resolved trait)
@ -1355,8 +1378,8 @@ impl<'a> TyLoweringContext<'a> {
crate::wrap_empty_binders(clause) crate::wrap_empty_binders(clause)
}); });
predicates.extend(sized_clause); predicates.extend(sized_clause);
predicates.shrink_to_fit();
} }
predicates.shrink_to_fit();
predicates predicates
}); });
ImplTrait { bounds: crate::make_single_type_binders(predicates) } ImplTrait { bounds: crate::make_single_type_binders(predicates) }
@ -1371,10 +1394,7 @@ impl<'a> TyLoweringContext<'a> {
LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id)) LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id))
} }
ParamLoweringMode::Variable => { ParamLoweringMode::Variable => {
let generics = generics( let generics = self.generics().expect("generics in scope");
self.db.upcast(),
self.resolver.generic_def().expect("generics in scope"),
);
let idx = match generics.lifetime_idx(id) { let idx = match generics.lifetime_idx(id) {
None => return error_lifetime(), None => return error_lifetime(),
Some(idx) => idx, Some(idx) => idx,
@ -1485,7 +1505,7 @@ fn named_associated_type_shorthand_candidates<R>(
// Handle `Self::Type` referring to own associated type in trait definitions // Handle `Self::Type` referring to own associated type in trait definitions
if let GenericDefId::TraitId(trait_id) = param_id.parent() { if let GenericDefId::TraitId(trait_id) = param_id.parent() {
let trait_generics = generics(db.upcast(), trait_id.into()); let trait_generics = generics(db.upcast(), trait_id.into());
if trait_generics.params.type_or_consts[param_id.local_id()].is_trait_self() { if trait_generics.params[param_id.local_id()].is_trait_self() {
let def_generics = generics(db.upcast(), def); let def_generics = generics(db.upcast(), def);
let starting_idx = match def { let starting_idx = match def {
GenericDefId::TraitId(_) => 0, GenericDefId::TraitId(_) => 0,
@ -1604,10 +1624,14 @@ pub(crate) fn generic_predicates_for_param_query(
let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
let explicitly_unsized_tys = ctx.unsized_types.into_inner(); let explicitly_unsized_tys = ctx.unsized_types.into_inner();
let implicitly_sized_predicates = if let Some(implicitly_sized_predicates) =
implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &subst, &resolver) implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &subst, &resolver)
.map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))); {
predicates.extend(implicitly_sized_predicates); predicates.extend(
implicitly_sized_predicates
.map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))),
);
}
predicates.into() predicates.into()
} }
@ -1657,18 +1681,7 @@ pub(crate) fn trait_environment_query(
} }
} }
let container: Option<ItemContainerId> = match def { if let Some(trait_id) = def.assoc_trait_container(db.upcast()) {
// FIXME: is there a function for this?
GenericDefId::FunctionId(f) => Some(f.lookup(db.upcast()).container),
GenericDefId::AdtId(_) => None,
GenericDefId::TraitId(_) => None,
GenericDefId::TraitAliasId(_) => None,
GenericDefId::TypeAliasId(t) => Some(t.lookup(db.upcast()).container),
GenericDefId::ImplId(_) => None,
GenericDefId::EnumVariantId(_) => None,
GenericDefId::ConstId(c) => Some(c.lookup(db.upcast()).container),
};
if let Some(ItemContainerId::TraitId(trait_id)) = container {
// add `Self: Trait<T1, T2, ...>` to the environment in trait // add `Self: Trait<T1, T2, ...>` to the environment in trait
// function default implementations (and speculative code // function default implementations (and speculative code
// inside consts or type aliases) // inside consts or type aliases)
@ -1676,24 +1689,23 @@ pub(crate) fn trait_environment_query(
let substs = TyBuilder::placeholder_subst(db, trait_id); let substs = TyBuilder::placeholder_subst(db, trait_id);
let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs }; let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs };
let pred = WhereClause::Implemented(trait_ref); let pred = WhereClause::Implemented(trait_ref);
let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner); clauses.push(pred.cast::<ProgramClause>(Interner).into_from_env_clause(Interner));
clauses.push(program_clause.into_from_env_clause(Interner));
} }
let subst = generics(db.upcast(), def).placeholder_subst(db); let subst = generics(db.upcast(), def).placeholder_subst(db);
let explicitly_unsized_tys = ctx.unsized_types.into_inner(); let explicitly_unsized_tys = ctx.unsized_types.into_inner();
let implicitly_sized_clauses = if let Some(implicitly_sized_clauses) =
implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver).map(|pred| { implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner); {
program_clause.into_from_env_clause(Interner) clauses.extend(
}); implicitly_sized_clauses
clauses.extend(implicitly_sized_clauses); .map(|pred| pred.cast::<ProgramClause>(Interner).into_from_env_clause(Interner)),
);
let krate = def.module(db.upcast()).krate(); }
let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
TraitEnvironment::new(krate, None, traits_in_scope.into_boxed_slice(), env) TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env)
} }
/// Resolve the where clause(s) of an item with generics. /// Resolve the where clause(s) of an item with generics.
@ -1721,10 +1733,14 @@ pub(crate) fn generic_predicates_query(
let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
let explicitly_unsized_tys = ctx.unsized_types.into_inner(); let explicitly_unsized_tys = ctx.unsized_types.into_inner();
let implicitly_sized_predicates = if let Some(implicitly_sized_predicates) =
implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
.map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))); {
predicates.extend(implicitly_sized_predicates); predicates.extend(
implicitly_sized_predicates
.map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))),
);
}
predicates.into() predicates.into()
} }
@ -1736,24 +1752,24 @@ fn implicitly_sized_clauses<'a>(
explicitly_unsized_tys: &'a FxHashSet<Ty>, explicitly_unsized_tys: &'a FxHashSet<Ty>,
substitution: &'a Substitution, substitution: &'a Substitution,
resolver: &Resolver, resolver: &Resolver,
) -> impl Iterator<Item = WhereClause> + 'a { ) -> Option<impl Iterator<Item = WhereClause> + 'a> {
let is_trait_def = matches!(def, GenericDefId::TraitId(..)); let is_trait_def = matches!(def, GenericDefId::TraitId(..));
let generic_args = &substitution.as_slice(Interner)[is_trait_def as usize..]; let generic_args = &substitution.as_slice(Interner)[is_trait_def as usize..];
let sized_trait = db let sized_trait = db
.lang_item(resolver.krate(), LangItem::Sized) .lang_item(resolver.krate(), LangItem::Sized)
.and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id)); .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id));
sized_trait.into_iter().flat_map(move |sized_trait| { sized_trait.map(move |sized_trait| {
let implicitly_sized_tys = generic_args generic_args
.iter() .iter()
.filter_map(|generic_arg| generic_arg.ty(Interner)) .filter_map(|generic_arg| generic_arg.ty(Interner))
.filter(move |&self_ty| !explicitly_unsized_tys.contains(self_ty)); .filter(move |&self_ty| !explicitly_unsized_tys.contains(self_ty))
implicitly_sized_tys.map(move |self_ty| { .map(move |self_ty| {
WhereClause::Implemented(TraitRef { WhereClause::Implemented(TraitRef {
trait_id: sized_trait, trait_id: sized_trait,
substitution: Substitution::from1(Interner, self_ty.clone()), substitution: Substitution::from1(Interner, self_ty.clone()),
})
}) })
})
}) })
} }
@ -1796,8 +1812,7 @@ pub(crate) fn generic_defaults_query(
make_binders(db, &generic_params, val) make_binders(db, &generic_params, val)
} }
GenericParamDataRef::LifetimeParamData(_) => { GenericParamDataRef::LifetimeParamData(_) => {
// using static because it requires defaults make_binders(db, &generic_params, error_lifetime().cast(Interner))
make_binders(db, &generic_params, static_lifetime().cast(Interner))
} }
} }
})); }));
@ -1817,7 +1832,7 @@ pub(crate) fn generic_defaults_recover(
let val = match id { let val = match id {
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)), GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)),
GenericParamId::LifetimeParamId(_) => static_lifetime().cast(Interner), GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
}; };
crate::make_binders(db, &generic_params, val) crate::make_binders(db, &generic_params, val)
})); }));
@ -2232,7 +2247,7 @@ pub(crate) fn const_or_path_to_chalk(
expected_ty: Ty, expected_ty: Ty,
value: &ConstRef, value: &ConstRef,
mode: ParamLoweringMode, mode: ParamLoweringMode,
args: impl FnOnce() -> Generics, args: impl FnOnce() -> Option<Generics>,
debruijn: DebruijnIndex, debruijn: DebruijnIndex,
) -> Const { ) -> Const {
match value { match value {
@ -2251,7 +2266,7 @@ pub(crate) fn const_or_path_to_chalk(
.unwrap_or_else(|| unknown_const(expected_ty)) .unwrap_or_else(|| unknown_const(expected_ty))
} }
&ConstRef::Complex(it) => { &ConstRef::Complex(it) => {
let crate_data = &db.crate_graph()[owner.module(db.upcast()).krate()]; let crate_data = &db.crate_graph()[resolver.krate()];
if crate_data.env.get("__ra_is_test_fixture").is_none() && crate_data.origin.is_local() if crate_data.env.get("__ra_is_test_fixture").is_none() && crate_data.origin.is_local()
{ {
// FIXME: current `InTypeConstId` is very unstable, so we only use it in non local crate // FIXME: current `InTypeConstId` is very unstable, so we only use it in non local crate

View file

@ -22,10 +22,10 @@ use triomphe::Arc;
use crate::{ use crate::{
autoderef::{self, AutoderefKind}, autoderef::{self, AutoderefKind},
db::HirDatabase, db::HirDatabase,
from_chalk_trait_id, from_foreign_def_id, error_lifetime, from_chalk_trait_id, from_foreign_def_id,
infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast}, infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast},
primitive::{FloatTy, IntTy, UintTy}, primitive::{FloatTy, IntTy, UintTy},
static_lifetime, to_chalk_trait_id, to_chalk_trait_id,
utils::all_super_traits, utils::all_super_traits,
AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, Goal, Guidance, AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, Goal, Guidance,
InEnvironment, Interner, Scalar, Solution, Substitution, TraitEnvironment, TraitRef, InEnvironment, Interner, Scalar, Solution, Substitution, TraitEnvironment, TraitRef,
@ -1035,7 +1035,7 @@ fn iterate_method_candidates_with_autoref(
iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?; iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?;
let refed = Canonical { let refed = Canonical {
value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone()) value: TyKind::Ref(Mutability::Not, error_lifetime(), receiver_ty.value.clone())
.intern(Interner), .intern(Interner),
binders: receiver_ty.binders.clone(), binders: receiver_ty.binders.clone(),
}; };
@ -1043,7 +1043,7 @@ fn iterate_method_candidates_with_autoref(
iterate_method_candidates_by_receiver(refed, first_adjustment.with_autoref(Mutability::Not))?; iterate_method_candidates_by_receiver(refed, first_adjustment.with_autoref(Mutability::Not))?;
let ref_muted = Canonical { let ref_muted = Canonical {
value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone()) value: TyKind::Ref(Mutability::Mut, error_lifetime(), receiver_ty.value.clone())
.intern(Interner), .intern(Interner),
binders: receiver_ty.binders, binders: receiver_ty.binders,
}; };
@ -1369,6 +1369,7 @@ pub(crate) fn resolve_indexing_op(
None None
} }
// FIXME: Replace this with a `Try` impl once stable
macro_rules! check_that { macro_rules! check_that {
($cond:expr) => { ($cond:expr) => {
if !$cond { if !$cond {
@ -1377,6 +1378,7 @@ macro_rules! check_that {
}; };
} }
#[derive(Debug)]
enum IsValidCandidate { enum IsValidCandidate {
Yes, Yes,
No, No,

View file

@ -9,11 +9,14 @@ use hir_def::{
resolver::HasResolver, resolver::HasResolver,
}; };
use crate::mir::eval::{ use crate::{
name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, error_lifetime,
HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy, mir::eval::{
IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, name, pad16, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule,
Mutability, Result, Substitution, Ty, TyBuilder, TyExt, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy, IntervalOrOwned,
ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability,
Result, Substitution, Ty, TyBuilder, TyExt,
},
}; };
mod simd; mod simd;
@ -247,7 +250,7 @@ impl Evaluator<'_> {
let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?; let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
let arg = IntervalAndTy { let arg = IntervalAndTy {
interval: Interval { addr: tmp, size: self.ptr_size() }, interval: Interval { addr: tmp, size: self.ptr_size() },
ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()).intern(Interner), ty: TyKind::Ref(Mutability::Not, error_lifetime(), ty.clone()).intern(Interner),
}; };
let offset = layout.fields.offset(i).bytes_usize(); let offset = layout.fields.offset(i).bytes_usize();
self.write_memory(tmp, &addr.offset(offset).to_bytes())?; self.write_memory(tmp, &addr.offset(offset).to_bytes())?;

View file

@ -27,6 +27,7 @@ use crate::{
consteval::ConstEvalError, consteval::ConstEvalError,
db::{HirDatabase, InternedClosure}, db::{HirDatabase, InternedClosure},
display::HirDisplay, display::HirDisplay,
error_lifetime,
infer::{CaptureKind, CapturedItem, TypeMismatch}, infer::{CaptureKind, CapturedItem, TypeMismatch},
inhabitedness::is_ty_uninhabited_from, inhabitedness::is_ty_uninhabited_from,
layout::LayoutError, layout::LayoutError,
@ -90,7 +91,7 @@ pub enum MirLowerError {
UnresolvedField, UnresolvedField,
UnsizedTemporary(Ty), UnsizedTemporary(Ty),
MissingFunctionDefinition(DefWithBodyId, ExprId), MissingFunctionDefinition(DefWithBodyId, ExprId),
TypeMismatch(TypeMismatch), TypeMismatch(Option<TypeMismatch>),
/// This should never happen. Type mismatch should catch everything. /// This should never happen. Type mismatch should catch everything.
TypeError(&'static str), TypeError(&'static str),
NotSupported(String), NotSupported(String),
@ -170,14 +171,15 @@ impl MirLowerError {
body.pretty_print_expr(db.upcast(), *owner, *it) body.pretty_print_expr(db.upcast(), *owner, *it)
)?; )?;
} }
MirLowerError::TypeMismatch(e) => { MirLowerError::TypeMismatch(e) => match e {
writeln!( Some(e) => writeln!(
f, f,
"Type mismatch: Expected {}, found {}", "Type mismatch: Expected {}, found {}",
e.expected.display(db), e.expected.display(db),
e.actual.display(db), e.actual.display(db),
)?; )?,
} None => writeln!(f, "Type mismatch: types mismatch with {{unknown}}",)?,
},
MirLowerError::GenericArgNotProvided(id, subst) => { MirLowerError::GenericArgNotProvided(id, subst) => {
let parent = id.parent; let parent = id.parent;
let param = &db.generic_params(parent).type_or_consts[id.local_id]; let param = &db.generic_params(parent).type_or_consts[id.local_id];
@ -493,9 +495,11 @@ impl<'ctx> MirLowerCtx<'ctx> {
ty, ty,
value: chalk_ir::ConstValue::BoundVar(BoundVar::new( value: chalk_ir::ConstValue::BoundVar(BoundVar::new(
DebruijnIndex::INNERMOST, DebruijnIndex::INNERMOST,
gen.param_idx(p.into()).ok_or(MirLowerError::TypeError( gen.type_or_const_param_idx(p.into()).ok_or(
"fail to lower const generic param", MirLowerError::TypeError(
))?, "fail to lower const generic param",
),
)?,
)), )),
} }
.intern(Interner), .intern(Interner),
@ -1702,7 +1706,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
} }
fn is_uninhabited(&self, expr_id: ExprId) -> bool { fn is_uninhabited(&self, expr_id: ExprId) -> bool {
is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db) is_ty_uninhabited_from(self.db, &self.infer[expr_id], self.owner.module(self.db.upcast()))
} }
/// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and
@ -2032,10 +2036,12 @@ pub fn mir_body_for_closure_query(
let closure_local = ctx.result.locals.alloc(Local { let closure_local = ctx.result.locals.alloc(Local {
ty: match kind { ty: match kind {
FnTrait::FnOnce => infer[expr].clone(), FnTrait::FnOnce => infer[expr].clone(),
FnTrait::FnMut => TyKind::Ref(Mutability::Mut, static_lifetime(), infer[expr].clone()) FnTrait::FnMut => {
.intern(Interner), TyKind::Ref(Mutability::Mut, error_lifetime(), infer[expr].clone()).intern(Interner)
FnTrait::Fn => TyKind::Ref(Mutability::Not, static_lifetime(), infer[expr].clone()) }
.intern(Interner), FnTrait::Fn => {
TyKind::Ref(Mutability::Not, error_lifetime(), infer[expr].clone()).intern(Interner)
}
}, },
}); });
ctx.result.param_locals.push(closure_local); ctx.result.param_locals.push(closure_local);
@ -2152,8 +2158,10 @@ pub fn lower_to_mir(
// need to take this input explicitly. // need to take this input explicitly.
root_expr: ExprId, root_expr: ExprId,
) -> Result<MirBody> { ) -> Result<MirBody> {
if let Some((_, it)) = infer.type_mismatches().next() { if infer.has_errors {
return Err(MirLowerError::TypeMismatch(it.clone())); return Err(MirLowerError::TypeMismatch(
infer.type_mismatches().next().map(|(_, it)| it.clone()),
));
} }
let mut ctx = MirLowerCtx::new(db, owner, body, infer); let mut ctx = MirLowerCtx::new(db, owner, body, infer);
// 0 is return local // 0 is return local

View file

@ -290,7 +290,7 @@ impl MirLowerCtx<'_> {
Some((_, _, mutability)) => mutability, Some((_, _, mutability)) => mutability,
None => Mutability::Not, None => Mutability::Not,
}; };
let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner); let result_ref = TyKind::Ref(mutability, error_lifetime(), result_ty).intern(Interner);
let mut result: Place = self.temp(result_ref, current, span)?.into(); let mut result: Place = self.temp(result_ref, current, span)?.into();
let index_fn_op = Operand::const_zst( let index_fn_op = Operand::const_zst(
TyKind::FnDef( TyKind::FnDef(
@ -333,8 +333,8 @@ impl MirLowerCtx<'_> {
BorrowKind::Mut { kind: MutBorrowKind::Default }, BorrowKind::Mut { kind: MutBorrowKind::Default },
) )
}; };
let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner); let ty_ref = TyKind::Ref(chalk_mut, error_lifetime(), source_ty.clone()).intern(Interner);
let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner); let target_ty_ref = TyKind::Ref(chalk_mut, error_lifetime(), target_ty).intern(Interner);
let ref_place: Place = self.temp(ty_ref, current, span)?.into(); let ref_place: Place = self.temp(ty_ref, current, span)?.into();
self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span); self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span);
let deref_trait = self let deref_trait = self

View file

@ -101,7 +101,7 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
_outer_binder: DebruijnIndex, _outer_binder: DebruijnIndex,
) -> std::result::Result<chalk_ir::Const<Interner>, Self::Error> { ) -> std::result::Result<chalk_ir::Const<Interner>, Self::Error> {
let it = from_placeholder_idx(self.db, idx); let it = from_placeholder_idx(self.db, idx);
let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else { let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else {
not_supported!("missing idx in generics"); not_supported!("missing idx in generics");
}; };
Ok(self Ok(self
@ -119,7 +119,7 @@ impl FallibleTypeFolder<Interner> for Filler<'_> {
_outer_binder: DebruijnIndex, _outer_binder: DebruijnIndex,
) -> std::result::Result<Ty, Self::Error> { ) -> std::result::Result<Ty, Self::Error> {
let it = from_placeholder_idx(self.db, idx); let it = from_placeholder_idx(self.db, idx);
let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else { let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else {
not_supported!("missing idx in generics"); not_supported!("missing idx in generics");
}; };
Ok(self Ok(self

View file

@ -136,3 +136,20 @@ impl Trait for () {
"#, "#,
); );
} }
#[test]
fn no_mismatches_with_unresolved_projections() {
check_no_mismatches(
r#"
// `Thing` is `{unknown}`
fn create() -> Option<(i32, Thing)> {
Some((69420, Thing))
}
fn consume() -> Option<()> {
let (number, thing) = create()?;
Some(())
}
"#,
);
}

View file

@ -85,7 +85,7 @@ fn render_dyn_for_ty() {
trait Foo<'a> {} trait Foo<'a> {}
fn foo(foo: &dyn for<'a> Foo<'a>) {} fn foo(foo: &dyn for<'a> Foo<'a>) {}
// ^^^ &dyn Foo<'static> // ^^^ &dyn Foo<'_>
"#, "#,
); );
} }

View file

@ -1109,7 +1109,7 @@ fn var_args() {
#[lang = "va_list"] #[lang = "va_list"]
pub struct VaListImpl<'f>; pub struct VaListImpl<'f>;
fn my_fn(foo: ...) {} fn my_fn(foo: ...) {}
//^^^ VaListImpl<'static> //^^^ VaListImpl<'{error}>
"#, "#,
); );
} }

View file

@ -896,13 +896,13 @@ fn flush(&self) {
"#, "#,
expect![[r#" expect![[r#"
123..127 'self': &Mutex<T> 123..127 'self': &Mutex<T>
150..152 '{}': MutexGuard<'static, T> 150..152 '{}': MutexGuard<'{error}, T>
234..238 'self': &{unknown} 234..238 'self': &{unknown}
240..290 '{ ...()); }': () 240..290 '{ ...()); }': ()
250..251 'w': &Mutex<BufWriter> 250..251 'w': &Mutex<BufWriter>
276..287 '*(w.lock())': BufWriter 276..287 '*(w.lock())': BufWriter
278..279 'w': &Mutex<BufWriter> 278..279 'w': &Mutex<BufWriter>
278..286 'w.lock()': MutexGuard<'static, BufWriter> 278..286 'w.lock()': MutexGuard<'{error}, BufWriter>
"#]], "#]],
); );
} }

View file

@ -3092,7 +3092,7 @@ fn main() {
389..394 'boxed': Box<Foo<i32>> 389..394 'boxed': Box<Foo<i32>>
389..406 'boxed....nner()': &i32 389..406 'boxed....nner()': &i32
416..421 'good1': &i32 416..421 'good1': &i32
424..438 'Foo::get_inner': fn get_inner<i32, 'static>(&Box<Foo<i32>>) -> &i32 424..438 'Foo::get_inner': fn get_inner<i32, '{error}>(&Box<Foo<i32>>) -> &i32
424..446 'Foo::g...boxed)': &i32 424..446 'Foo::g...boxed)': &i32
439..445 '&boxed': &Box<Foo<i32>> 439..445 '&boxed': &Box<Foo<i32>>
440..445 'boxed': Box<Foo<i32>> 440..445 'boxed': Box<Foo<i32>>
@ -3100,7 +3100,7 @@ fn main() {
464..469 'boxed': Box<Foo<i32>> 464..469 'boxed': Box<Foo<i32>>
464..480 'boxed....self()': &Foo<i32> 464..480 'boxed....self()': &Foo<i32>
490..495 'good2': &Foo<i32> 490..495 'good2': &Foo<i32>
498..511 'Foo::get_self': fn get_self<i32, 'static>(&Box<Foo<i32>>) -> &Foo<i32> 498..511 'Foo::get_self': fn get_self<i32, '{error}>(&Box<Foo<i32>>) -> &Foo<i32>
498..519 'Foo::g...boxed)': &Foo<i32> 498..519 'Foo::g...boxed)': &Foo<i32>
512..518 '&boxed': &Box<Foo<i32>> 512..518 '&boxed': &Box<Foo<i32>>
513..518 'boxed': Box<Foo<i32>> 513..518 'boxed': Box<Foo<i32>>
@ -3659,7 +3659,7 @@ fn main() {
let are = "are"; let are = "are";
let count = 10; let count = 10;
builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'static> // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'{error}>
} }
"#, "#,
); );

View file

@ -1602,7 +1602,7 @@ fn weird_bounds() {
r#" r#"
//- minicore: sized //- minicore: sized
trait Trait {} trait Trait {}
fn test( fn test<'lifetime>(
a: impl Trait + 'lifetime, a: impl Trait + 'lifetime,
b: impl 'lifetime, b: impl 'lifetime,
c: impl (Trait), c: impl (Trait),
@ -1612,13 +1612,13 @@ fn test(
) {} ) {}
"#, "#,
expect![[r#" expect![[r#"
28..29 'a': impl Trait 39..40 'a': impl Trait + 'lifetime
59..60 'b': impl Sized 70..71 'b': impl 'lifetime
82..83 'c': impl Trait 93..94 'c': impl Trait
103..104 'd': impl Sized 114..115 'd': impl 'lifetime
128..129 'e': impl ?Sized 139..140 'e': impl ?Sized
148..149 'f': impl Trait + ?Sized 159..160 'f': impl Trait + ?Sized
173..175 '{}': () 184..186 '{}': ()
"#]], "#]],
); );
} }

View file

@ -262,7 +262,7 @@ impl<'a> ClosureSubst<'a> {
} }
} }
#[derive(Debug)] #[derive(Clone, Debug)]
pub(crate) struct Generics { pub(crate) struct Generics {
def: GenericDefId, def: GenericDefId,
pub(crate) params: Interned<GenericParams>, pub(crate) params: Interned<GenericParams>,
@ -274,6 +274,10 @@ impl Generics {
self.iter().map(|(id, _)| id) self.iter().map(|(id, _)| id)
} }
pub(crate) fn def(&self) -> GenericDefId {
self.def
}
/// Iterator over types and const params of self, then parent. /// Iterator over types and const params of self, then parent.
pub(crate) fn iter<'a>( pub(crate) fn iter<'a>(
&'a self, &'a self,
@ -304,7 +308,11 @@ impl Generics {
}; };
let lt_iter = self.params.iter_lt().map(from_lt_id(self)); let lt_iter = self.params.iter_lt().map(from_lt_id(self));
self.params.iter().map(from_toc_id(self)).chain(lt_iter).chain(self.iter_parent()) self.params
.iter_type_or_consts()
.map(from_toc_id(self))
.chain(lt_iter)
.chain(self.iter_parent())
} }
/// Iterate over types and const params without parent params. /// Iterate over types and const params without parent params.
@ -336,16 +344,19 @@ impl Generics {
} }
}; };
self.params.iter().map(from_toc_id(self)).chain(self.params.iter_lt().map(from_lt_id(self))) self.params
.iter_type_or_consts()
.map(from_toc_id(self))
.chain(self.params.iter_lt().map(from_lt_id(self)))
} }
/// Iterator over types and const params of parent. /// Iterator over types and const params of parent.
#[allow(clippy::needless_lifetimes)] pub(crate) fn iter_parent(
pub(crate) fn iter_parent<'a>( &self,
&'a self, ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'a>)> + 'a {
self.parent_generics().into_iter().flat_map(|it| { self.parent_generics().into_iter().flat_map(|it| {
let from_toc_id = move |(local_id, p): (_, &'a TypeOrConstParamData)| { let from_toc_id = move |(local_id, p)| {
let p: &_ = p;
let id = TypeOrConstParamId { parent: it.def, local_id }; let id = TypeOrConstParamId { parent: it.def, local_id };
match p { match p {
TypeOrConstParamData::TypeParamData(p) => ( TypeOrConstParamData::TypeParamData(p) => (
@ -359,14 +370,14 @@ impl Generics {
} }
}; };
let from_lt_id = move |(local_id, p): (_, &'a LifetimeParamData)| { let from_lt_id = move |(local_id, p): (_, _)| {
( (
GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
GenericParamDataRef::LifetimeParamData(p), GenericParamDataRef::LifetimeParamData(p),
) )
}; };
let lt_iter = it.params.iter_lt().map(from_lt_id); let lt_iter = it.params.iter_lt().map(from_lt_id);
it.params.iter().map(from_toc_id).chain(lt_iter) it.params.iter_type_or_consts().map(from_toc_id).chain(lt_iter)
}) })
} }
@ -383,7 +394,7 @@ impl Generics {
} }
/// Returns number of generic parameter excluding those from parent /// Returns number of generic parameter excluding those from parent
fn len_params(&self) -> usize { fn len_type_and_const_params(&self) -> usize {
self.params.type_or_consts.len() self.params.type_or_consts.len()
} }
@ -394,7 +405,7 @@ impl Generics {
let mut impl_trait_params = 0; let mut impl_trait_params = 0;
let mut const_params = 0; let mut const_params = 0;
let mut lifetime_params = 0; let mut lifetime_params = 0;
self.params.iter().for_each(|(_, data)| match data { self.params.iter_type_or_consts().for_each(|(_, data)| match data {
TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeOrConstParamData::TypeParamData(p) => match p.provenance {
TypeParamProvenance::TypeParamList => type_params += 1, TypeParamProvenance::TypeParamList => type_params += 1,
TypeParamProvenance::TraitSelf => self_params += 1, TypeParamProvenance::TraitSelf => self_params += 1,
@ -409,18 +420,23 @@ impl Generics {
(parent_len, self_params, type_params, const_params, impl_trait_params, lifetime_params) (parent_len, self_params, type_params, const_params, impl_trait_params, lifetime_params)
} }
pub(crate) fn param_idx(&self, param: TypeOrConstParamId) -> Option<usize> { pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> {
Some(self.find_param(param)?.0) Some(self.find_type_or_const_param(param)?.0)
} }
fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> { fn find_type_or_const_param(
&self,
param: TypeOrConstParamId,
) -> Option<(usize, &TypeOrConstParamData)> {
if param.parent == self.def { if param.parent == self.def {
let (idx, (_local_id, data)) = let idx = param.local_id.into_raw().into_u32() as usize;
self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?; if idx >= self.params.type_or_consts.len() {
Some((idx, data)) return None;
}
Some((idx, &self.params.type_or_consts[param.local_id]))
} else { } else {
self.parent_generics() self.parent_generics()
.and_then(|g| g.find_param(param)) .and_then(|g| g.find_type_or_const_param(param))
// Remember that parent parameters come after parameters for self. // Remember that parent parameters come after parameters for self.
.map(|(idx, data)| (self.len_self() + idx, data)) .map(|(idx, data)| (self.len_self() + idx, data))
} }
@ -432,13 +448,14 @@ impl Generics {
fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<(usize, &LifetimeParamData)> { fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<(usize, &LifetimeParamData)> {
if lifetime.parent == self.def { if lifetime.parent == self.def {
let (idx, (_local_id, data)) = self let idx = lifetime.local_id.into_raw().into_u32() as usize;
.params if idx >= self.params.lifetimes.len() {
.iter_lt() return None;
.enumerate() }
.find(|(_, (idx, _))| *idx == lifetime.local_id)?; Some((
self.len_type_and_const_params() + idx,
Some((self.len_params() + idx, data)) &self.params.lifetimes[lifetime.local_id],
))
} else { } else {
self.parent_generics() self.parent_generics()
.and_then(|g| g.find_lifetime(lifetime)) .and_then(|g| g.find_lifetime(lifetime))
@ -450,6 +467,10 @@ impl Generics {
self.parent_generics.as_deref() self.parent_generics.as_deref()
} }
pub(crate) fn parent_or_self(&self) -> &Generics {
self.parent_generics.as_deref().unwrap_or(self)
}
/// Returns a Substitution that replaces each parameter by a bound variable. /// Returns a Substitution that replaces each parameter by a bound variable.
pub(crate) fn bound_vars_subst( pub(crate) fn bound_vars_subst(
&self, &self,

View file

@ -33,7 +33,7 @@ tt.workspace = true
span.workspace = true span.workspace = true
[features] [features]
in-rust-tree = [] in-rust-tree = ["hir-expand/in-rust-tree"]
[lints] [lints]
workspace = true workspace = true

View file

@ -4,24 +4,35 @@
//! //!
//! But we need this for at least LRU caching at the query level. //! But we need this for at least LRU caching at the query level.
pub use hir_def::db::{ pub use hir_def::db::{
AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, AttrsQuery, BlockDefMapQuery, BlockItemTreeQuery, BodyQuery, BodyWithSourceMapQuery,
ConstVisibilityQuery, CrateLangItemsQuery, CrateSupportsNoStdQuery, DefDatabase, ConstDataQuery, ConstVisibilityQuery, CrateDefMapQuery, CrateLangItemsQuery,
DefDatabaseStorage, EnumDataQuery, EnumVariantDataWithDiagnosticsQuery, ExprScopesQuery, CrateNotableTraitsQuery, CrateSupportsNoStdQuery, DefDatabase, DefDatabaseStorage,
ExternCrateDeclDataQuery, FieldVisibilitiesQuery, FieldsAttrsQuery, FieldsAttrsSourceMapQuery, EnumDataQuery, EnumVariantDataWithDiagnosticsQuery, ExprScopesQuery, ExternCrateDeclDataQuery,
FileItemTreeQuery, FunctionDataQuery, FunctionVisibilityQuery, GenericParamsQuery, FieldVisibilitiesQuery, FieldsAttrsQuery, FieldsAttrsSourceMapQuery, FileItemTreeQuery,
ImplDataWithDiagnosticsQuery, ImportMapQuery, InternAnonymousConstQuery, InternBlockQuery, FunctionDataQuery, FunctionVisibilityQuery, GenericParamsQuery, ImplDataWithDiagnosticsQuery,
InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, ImportMapQuery, InternAnonymousConstQuery, InternBlockQuery, InternConstQuery, InternDatabase,
InternExternBlockQuery, InternExternCrateQuery, InternFunctionQuery, InternImplQuery, InternDatabaseStorage, InternEnumQuery, InternExternBlockQuery, InternExternCrateQuery,
InternInTypeConstQuery, InternMacro2Query, InternMacroRulesQuery, InternProcMacroQuery, InternFunctionQuery, InternImplQuery, InternInTypeConstQuery, InternMacro2Query,
InternStaticQuery, InternStructQuery, InternTraitAliasQuery, InternTraitQuery, InternMacroRulesQuery, InternProcMacroQuery, InternStaticQuery, InternStructQuery,
InternTypeAliasQuery, InternUnionQuery, InternUseQuery, LangItemQuery, Macro2DataQuery, InternTraitAliasQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery,
MacroRulesDataQuery, ProcMacroDataQuery, StaticDataQuery, StructDataWithDiagnosticsQuery, InternUseQuery, LangItemQuery, Macro2DataQuery, MacroRulesDataQuery, ProcMacroDataQuery,
TraitAliasDataQuery, TraitDataWithDiagnosticsQuery, TypeAliasDataQuery, StaticDataQuery, StructDataWithDiagnosticsQuery, TraitAliasDataQuery,
UnionDataWithDiagnosticsQuery, TraitDataWithDiagnosticsQuery, TypeAliasDataQuery, UnionDataWithDiagnosticsQuery,
}; };
pub use hir_expand::db::{ pub use hir_expand::db::{
AstIdMapQuery, DeclMacroExpanderQuery, ExpandDatabase, ExpandDatabaseStorage, AstIdMapQuery, DeclMacroExpanderQuery, ExpandDatabase, ExpandDatabaseStorage,
ExpandProcMacroQuery, InternMacroCallQuery, InternSyntaxContextQuery, MacroArgQuery, ExpandProcMacroQuery, InternMacroCallQuery, InternSyntaxContextQuery, MacroArgQuery,
ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, ProcMacrosQuery, RealSpanMapQuery, ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, ProcMacrosQuery, RealSpanMapQuery,
}; };
pub use hir_ty::db::*; pub use hir_ty::db::{
AdtDatumQuery, AdtVarianceQuery, AssociatedTyDataQuery, AssociatedTyValueQuery, BorrowckQuery,
CallableItemSignatureQuery, ConstEvalDiscriminantQuery, ConstEvalQuery, ConstEvalStaticQuery,
ConstParamTyQuery, FieldTypesQuery, FnDefDatumQuery, FnDefVarianceQuery, GenericDefaultsQuery,
GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, HirDatabaseStorage,
ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, IncoherentInherentImplCratesQuery,
InherentImplsInBlockQuery, InherentImplsInCrateQuery, InternCallableDefQuery,
InternClosureQuery, InternCoroutineQuery, InternImplTraitIdQuery, InternLifetimeParamIdQuery,
InternTypeOrConstParamIdQuery, LayoutOfAdtQuery, MirBodyQuery, ProgramClausesForChalkEnvQuery,
ReturnTypeImplTraitsQuery, TargetDataLayoutQuery, TraitDatumQuery, TraitEnvironmentQuery,
TraitImplsInBlockQuery, TraitImplsInCrateQuery, TraitImplsInDepsQuery, TyQuery, ValueTyQuery,
};

View file

@ -1,4 +1,5 @@
//! HirDisplay implementations for various hir types. //! HirDisplay implementations for various hir types.
use either::Either;
use hir_def::{ use hir_def::{
data::adt::{StructKind, VariantData}, data::adt::{StructKind, VariantData},
generics::{ generics::{
@ -13,7 +14,7 @@ use hir_ty::{
write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
HirFormatter, SizedByDefault, HirFormatter, SizedByDefault,
}, },
Interner, TraitRefExt, WhereClause, AliasEq, AliasTy, Interner, ProjectionTyExt, TraitRefExt, TyKind, WhereClause,
}; };
use crate::{ use crate::{
@ -363,16 +364,52 @@ impl HirDisplay for TypeOrConstParam {
impl HirDisplay for TypeParam { impl HirDisplay for TypeParam {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let params = f.db.generic_params(self.id.parent());
let param_data = &params.type_or_consts[self.id.local_id()];
let substs = TyBuilder::placeholder_subst(f.db, self.id.parent());
let krate = self.id.parent().krate(f.db).id;
let ty =
TyKind::Placeholder(hir_ty::to_placeholder_idx(f.db, self.id.into())).intern(Interner);
let predicates = f.db.generic_predicates(self.id.parent());
let predicates = predicates
.iter()
.cloned()
.map(|pred| pred.substitute(Interner, &substs))
.filter(|wc| match wc.skip_binders() {
WhereClause::Implemented(tr) => tr.self_type_parameter(Interner) == ty,
WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), ty: _ }) => {
proj.self_type_parameter(f.db) == ty
}
WhereClause::AliasEq(_) => false,
WhereClause::TypeOutlives(to) => to.ty == ty,
WhereClause::LifetimeOutlives(_) => false,
})
.collect::<Vec<_>>();
match param_data {
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
write!(f, "{}", p.name.clone().unwrap().display(f.db.upcast()))?
}
TypeParamProvenance::ArgumentImplTrait => {
return write_bounds_like_dyn_trait_with_prefix(
f,
"impl",
Either::Left(&ty),
&predicates,
SizedByDefault::Sized { anchor: krate },
);
}
},
TypeOrConstParamData::ConstParamData(p) => {
write!(f, "{}", p.name.display(f.db.upcast()))?;
}
}
if f.omit_verbose_types() { if f.omit_verbose_types() {
return Ok(()); return Ok(());
} }
let bounds = f.db.generic_predicates_for_param(self.id.parent(), self.id.into(), None);
let substs = TyBuilder::placeholder_subst(f.db, self.id.parent());
let predicates: Vec<_> =
bounds.iter().cloned().map(|b| b.substitute(Interner, &substs)).collect();
let krate = self.id.parent().krate(f.db).id;
let sized_trait = let sized_trait =
f.db.lang_item(krate, LangItem::Sized).and_then(|lang_item| lang_item.as_trait()); f.db.lang_item(krate, LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() { let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() {
@ -382,7 +419,16 @@ impl HirDisplay for TypeParam {
let has_only_not_sized_bound = predicates.is_empty(); let has_only_not_sized_bound = predicates.is_empty();
if !has_only_sized_bound || has_only_not_sized_bound { if !has_only_sized_bound || has_only_not_sized_bound {
let default_sized = SizedByDefault::Sized { anchor: krate }; let default_sized = SizedByDefault::Sized { anchor: krate };
write_bounds_like_dyn_trait_with_prefix(f, ":", &predicates, default_sized)?; write_bounds_like_dyn_trait_with_prefix(
f,
":",
Either::Left(
&hir_ty::TyKind::Placeholder(hir_ty::to_placeholder_idx(f.db, self.id.into()))
.intern(Interner),
),
&predicates,
default_sized,
)?;
} }
Ok(()) Ok(())
} }

View file

@ -239,7 +239,7 @@ impl Crate {
db: &dyn DefDatabase, db: &dyn DefDatabase,
query: import_map::Query, query: import_map::Query,
) -> impl Iterator<Item = Either<ModuleDef, Macro>> { ) -> impl Iterator<Item = Either<ModuleDef, Macro>> {
let _p = tracing::span!(tracing::Level::INFO, "query_external_importables"); let _p = tracing::span!(tracing::Level::INFO, "query_external_importables").entered();
import_map::search_dependencies(db, self.into(), &query).into_iter().map(|item| { import_map::search_dependencies(db, self.into(), &query).into_iter().map(|item| {
match ItemInNs::from(item) { match ItemInNs::from(item) {
ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id), ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id),
@ -260,11 +260,11 @@ impl Crate {
doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/")
} }
pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions { pub fn cfg(&self, db: &dyn HirDatabase) -> Arc<CfgOptions> {
db.crate_graph()[self.id].cfg_options.clone() db.crate_graph()[self.id].cfg_options.clone()
} }
pub fn potential_cfg(&self, db: &dyn HirDatabase) -> CfgOptions { pub fn potential_cfg(&self, db: &dyn HirDatabase) -> Arc<CfgOptions> {
let data = &db.crate_graph()[self.id]; let data = &db.crate_graph()[self.id];
data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone()) data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone())
} }
@ -548,8 +548,8 @@ impl Module {
acc: &mut Vec<AnyDiagnostic>, acc: &mut Vec<AnyDiagnostic>,
style_lints: bool, style_lints: bool,
) { ) {
let name = self.name(db); let _p = tracing::span!(tracing::Level::INFO, "Module::diagnostics", name = ?self.name(db))
let _p = tracing::span!(tracing::Level::INFO, "Module::diagnostics", ?name); .entered();
let def_map = self.id.def_map(db.upcast()); let def_map = self.id.def_map(db.upcast());
for diag in def_map.diagnostics() { for diag in def_map.diagnostics() {
if diag.in_module != self.id.local_id { if diag.in_module != self.id.local_id {
@ -653,7 +653,7 @@ impl Module {
GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }) GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id })
}); });
let type_params = generic_params let type_params = generic_params
.iter() .iter_type_or_consts()
.filter(|(_, it)| it.type_param().is_some()) .filter(|(_, it)| it.type_param().is_some())
.map(|(local_id, _)| { .map(|(local_id, _)| {
GenericParamId::TypeParamId(TypeParamId::from_unchecked( GenericParamId::TypeParamId(TypeParamId::from_unchecked(
@ -684,7 +684,7 @@ impl Module {
let items = &db.trait_data(trait_.into()).items; let items = &db.trait_data(trait_.into()).items;
let required_items = items.iter().filter(|&(_, assoc)| match *assoc { let required_items = items.iter().filter(|&(_, assoc)| match *assoc {
AssocItemId::FunctionId(it) => !db.function_data(it).has_body(), AssocItemId::FunctionId(it) => !db.function_data(it).has_body(),
AssocItemId::ConstId(id) => Const::from(id).value(db).is_none(), AssocItemId::ConstId(id) => !db.const_data(id).has_body,
AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(), AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(),
}); });
impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().filter_map( impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().filter_map(
@ -1418,16 +1418,14 @@ impl Adt {
} }
pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
if !db.generic_params(self.into()).is_empty() {
return Err(LayoutError::HasPlaceholder);
}
let krate = self.krate(db).id;
db.layout_of_adt( db.layout_of_adt(
self.into(), self.into(),
Substitution::empty(Interner), TyBuilder::adt(db, self.into())
.fill_with_defaults(db, || TyKind::Error.intern(Interner))
.build_into_subst(),
db.trait_environment(self.into()), db.trait_environment(self.into()),
) )
.map(|layout| Layout(layout, db.target_data_layout(krate).unwrap())) .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).id).unwrap()))
} }
/// Turns this ADT into a type. Any type parameters of the ADT will be /// Turns this ADT into a type. Any type parameters of the ADT will be
@ -1630,7 +1628,6 @@ impl DefWithBody {
acc: &mut Vec<AnyDiagnostic>, acc: &mut Vec<AnyDiagnostic>,
style_lints: bool, style_lints: bool,
) { ) {
db.unwind_if_cancelled();
let krate = self.module(db).id.krate(); let krate = self.module(db).id.krate();
let (body, source_map) = db.body_with_source_map(self.into()); let (body, source_map) = db.body_with_source_map(self.into());
@ -1678,6 +1675,7 @@ impl DefWithBody {
for d in &infer.diagnostics { for d in &infer.diagnostics {
acc.extend(AnyDiagnostic::inference_diagnostic(db, self.into(), d, &source_map)); acc.extend(AnyDiagnostic::inference_diagnostic(db, self.into(), d, &source_map));
} }
for (pat_or_expr, mismatch) in infer.type_mismatches() { for (pat_or_expr, mismatch) in infer.type_mismatches() {
let expr_or_pat = match pat_or_expr { let expr_or_pat = match pat_or_expr {
ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left), ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left),
@ -1763,7 +1761,9 @@ impl DefWithBody {
need_mut = &mir::MutabilityReason::Not; need_mut = &mir::MutabilityReason::Not;
} }
let local = Local { parent: self.into(), binding_id }; let local = Local { parent: self.into(), binding_id };
match (need_mut, local.is_mut(db)) { let is_mut = body[binding_id].mode == BindingAnnotation::Mutable;
match (need_mut, is_mut) {
(mir::MutabilityReason::Unused, _) => { (mir::MutabilityReason::Unused, _) => {
let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with('_')); let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with('_'));
if !should_ignore { if !should_ignore {
@ -2007,12 +2007,15 @@ impl Function {
/// is this a `fn main` or a function with an `export_name` of `main`? /// is this a `fn main` or a function with an `export_name` of `main`?
pub fn is_main(self, db: &dyn HirDatabase) -> bool { pub fn is_main(self, db: &dyn HirDatabase) -> bool {
if !self.module(db).is_crate_root() {
return false;
}
let data = db.function_data(self.id); let data = db.function_data(self.id);
data.attrs.export_name() == Some("main")
|| self.module(db).is_crate_root() && data.name.to_smol_str() == "main"
}
data.name.to_smol_str() == "main" || data.attrs.export_name() == Some("main") /// Is this a function with an `export_name` of `main`?
pub fn exported_main(self, db: &dyn HirDatabase) -> bool {
let data = db.function_data(self.id);
data.attrs.export_name() == Some("main")
} }
/// Does this function have the ignore attribute? /// Does this function have the ignore attribute?
@ -3909,7 +3912,7 @@ impl Type {
inner.derived( inner.derived(
TyKind::Ref( TyKind::Ref(
if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not }, if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not },
hir_ty::static_lifetime(), hir_ty::error_lifetime(),
inner.ty.clone(), inner.ty.clone(),
) )
.intern(Interner), .intern(Interner),
@ -4492,7 +4495,7 @@ impl Type {
name: Option<&Name>, name: Option<&Name>,
mut callback: impl FnMut(Function) -> Option<T>, mut callback: impl FnMut(Function) -> Option<T>,
) -> Option<T> { ) -> Option<T> {
let _p = tracing::span!(tracing::Level::INFO, "iterate_method_candidates"); let _p = tracing::span!(tracing::Level::INFO, "iterate_method_candidates").entered();
let mut slot = None; let mut slot = None;
self.iterate_method_candidates_dyn( self.iterate_method_candidates_dyn(
@ -4580,7 +4583,7 @@ impl Type {
name: Option<&Name>, name: Option<&Name>,
mut callback: impl FnMut(AssocItem) -> Option<T>, mut callback: impl FnMut(AssocItem) -> Option<T>,
) -> Option<T> { ) -> Option<T> {
let _p = tracing::span!(tracing::Level::INFO, "iterate_path_candidates"); let _p = tracing::span!(tracing::Level::INFO, "iterate_path_candidates").entered();
let mut slot = None; let mut slot = None;
self.iterate_path_candidates_dyn( self.iterate_path_candidates_dyn(
db, db,
@ -4647,7 +4650,7 @@ impl Type {
&'a self, &'a self,
db: &'a dyn HirDatabase, db: &'a dyn HirDatabase,
) -> impl Iterator<Item = Trait> + 'a { ) -> impl Iterator<Item = Trait> + 'a {
let _p = tracing::span!(tracing::Level::INFO, "applicable_inherent_traits"); let _p = tracing::span!(tracing::Level::INFO, "applicable_inherent_traits").entered();
self.autoderef_(db) self.autoderef_(db)
.filter_map(|ty| ty.dyn_trait()) .filter_map(|ty| ty.dyn_trait())
.flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id)) .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id))
@ -4655,7 +4658,7 @@ impl Type {
} }
pub fn env_traits<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Trait> + 'a { pub fn env_traits<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Trait> + 'a {
let _p = tracing::span!(tracing::Level::INFO, "env_traits"); let _p = tracing::span!(tracing::Level::INFO, "env_traits").entered();
self.autoderef_(db) self.autoderef_(db)
.filter(|ty| matches!(ty.kind(Interner), TyKind::Placeholder(_))) .filter(|ty| matches!(ty.kind(Interner), TyKind::Placeholder(_)))
.flat_map(|ty| { .flat_map(|ty| {
@ -4709,10 +4712,12 @@ impl Type {
if let WhereClause::Implemented(trait_ref) = pred.skip_binders() { if let WhereClause::Implemented(trait_ref) = pred.skip_binders() {
cb(type_.clone()); cb(type_.clone());
// skip the self type. it's likely the type we just got the bounds from // skip the self type. it's likely the type we just got the bounds from
for ty in if let [self_ty, params @ ..] = trait_ref.substitution.as_slice(Interner) {
trait_ref.substitution.iter(Interner).skip(1).filter_map(|a| a.ty(Interner)) for ty in
{ params.iter().filter(|&ty| ty != self_ty).filter_map(|a| a.ty(Interner))
walk_type(db, &type_.derived(ty.clone()), cb); {
walk_type(db, &type_.derived(ty.clone()), cb);
}
} }
} }
} }

View file

@ -722,7 +722,7 @@ impl<'db> SemanticsImpl<'db> {
mut token: SyntaxToken, mut token: SyntaxToken,
f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<()>, f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<()>,
) { ) {
let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros"); let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros").entered();
let (sa, span, file_id) = let (sa, span, file_id) =
match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) { match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) {
Some(sa) => match sa.file_id.file_id() { Some(sa) => match sa.file_id.file_id() {
@ -1246,6 +1246,17 @@ impl<'db> SemanticsImpl<'db> {
.map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..))) .map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..)))
} }
pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option<u32> {
let sa = self.analyze(macro_call.syntax())?;
self.db
.parse_macro_expansion(
sa.expand(self.db, self.wrap_node_infile(macro_call.clone()).as_ref())?,
)
.value
.1
.matched_arm
}
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
let sa = match self.analyze(macro_call.syntax()) { let sa = match self.analyze(macro_call.syntax()) {
Some(it) => it, Some(it) => it,
@ -1359,7 +1370,7 @@ impl<'db> SemanticsImpl<'db> {
offset: Option<TextSize>, offset: Option<TextSize>,
infer_body: bool, infer_body: bool,
) -> Option<SourceAnalyzer> { ) -> Option<SourceAnalyzer> {
let _p = tracing::span!(tracing::Level::INFO, "Semantics::analyze_impl"); let _p = tracing::span!(tracing::Level::INFO, "Semantics::analyze_impl").entered();
let node = self.find_file(node); let node = self.find_file(node);
let container = self.with_ctx(|ctx| ctx.find_container(node))?; let container = self.with_ctx(|ctx| ctx.find_container(node))?;

View file

@ -118,7 +118,7 @@ pub(super) struct SourceToDefCtx<'a, 'b> {
impl SourceToDefCtx<'_, '_> { impl SourceToDefCtx<'_, '_> {
pub(super) fn file_to_def(&self, file: FileId) -> SmallVec<[ModuleId; 1]> { pub(super) fn file_to_def(&self, file: FileId) -> SmallVec<[ModuleId; 1]> {
let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def"); let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def").entered();
let mut mods = SmallVec::new(); let mut mods = SmallVec::new();
for &crate_id in self.db.relevant_crates(file).iter() { for &crate_id in self.db.relevant_crates(file).iter() {
// FIXME: inner items // FIXME: inner items
@ -133,7 +133,7 @@ impl SourceToDefCtx<'_, '_> {
} }
pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> { pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> {
let _p = tracing::span!(tracing::Level::INFO, "module_to_def"); let _p = tracing::span!(tracing::Level::INFO, "module_to_def").entered();
let parent_declaration = src let parent_declaration = src
.syntax() .syntax()
.ancestors_with_macros_skip_attr_item(self.db.upcast()) .ancestors_with_macros_skip_attr_item(self.db.upcast())
@ -158,7 +158,7 @@ impl SourceToDefCtx<'_, '_> {
} }
pub(super) fn source_file_to_def(&self, src: InFile<ast::SourceFile>) -> Option<ModuleId> { pub(super) fn source_file_to_def(&self, src: InFile<ast::SourceFile>) -> Option<ModuleId> {
let _p = tracing::span!(tracing::Level::INFO, "source_file_to_def"); let _p = tracing::span!(tracing::Level::INFO, "source_file_to_def").entered();
let file_id = src.file_id.original_file(self.db.upcast()); let file_id = src.file_id.original_file(self.db.upcast());
self.file_to_def(file_id).first().copied() self.file_to_def(file_id).first().copied()
} }

View file

@ -105,7 +105,7 @@ fn add_missing_impl_members_inner(
assist_id: &'static str, assist_id: &'static str,
label: &'static str, label: &'static str,
) -> Option<()> { ) -> Option<()> {
let _p = tracing::span!(tracing::Level::INFO, "add_missing_impl_members_inner"); let _p = tracing::span!(tracing::Level::INFO, "add_missing_impl_members_inner").entered();
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
let impl_ = ctx.sema.to_def(&impl_def)?; let impl_ = ctx.sema.to_def(&impl_def)?;

View file

@ -1588,4 +1588,82 @@ mod bar {
"#, "#,
); );
} }
#[test]
fn local_inline_import_has_alias() {
// FIXME
check_assist_not_applicable(
auto_import,
r#"
struct S<T>(T);
use S as IoResult;
mod foo {
pub fn bar() -> S$0<()> {}
}
"#,
);
}
#[test]
fn alias_local() {
// FIXME
check_assist_not_applicable(
auto_import,
r#"
struct S<T>(T);
use S as IoResult;
mod foo {
pub fn bar() -> IoResult$0<()> {}
}
"#,
);
}
#[test]
fn preserve_raw_identifiers_strict() {
check_assist(
auto_import,
r"
r#as$0
pub mod ffi_mod {
pub fn r#as() {};
}
",
r"
use ffi_mod::r#as;
r#as
pub mod ffi_mod {
pub fn r#as() {};
}
",
);
}
#[test]
fn preserve_raw_identifiers_reserved() {
check_assist(
auto_import,
r"
r#abstract$0
pub mod ffi_mod {
pub fn r#abstract() {};
}
",
r"
use ffi_mod::r#abstract;
r#abstract
pub mod ffi_mod {
pub fn r#abstract() {};
}
",
);
}
} }

View file

@ -0,0 +1,250 @@
use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait};
use itertools::Itertools;
use syntax::{
ast::{self, make, AstNode, HasName},
ted,
};
use crate::{AssistContext, AssistId, AssistKind, Assists};
// Assist: convert_from_to_tryfrom
//
// Converts a From impl to a TryFrom impl, wrapping returns in `Ok`.
//
// ```
// # //- minicore: from
// impl $0From<usize> for Thing {
// fn from(val: usize) -> Self {
// Thing {
// b: val.to_string(),
// a: val
// }
// }
// }
// ```
// ->
// ```
// impl TryFrom<usize> for Thing {
// type Error = ${0:()};
//
// fn try_from(val: usize) -> Result<Self, Self::Error> {
// Ok(Thing {
// b: val.to_string(),
// a: val
// })
// }
// }
// ```
pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let impl_ = ctx.find_node_at_offset::<ast::Impl>()?;
let trait_ty = impl_.trait_()?;
let module = ctx.sema.scope(impl_.syntax())?.module();
let from_type = match &trait_ty {
ast::Type::PathType(path) => {
path.path()?.segment()?.generic_arg_list()?.generic_args().next()?
}
_ => return None,
};
let associated_items = impl_.assoc_item_list()?;
let from_fn = associated_items.assoc_items().find_map(|item| {
if let ast::AssocItem::Fn(f) = item {
if f.name()?.text() == "from" {
return Some(f);
}
};
None
})?;
let from_fn_name = from_fn.name()?;
let from_fn_return_type = from_fn.ret_type()?.ty()?;
let return_exprs = from_fn.body()?.syntax().descendants().filter_map(ast::ReturnExpr::cast);
let tail_expr = from_fn.body()?.tail_expr()?;
if resolve_target_trait(&ctx.sema, &impl_)?
!= FamousDefs(&ctx.sema, module.krate()).core_convert_From()?
{
return None;
}
acc.add(
AssistId("convert_from_to_tryfrom", AssistKind::RefactorRewrite),
"Convert From to TryFrom",
impl_.syntax().text_range(),
|builder| {
let trait_ty = builder.make_mut(trait_ty);
let from_fn_return_type = builder.make_mut(from_fn_return_type);
let from_fn_name = builder.make_mut(from_fn_name);
let tail_expr = builder.make_mut(tail_expr);
let return_exprs = return_exprs.map(|r| builder.make_mut(r)).collect_vec();
let associated_items = builder.make_mut(associated_items).clone();
ted::replace(
trait_ty.syntax(),
make::ty(&format!("TryFrom<{from_type}>")).syntax().clone_for_update(),
);
ted::replace(
from_fn_return_type.syntax(),
make::ty("Result<Self, Self::Error>").syntax().clone_for_update(),
);
ted::replace(from_fn_name.syntax(), make::name("try_from").syntax().clone_for_update());
ted::replace(
tail_expr.syntax(),
wrap_ok(tail_expr.clone()).syntax().clone_for_update(),
);
for r in return_exprs {
let t = r.expr().unwrap_or_else(make::expr_unit);
ted::replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update());
}
let error_type = ast::AssocItem::TypeAlias(make::ty_alias(
"Error",
None,
None,
None,
Some((make::ty_unit(), None)),
))
.clone_for_update();
if let Some(cap) = ctx.config.snippet_cap {
if let ast::AssocItem::TypeAlias(type_alias) = &error_type {
if let Some(ty) = type_alias.ty() {
builder.add_placeholder_snippet(cap, ty);
}
}
}
associated_items.add_item_at_start(error_type);
},
)
}
fn wrap_ok(expr: ast::Expr) -> ast::Expr {
make::expr_call(
make::expr_path(make::ext::ident_path("Ok")),
make::arg_list(std::iter::once(expr)),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{check_assist, check_assist_not_applicable};
#[test]
fn converts_from_to_tryfrom() {
check_assist(
convert_from_to_tryfrom,
r#"
//- minicore: from
struct Foo(String);
impl $0From<String> for Foo {
fn from(val: String) -> Self {
if val == "bar" {
return Foo(val);
}
Self(val)
}
}
"#,
r#"
struct Foo(String);
impl TryFrom<String> for Foo {
type Error = ${0:()};
fn try_from(val: String) -> Result<Self, Self::Error> {
if val == "bar" {
return Ok(Foo(val));
}
Ok(Self(val))
}
}
"#,
);
}
#[test]
fn converts_from_to_tryfrom_nested_type() {
check_assist(
convert_from_to_tryfrom,
r#"
//- minicore: from
struct Foo(String);
impl $0From<Option<String>> for Foo {
fn from(val: Option<String>) -> Self {
match val {
Some(val) => Foo(val),
None => Foo("".to_string())
}
}
}
"#,
r#"
struct Foo(String);
impl TryFrom<Option<String>> for Foo {
type Error = ${0:()};
fn try_from(val: Option<String>) -> Result<Self, Self::Error> {
Ok(match val {
Some(val) => Foo(val),
None => Foo("".to_string())
})
}
}
"#,
);
}
#[test]
fn converts_from_to_tryfrom_preserves_lifetimes() {
check_assist(
convert_from_to_tryfrom,
r#"
//- minicore: from
struct Foo<'a>(&'a str);
impl<'a> $0From<&'a str> for Foo<'a> {
fn from(val: &'a str) -> Self {
Self(val)
}
}
"#,
r#"
struct Foo<'a>(&'a str);
impl<'a> TryFrom<&'a str> for Foo<'a> {
type Error = ${0:()};
fn try_from(val: &'a str) -> Result<Self, Self::Error> {
Ok(Self(val))
}
}
"#,
);
}
#[test]
fn other_trait_not_applicable() {
check_assist_not_applicable(
convert_from_to_tryfrom,
r#"
struct Foo(String);
impl $0TryFrom<String> for Foo {
fn try_from(val: String) -> Result<Self, Self::Error> {
Ok(Self(val))
}
}
"#,
);
}
}

View file

@ -1149,8 +1149,14 @@ fn reference_is_exclusive(
node: &dyn HasTokenAtOffset, node: &dyn HasTokenAtOffset,
ctx: &AssistContext<'_>, ctx: &AssistContext<'_>,
) -> bool { ) -> bool {
// FIXME: this quite an incorrect way to go about doing this :-)
// `FileReference` is an IDE-type --- it encapsulates data communicated to the human,
// but doesn't necessary fully reflect all the intricacies of the underlying language semantics
// The correct approach here would be to expose this entire analysis as a method on some hir
// type. Something like `body.free_variables(statement_range)`.
// we directly modify variable with set: `n = 0`, `n += 1` // we directly modify variable with set: `n = 0`, `n += 1`
if reference.category == Some(ReferenceCategory::Write) { if reference.category.contains(ReferenceCategory::WRITE) {
return true; return true;
} }
@ -5617,7 +5623,7 @@ fn func<T: Debug>(i: Struct<'_, T>) {
fun_name(i); fun_name(i);
} }
fn $0fun_name(i: Struct<'static, T>) { fn $0fun_name(i: Struct<'_, T>) {
foo(i); foo(i);
} }
"#, "#,

View file

@ -175,8 +175,7 @@ fn add_func_to_accumulator(
edit.edit_file(file); edit.edit_file(file);
let target = function_builder.target.clone(); let target = function_builder.target.clone();
let function_template = function_builder.render(); let func = function_builder.render(ctx.config.snippet_cap, edit);
let func = function_template.to_ast(ctx.config.snippet_cap, edit);
if let Some(name) = adt_name { if let Some(name) = adt_name {
let name = make::ty_path(make::ext::ident_path(&format!("{}", name.display(ctx.db())))); let name = make::ty_path(make::ext::ident_path(&format!("{}", name.display(ctx.db()))));
@ -205,37 +204,6 @@ fn get_adt_source(
find_struct_impl(ctx, &adt_source, &[fn_name.to_owned()]).map(|impl_| (impl_, range.file_id)) find_struct_impl(ctx, &adt_source, &[fn_name.to_owned()]).map(|impl_| (impl_, range.file_id))
} }
struct FunctionTemplate {
fn_def: ast::Fn,
ret_type: Option<ast::RetType>,
should_focus_return_type: bool,
tail_expr: ast::Expr,
}
impl FunctionTemplate {
fn to_ast(&self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn {
let Self { fn_def, ret_type, should_focus_return_type, tail_expr } = self;
if let Some(cap) = cap {
if *should_focus_return_type {
// Focus the return type if there is one
match ret_type {
Some(ret_type) => {
edit.add_placeholder_snippet(cap, ret_type.clone());
}
None => {
edit.add_placeholder_snippet(cap, tail_expr.clone());
}
}
} else {
edit.add_placeholder_snippet(cap, tail_expr.clone());
}
}
fn_def.clone()
}
}
struct FunctionBuilder { struct FunctionBuilder {
target: GeneratedFunctionTarget, target: GeneratedFunctionTarget,
fn_name: ast::Name, fn_name: ast::Name,
@ -339,7 +307,7 @@ impl FunctionBuilder {
}) })
} }
fn render(self) -> FunctionTemplate { fn render(self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn {
let placeholder_expr = make::ext::expr_todo(); let placeholder_expr = make::ext::expr_todo();
let fn_body = make::block_expr(vec![], Some(placeholder_expr)); let fn_body = make::block_expr(vec![], Some(placeholder_expr));
let visibility = match self.visibility { let visibility = match self.visibility {
@ -361,17 +329,31 @@ impl FunctionBuilder {
) )
.clone_for_update(); .clone_for_update();
FunctionTemplate { let ret_type = fn_def.ret_type();
ret_type: fn_def.ret_type(), // PANIC: we guarantee we always create a function body with a tail expr
// PANIC: we guarantee we always create a function body with a tail expr let tail_expr = fn_def
tail_expr: fn_def .body()
.body() .expect("generated function should have a body")
.expect("generated function should have a body") .tail_expr()
.tail_expr() .expect("function body should have a tail expression");
.expect("function body should have a tail expression"),
should_focus_return_type: self.should_focus_return_type, if let Some(cap) = cap {
fn_def, if self.should_focus_return_type {
// Focus the return type if there is one
match ret_type {
Some(ret_type) => {
edit.add_placeholder_snippet(cap, ret_type.clone());
}
None => {
edit.add_placeholder_snippet(cap, tail_expr.clone());
}
}
} else {
edit.add_placeholder_snippet(cap, tail_expr.clone());
}
} }
fn_def
} }
} }

View file

@ -49,13 +49,13 @@ use crate::{
// //
// fn bar() { // fn bar() {
// { // {
// let word = "안녕하세요"; // let word: &str = "안녕하세요";
// if !word.is_empty() { // if !word.is_empty() {
// print(word); // print(word);
// } // }
// }; // };
// { // {
// let word = "여러분"; // let word: &str = "여러분";
// if !word.is_empty() { // if !word.is_empty() {
// print(word); // print(word);
// } // }

View file

@ -59,10 +59,7 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
let ty = match ty.display_source_code(ctx.db(), module.into(), false) { let ty = match ty.display_source_code(ctx.db(), module.into(), false) {
Ok(ty) => ty, Ok(ty) => ty,
Err(_) => { Err(_) => return None,
cov_mark::hit!(promote_local_not_applicable_if_ty_not_inferred);
return None;
}
}; };
let initializer = let_stmt.initializer()?; let initializer = let_stmt.initializer()?;
@ -315,13 +312,17 @@ fn foo() {
#[test] #[test]
fn not_applicable_unknown_ty() { fn not_applicable_unknown_ty() {
cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred); check_assist(
check_assist_not_applicable(
promote_local_to_const, promote_local_to_const,
r" r"
fn foo() { fn foo() {
let x$0 = bar(); let x$0 = bar();
} }
",
r"
fn foo() {
const $0X: _ = bar();
}
", ",
); );
} }

View file

@ -145,7 +145,7 @@ fn used_once_in_scope(ctx: &AssistContext<'_>, def: Definition, scopes: &Vec<Sea
for scope in scopes { for scope in scopes {
let mut search_non_import = |_, r: FileReference| { let mut search_non_import = |_, r: FileReference| {
// The import itself is a use; we must skip that. // The import itself is a use; we must skip that.
if r.category != Some(ReferenceCategory::Import) { if !r.category.contains(ReferenceCategory::IMPORT) {
found = true; found = true;
true true
} else { } else {

View file

@ -3,7 +3,7 @@ use syntax::{
AstNode, AstToken, AstNode, AstToken,
}; };
use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; use crate::{utils::test_related_attribute_syn, AssistContext, AssistId, AssistKind, Assists};
// Assist: toggle_ignore // Assist: toggle_ignore
// //
@ -26,7 +26,7 @@ use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind,
pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let attr: ast::Attr = ctx.find_node_at_offset()?; let attr: ast::Attr = ctx.find_node_at_offset()?;
let func = attr.syntax().parent().and_then(ast::Fn::cast)?; let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
let attr = test_related_attribute(&func)?; let attr = test_related_attribute_syn(&func)?;
match has_ignore_attribute(&func) { match has_ignore_attribute(&func) {
None => acc.add( None => acc.add(

View file

@ -0,0 +1,581 @@
use ide_db::source_change::SourceChangeBuilder;
use itertools::Itertools;
use syntax::{
algo,
ast::{self, make, AstNode},
ted::{self, Position},
NodeOrToken, SyntaxToken, TextRange, T,
};
use crate::{AssistContext, AssistId, AssistKind, Assists};
// Assist: wrap_unwrap_cfg_attr
//
// Wraps an attribute to a cfg_attr attribute or unwraps a cfg_attr attribute to the inner attributes.
//
// ```
// #[derive$0(Debug)]
// struct S {
// field: i32
// }
// ```
// ->
// ```
// #[cfg_attr($0, derive(Debug))]
// struct S {
// field: i32
// }
enum WrapUnwrapOption {
WrapDerive { derive: TextRange, attr: ast::Attr },
WrapAttr(ast::Attr),
}
/// Attempts to get the derive attribute from a derive attribute list
///
/// This will collect all the tokens in the "path" within the derive attribute list
/// But a derive attribute list doesn't have paths. So we need to collect all the tokens before and after the ident
///
/// If this functions return None just map to WrapAttr
fn attempt_get_derive(attr: ast::Attr, ident: SyntaxToken) -> WrapUnwrapOption {
let attempt_attr = || {
{
let mut derive = ident.text_range();
// TokenTree is all the tokens between the `(` and `)`. They do not have paths. So a path `serde::Serialize` would be [Ident Colon Colon Ident]
// So lets say we have derive(Debug, serde::Serialize, Copy) ident would be on Serialize
// We need to grab all previous tokens until we find a `,` or `(` and all following tokens until we find a `,` or `)`
// We also want to consume the following comma if it exists
let mut prev = algo::skip_trivia_token(
ident.prev_sibling_or_token()?.into_token()?,
syntax::Direction::Prev,
)?;
let mut following = algo::skip_trivia_token(
ident.next_sibling_or_token()?.into_token()?,
syntax::Direction::Next,
)?;
if (prev.kind() == T![,] || prev.kind() == T!['('])
&& (following.kind() == T![,] || following.kind() == T![')'])
{
// This would be a single ident such as Debug. As no path is present
if following.kind() == T![,] {
derive = derive.cover(following.text_range());
} else if following.kind() == T![')'] && prev.kind() == T![,] {
derive = derive.cover(prev.text_range());
}
Some(WrapUnwrapOption::WrapDerive { derive, attr: attr.clone() })
} else {
let mut consumed_comma = false;
// Collect the path
while let Some(prev_token) = algo::skip_trivia_token(prev, syntax::Direction::Prev)
{
let kind = prev_token.kind();
if kind == T![,] {
consumed_comma = true;
derive = derive.cover(prev_token.text_range());
break;
} else if kind == T!['('] {
break;
} else {
derive = derive.cover(prev_token.text_range());
}
prev = prev_token.prev_sibling_or_token()?.into_token()?;
}
while let Some(next_token) =
algo::skip_trivia_token(following.clone(), syntax::Direction::Next)
{
let kind = next_token.kind();
match kind {
T![,] if !consumed_comma => {
derive = derive.cover(next_token.text_range());
break;
}
T![')'] | T![,] => break,
_ => derive = derive.cover(next_token.text_range()),
}
following = next_token.next_sibling_or_token()?.into_token()?;
}
Some(WrapUnwrapOption::WrapDerive { derive, attr: attr.clone() })
}
}
};
if ident.parent().and_then(ast::TokenTree::cast).is_none()
|| !attr.simple_name().map(|v| v.eq("derive")).unwrap_or_default()
{
WrapUnwrapOption::WrapAttr(attr)
} else {
attempt_attr().unwrap_or(WrapUnwrapOption::WrapAttr(attr))
}
}
pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let option = if ctx.has_empty_selection() {
let ident = ctx.find_token_syntax_at_offset(T![ident]);
let attr = ctx.find_node_at_offset::<ast::Attr>();
match (attr, ident) {
(Some(attr), Some(ident))
if attr.simple_name().map(|v| v.eq("derive")).unwrap_or_default() =>
{
Some(attempt_get_derive(attr.clone(), ident))
}
(Some(attr), _) => Some(WrapUnwrapOption::WrapAttr(attr)),
_ => None,
}
} else {
let covering_element = ctx.covering_element();
match covering_element {
NodeOrToken::Node(node) => ast::Attr::cast(node).map(WrapUnwrapOption::WrapAttr),
NodeOrToken::Token(ident) if ident.kind() == syntax::T![ident] => {
let attr = ident.parent_ancestors().find_map(ast::Attr::cast)?;
Some(attempt_get_derive(attr.clone(), ident))
}
_ => None,
}
}?;
match option {
WrapUnwrapOption::WrapAttr(attr) if attr.simple_name().as_deref() == Some("cfg_attr") => {
unwrap_cfg_attr(acc, attr)
}
WrapUnwrapOption::WrapAttr(attr) => wrap_cfg_attr(acc, ctx, attr),
WrapUnwrapOption::WrapDerive { derive, attr } => wrap_derive(acc, ctx, attr, derive),
}
}
fn wrap_derive(
acc: &mut Assists,
ctx: &AssistContext<'_>,
attr: ast::Attr,
derive_element: TextRange,
) -> Option<()> {
let range = attr.syntax().text_range();
let token_tree = attr.token_tree()?;
let mut path_text = String::new();
let mut cfg_derive_tokens = Vec::new();
let mut new_derive = Vec::new();
for tt in token_tree.token_trees_and_tokens() {
let NodeOrToken::Token(token) = tt else {
continue;
};
if token.kind() == T!['('] || token.kind() == T![')'] {
continue;
}
if derive_element.contains_range(token.text_range()) {
if token.kind() != T![,] && token.kind() != syntax::SyntaxKind::WHITESPACE {
path_text.push_str(token.text());
cfg_derive_tokens.push(NodeOrToken::Token(token));
}
} else {
new_derive.push(NodeOrToken::Token(token));
}
}
let handle_source_change = |edit: &mut SourceChangeBuilder| {
let new_derive = make::attr_outer(make::meta_token_tree(
make::ext::ident_path("derive"),
make::token_tree(T!['('], new_derive),
))
.clone_for_update();
let meta = make::meta_token_tree(
make::ext::ident_path("cfg_attr"),
make::token_tree(
T!['('],
vec![
NodeOrToken::Token(make::token(T![,])),
NodeOrToken::Token(make::tokens::whitespace(" ")),
NodeOrToken::Token(make::tokens::ident("derive")),
NodeOrToken::Node(make::token_tree(T!['('], cfg_derive_tokens)),
],
),
);
// Remove the derive attribute
let edit_attr = edit.make_syntax_mut(attr.syntax().clone());
ted::replace(edit_attr, new_derive.syntax().clone());
let cfg_attr = make::attr_outer(meta).clone_for_update();
ted::insert_all_raw(
Position::after(new_derive.syntax().clone()),
vec![make::tokens::whitespace("\n").into(), cfg_attr.syntax().clone().into()],
);
if let Some(snippet_cap) = ctx.config.snippet_cap {
if let Some(first_meta) =
cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
{
edit.add_tabstop_after_token(snippet_cap, first_meta)
}
}
};
acc.add(
AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor),
format!("Wrap #[derive({path_text})] in `cfg_attr`",),
range,
handle_source_change,
);
Some(())
}
fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) -> Option<()> {
let range = attr.syntax().text_range();
let path = attr.path()?;
let handle_source_change = |edit: &mut SourceChangeBuilder| {
let mut raw_tokens = vec![
NodeOrToken::Token(make::token(T![,])),
NodeOrToken::Token(make::tokens::whitespace(" ")),
];
path.syntax().descendants_with_tokens().for_each(|it| {
if let NodeOrToken::Token(token) = it {
raw_tokens.push(NodeOrToken::Token(token));
}
});
if let Some(meta) = attr.meta() {
if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) {
raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" ")));
raw_tokens.push(NodeOrToken::Token(eq.clone()));
raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" ")));
expr.syntax().descendants_with_tokens().for_each(|it| {
if let NodeOrToken::Token(token) = it {
raw_tokens.push(NodeOrToken::Token(token));
}
});
} else if let Some(tt) = meta.token_tree() {
raw_tokens.extend(tt.token_trees_and_tokens());
}
}
let meta = make::meta_token_tree(
make::ext::ident_path("cfg_attr"),
make::token_tree(T!['('], raw_tokens),
);
let cfg_attr = if attr.excl_token().is_some() {
make::attr_inner(meta)
} else {
make::attr_outer(meta)
}
.clone_for_update();
let attr_syntax = edit.make_syntax_mut(attr.syntax().clone());
ted::replace(attr_syntax, cfg_attr.syntax());
if let Some(snippet_cap) = ctx.config.snippet_cap {
if let Some(first_meta) =
cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
{
edit.add_tabstop_after_token(snippet_cap, first_meta)
}
}
};
acc.add(
AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor),
"Convert to `cfg_attr`",
range,
handle_source_change,
);
Some(())
}
fn unwrap_cfg_attr(acc: &mut Assists, attr: ast::Attr) -> Option<()> {
let range = attr.syntax().text_range();
let meta = attr.meta()?;
let meta_tt = meta.token_tree()?;
let mut inner_attrs = Vec::with_capacity(1);
let mut found_comma = false;
let mut iter = meta_tt.token_trees_and_tokens().skip(1).peekable();
while let Some(tt) = iter.next() {
if let NodeOrToken::Token(token) = &tt {
if token.kind() == T![')'] {
break;
}
if token.kind() == T![,] {
found_comma = true;
continue;
}
}
if !found_comma {
continue;
}
let Some(attr_name) = tt.into_token().and_then(|token| {
if token.kind() == T![ident] {
Some(make::ext::ident_path(token.text()))
} else {
None
}
}) else {
continue;
};
let next_tt = iter.next()?;
let meta = match next_tt {
NodeOrToken::Node(tt) => make::meta_token_tree(attr_name, tt),
NodeOrToken::Token(token) if token.kind() == T![,] || token.kind() == T![')'] => {
make::meta_path(attr_name)
}
NodeOrToken::Token(token) => {
let equals = algo::skip_trivia_token(token, syntax::Direction::Next)?;
if equals.kind() != T![=] {
return None;
}
let expr_token =
algo::skip_trivia_token(equals.next_token()?, syntax::Direction::Next)
.and_then(|it| {
if it.kind().is_literal() {
Some(make::expr_literal(it.text()))
} else {
None
}
})?;
make::meta_expr(attr_name, ast::Expr::Literal(expr_token))
}
};
if attr.excl_token().is_some() {
inner_attrs.push(make::attr_inner(meta));
} else {
inner_attrs.push(make::attr_outer(meta));
}
}
if inner_attrs.is_empty() {
return None;
}
let handle_source_change = |f: &mut SourceChangeBuilder| {
let inner_attrs = inner_attrs.iter().map(|it| it.to_string()).join("\n");
f.replace(range, inner_attrs);
};
acc.add(
AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor),
"Extract Inner Attributes from `cfg_attr`",
range,
handle_source_change,
);
Some(())
}
#[cfg(test)]
mod tests {
use crate::tests::check_assist;
use super::*;
#[test]
fn test_basic_to_from_cfg_attr() {
check_assist(
wrap_unwrap_cfg_attr,
r#"
#[derive$0(Debug)]
pub struct Test {
test: u32,
}
"#,
r#"
#[cfg_attr($0, derive(Debug))]
pub struct Test {
test: u32,
}
"#,
);
check_assist(
wrap_unwrap_cfg_attr,
r#"
#[cfg_attr(debug_assertions, $0 derive(Debug))]
pub struct Test {
test: u32,
}
"#,
r#"
#[derive(Debug)]
pub struct Test {
test: u32,
}
"#,
);
}
#[test]
fn to_from_path_attr() {
check_assist(
wrap_unwrap_cfg_attr,
r#"
pub struct Test {
#[foo$0]
test: u32,
}
"#,
r#"
pub struct Test {
#[cfg_attr($0, foo)]
test: u32,
}
"#,
);
check_assist(
wrap_unwrap_cfg_attr,
r#"
pub struct Test {
#[cfg_attr(debug_assertions$0, foo)]
test: u32,
}
"#,
r#"
pub struct Test {
#[foo]
test: u32,
}
"#,
);
}
#[test]
fn to_from_eq_attr() {
check_assist(
wrap_unwrap_cfg_attr,
r#"
pub struct Test {
#[foo = "bar"$0]
test: u32,
}
"#,
r#"
pub struct Test {
#[cfg_attr($0, foo = "bar")]
test: u32,
}
"#,
);
check_assist(
wrap_unwrap_cfg_attr,
r#"
pub struct Test {
#[cfg_attr(debug_assertions$0, foo = "bar")]
test: u32,
}
"#,
r#"
pub struct Test {
#[foo = "bar"]
test: u32,
}
"#,
);
}
#[test]
fn inner_attrs() {
check_assist(
wrap_unwrap_cfg_attr,
r#"
#![no_std$0]
"#,
r#"
#![cfg_attr($0, no_std)]
"#,
);
check_assist(
wrap_unwrap_cfg_attr,
r#"
#![cfg_attr(not(feature = "std")$0, no_std)]
"#,
r#"
#![no_std]
"#,
);
}
#[test]
fn test_derive_wrap() {
check_assist(
wrap_unwrap_cfg_attr,
r#"
#[derive(Debug$0, Clone, Copy)]
pub struct Test {
test: u32,
}
"#,
r#"
#[derive( Clone, Copy)]
#[cfg_attr($0, derive(Debug))]
pub struct Test {
test: u32,
}
"#,
);
check_assist(
wrap_unwrap_cfg_attr,
r#"
#[derive(Clone, Debug$0, Copy)]
pub struct Test {
test: u32,
}
"#,
r#"
#[derive(Clone, Copy)]
#[cfg_attr($0, derive(Debug))]
pub struct Test {
test: u32,
}
"#,
);
}
#[test]
fn test_derive_wrap_with_path() {
check_assist(
wrap_unwrap_cfg_attr,
r#"
#[derive(std::fmt::Debug$0, Clone, Copy)]
pub struct Test {
test: u32,
}
"#,
r#"
#[derive( Clone, Copy)]
#[cfg_attr($0, derive(std::fmt::Debug))]
pub struct Test {
test: u32,
}
"#,
);
check_assist(
wrap_unwrap_cfg_attr,
r#"
#[derive(Clone, std::fmt::Debug$0, Copy)]
pub struct Test {
test: u32,
}
"#,
r#"
#[derive(Clone, Copy)]
#[cfg_attr($0, derive(std::fmt::Debug))]
pub struct Test {
test: u32,
}
"#,
);
}
#[test]
fn test_derive_wrap_at_end() {
check_assist(
wrap_unwrap_cfg_attr,
r#"
#[derive(std::fmt::Debug, Clone, Cop$0y)]
pub struct Test {
test: u32,
}
"#,
r#"
#[derive(std::fmt::Debug, Clone)]
#[cfg_attr($0, derive(Copy))]
pub struct Test {
test: u32,
}
"#,
);
check_assist(
wrap_unwrap_cfg_attr,
r#"
#[derive(Clone, Copy, std::fmt::D$0ebug)]
pub struct Test {
test: u32,
}
"#,
r#"
#[derive(Clone, Copy)]
#[cfg_attr($0, derive(std::fmt::Debug))]
pub struct Test {
test: u32,
}
"#,
);
}
}

View file

@ -116,6 +116,7 @@ mod handlers {
mod change_visibility; mod change_visibility;
mod convert_bool_then; mod convert_bool_then;
mod convert_comment_block; mod convert_comment_block;
mod convert_from_to_tryfrom;
mod convert_integer_literal; mod convert_integer_literal;
mod convert_into_to_from; mod convert_into_to_from;
mod convert_iter_for_each_to_for; mod convert_iter_for_each_to_for;
@ -217,6 +218,7 @@ mod handlers {
mod unwrap_result_return_type; mod unwrap_result_return_type;
mod unwrap_tuple; mod unwrap_tuple;
mod wrap_return_type_in_result; mod wrap_return_type_in_result;
mod wrap_unwrap_cfg_attr;
pub(crate) fn all() -> &'static [Handler] { pub(crate) fn all() -> &'static [Handler] {
&[ &[
@ -237,6 +239,7 @@ mod handlers {
convert_bool_then::convert_bool_then_to_if, convert_bool_then::convert_bool_then_to_if,
convert_bool_then::convert_if_to_bool_then, convert_bool_then::convert_if_to_bool_then,
convert_comment_block::convert_comment_block, convert_comment_block::convert_comment_block,
convert_from_to_tryfrom::convert_from_to_tryfrom,
convert_integer_literal::convert_integer_literal, convert_integer_literal::convert_integer_literal,
convert_into_to_from::convert_into_to_from, convert_into_to_from::convert_into_to_from,
convert_iter_for_each_to_for::convert_iter_for_each_to_for, convert_iter_for_each_to_for::convert_iter_for_each_to_for,
@ -342,6 +345,8 @@ mod handlers {
unwrap_tuple::unwrap_tuple, unwrap_tuple::unwrap_tuple,
unqualify_method_call::unqualify_method_call, unqualify_method_call::unqualify_method_call,
wrap_return_type_in_result::wrap_return_type_in_result, wrap_return_type_in_result::wrap_return_type_in_result,
wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr,
// These are manually sorted for better priorities. By default, // These are manually sorted for better priorities. By default,
// priority is determined by the size of the target range (smaller // priority is determined by the size of the target range (smaller
// target wins). If the ranges are equal, position in this list is // target wins). If the ranges are equal, position in this list is

View file

@ -390,6 +390,36 @@ fn main() {
) )
} }
#[test]
fn doctest_convert_from_to_tryfrom() {
check_doc_test(
"convert_from_to_tryfrom",
r#####"
//- minicore: from
impl $0From<usize> for Thing {
fn from(val: usize) -> Self {
Thing {
b: val.to_string(),
a: val
}
}
}
"#####,
r#####"
impl TryFrom<usize> for Thing {
type Error = ${0:()};
fn try_from(val: usize) -> Result<Self, Self::Error> {
Ok(Thing {
b: val.to_string(),
a: val
})
}
}
"#####,
)
}
#[test] #[test]
fn doctest_convert_if_to_bool_then() { fn doctest_convert_if_to_bool_then() {
check_doc_test( check_doc_test(
@ -1820,13 +1850,13 @@ fn print(_: &str) {}
fn bar() { fn bar() {
{ {
let word = "안녕하세요"; let word: &str = "안녕하세요";
if !word.is_empty() { if !word.is_empty() {
print(word); print(word);
} }
}; };
{ {
let word = "여러분"; let word: &str = "여러분";
if !word.is_empty() { if !word.is_empty() {
print(word); print(word);
} }
@ -3151,3 +3181,22 @@ fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
"#####, "#####,
) )
} }
#[test]
fn doctest_wrap_unwrap_cfg_attr() {
check_doc_test(
"wrap_unwrap_cfg_attr",
r#####"
#[derive$0(Debug)]
struct S {
field: i32
}
"#####,
r#####"
#[cfg_attr($0, derive(Debug))]
struct S {
field: i32
}
"#####,
)
}

View file

@ -71,7 +71,7 @@ pub fn extract_trivial_expression(block_expr: &ast::BlockExpr) -> Option<ast::Ex
/// ///
/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
/// but it's better than not to have the runnables for the tests at all. /// but it's better than not to have the runnables for the tests at all.
pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { pub fn test_related_attribute_syn(fn_def: &ast::Fn) -> Option<ast::Attr> {
fn_def.attrs().find_map(|attr| { fn_def.attrs().find_map(|attr| {
let path = attr.path()?; let path = attr.path()?;
let text = path.syntax().text().to_string(); let text = path.syntax().text().to_string();
@ -83,6 +83,19 @@ pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
}) })
} }
pub fn has_test_related_attribute(attrs: &hir::AttrsWithOwner) -> bool {
attrs.iter().any(|attr| {
let path = attr.path();
(|| {
Some(
path.segments().first()?.as_text()?.starts_with("test")
|| path.segments().last()?.as_text()?.ends_with("test"),
)
})()
.unwrap_or_default()
})
}
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum IgnoreAssocItems { pub enum IgnoreAssocItems {
DocHiddenAttrPresent, DocHiddenAttrPresent,

View file

@ -9,6 +9,7 @@ use ide_db::{
ty_filter::TryEnum, ty_filter::TryEnum,
SnippetCap, SnippetCap,
}; };
use stdx::never;
use syntax::{ use syntax::{
ast::{self, make, AstNode, AstToken}, ast::{self, make, AstNode, AstToken},
SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR}, SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR},
@ -319,7 +320,9 @@ fn build_postfix_snippet_builder<'ctx>(
) -> Option<impl Fn(&str, &str, &str) -> Builder + 'ctx> { ) -> Option<impl Fn(&str, &str, &str) -> Builder + 'ctx> {
let receiver_range = ctx.sema.original_range_opt(receiver.syntax())?.range; let receiver_range = ctx.sema.original_range_opt(receiver.syntax())?.range;
if ctx.source_range().end() < receiver_range.start() { if ctx.source_range().end() < receiver_range.start() {
// This shouldn't happen, yet it does. I assume this might be due to an incorrect token mapping. // This shouldn't happen, yet it does. I assume this might be due to an incorrect token
// mapping.
never!();
return None; return None;
} }
let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end()); let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());

View file

@ -226,7 +226,7 @@ pub(crate) fn complete_ascribed_type(
if !path_ctx.is_trivial_path() { if !path_ctx.is_trivial_path() {
return None; return None;
} }
let x = match ascription { let ty = match ascription {
TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => { TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
ctx.sema.type_of_pat(pat.as_ref()?) ctx.sema.type_of_pat(pat.as_ref()?)
} }
@ -235,7 +235,9 @@ pub(crate) fn complete_ascribed_type(
} }
}? }?
.adjusted(); .adjusted();
let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?; if !ty.is_unknown() {
acc.add(render_type_inference(ty_string, ctx)); let ty_string = ty.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
acc.add(render_type_inference(ty_string, ctx));
}
None None
} }

View file

@ -17,7 +17,7 @@ use ide_db::{
}; };
use syntax::{ use syntax::{
ast::{self, AttrKind, NameOrNameRef}, ast::{self, AttrKind, NameOrNameRef},
AstNode, SmolStr, AstNode, Edition, SmolStr,
SyntaxKind::{self, *}, SyntaxKind::{self, *},
SyntaxToken, TextRange, TextSize, T, SyntaxToken, TextRange, TextSize, T,
}; };
@ -667,7 +667,8 @@ impl<'a> CompletionContext<'a> {
let file_with_fake_ident = { let file_with_fake_ident = {
let parse = db.parse(file_id); let parse = db.parse(file_id);
let edit = Indel::insert(offset, COMPLETION_MARKER.to_owned()); let edit = Indel::insert(offset, COMPLETION_MARKER.to_owned());
parse.reparse(&edit).tree() // FIXME: Edition
parse.reparse(&edit, Edition::CURRENT).tree()
}; };
// always pick the token to the immediate left of the cursor, as that is what we are actually // always pick the token to the immediate left of the cursor, as that is what we are actually

View file

@ -200,7 +200,7 @@ fn validate_snippet(
) -> Option<(Box<[GreenNode]>, String, Option<Box<str>>)> { ) -> Option<(Box<[GreenNode]>, String, Option<Box<str>>)> {
let mut imports = Vec::with_capacity(requires.len()); let mut imports = Vec::with_capacity(requires.len());
for path in requires.iter() { for path in requires.iter() {
let use_path = ast::SourceFile::parse(&format!("use {path};")) let use_path = ast::SourceFile::parse(&format!("use {path};"), syntax::Edition::CURRENT)
.syntax_node() .syntax_node()
.descendants() .descendants()
.find_map(ast::Path::cast)?; .find_map(ast::Path::cast)?;

View file

@ -19,7 +19,7 @@ struct Foo<'lt, T, const C: usize> where $0 {}
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo<> Foo<'static, {unknown}, _> st Foo<> Foo<'{error}, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
@ -92,7 +92,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
st Foo<> Foo<'static, {unknown}, _> st Foo<> Foo<'{error}, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit

View file

@ -20,8 +20,8 @@ struct Foo<'lt, T, const C: usize> {
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
sp Self Foo<'static, {unknown}, _> sp Self Foo<'{error}, {unknown}, _>
st Foo<> Foo<'static, {unknown}, _> st Foo<> Foo<'{error}, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit
@ -45,8 +45,8 @@ struct Foo<'lt, T, const C: usize>(f$0);
en Enum Enum en Enum Enum
ma makro!() macro_rules! makro ma makro!() macro_rules! makro
md module md module
sp Self Foo<'static, {unknown}, _> sp Self Foo<'{error}, {unknown}, _>
st Foo<> Foo<'static, {unknown}, _> st Foo<> Foo<'{error}, {unknown}, _>
st Record Record st Record Record
st Tuple Tuple st Tuple Tuple
st Unit Unit st Unit Unit

View file

@ -26,6 +26,7 @@ indexmap.workspace = true
memchr = "2.6.4" memchr = "2.6.4"
triomphe.workspace = true triomphe.workspace = true
nohash-hasher.workspace = true nohash-hasher.workspace = true
bitflags.workspace = true
# local deps # local deps
base-db.workspace = true base-db.workspace = true

View file

@ -91,82 +91,101 @@ impl RootDatabase {
crate::symbol_index::LocalRootsQuery crate::symbol_index::LocalRootsQuery
crate::symbol_index::LibraryRootsQuery crate::symbol_index::LibraryRootsQuery
// HirDatabase // HirDatabase
hir::db::MirBodyQuery
hir::db::BorrowckQuery
hir::db::TyQuery
hir::db::ValueTyQuery
hir::db::ImplSelfTyQuery
hir::db::ConstParamTyQuery
hir::db::ConstEvalQuery
hir::db::ConstEvalDiscriminantQuery
hir::db::ImplTraitQuery
hir::db::FieldTypesQuery
hir::db::LayoutOfAdtQuery
hir::db::TargetDataLayoutQuery
hir::db::CallableItemSignatureQuery
hir::db::ReturnTypeImplTraitsQuery
hir::db::GenericPredicatesForParamQuery
hir::db::GenericPredicatesQuery
hir::db::TraitEnvironmentQuery
hir::db::GenericDefaultsQuery
hir::db::InherentImplsInCrateQuery
hir::db::InherentImplsInBlockQuery
hir::db::IncoherentInherentImplCratesQuery
hir::db::TraitImplsInCrateQuery
hir::db::TraitImplsInBlockQuery
hir::db::TraitImplsInDepsQuery
hir::db::InternCallableDefQuery
hir::db::InternLifetimeParamIdQuery
hir::db::InternImplTraitIdQuery
hir::db::InternTypeOrConstParamIdQuery
hir::db::InternClosureQuery
hir::db::InternCoroutineQuery
hir::db::AssociatedTyDataQuery
hir::db::TraitDatumQuery
hir::db::AdtDatumQuery hir::db::AdtDatumQuery
hir::db::ImplDatumQuery hir::db::AdtVarianceQuery
hir::db::AssociatedTyDataQuery
hir::db::AssociatedTyValueQuery
hir::db::BorrowckQuery
hir::db::CallableItemSignatureQuery
hir::db::ConstEvalDiscriminantQuery
hir::db::ConstEvalQuery
hir::db::ConstEvalStaticQuery
hir::db::ConstParamTyQuery
hir::db::FieldTypesQuery
hir::db::FnDefDatumQuery hir::db::FnDefDatumQuery
hir::db::FnDefVarianceQuery hir::db::FnDefVarianceQuery
hir::db::AdtVarianceQuery hir::db::GenericDefaultsQuery
hir::db::AssociatedTyValueQuery hir::db::GenericPredicatesForParamQuery
hir::db::GenericPredicatesQuery
hir::db::ImplDatumQuery
hir::db::ImplSelfTyQuery
hir::db::ImplTraitQuery
hir::db::IncoherentInherentImplCratesQuery
hir::db::InherentImplsInBlockQuery
hir::db::InherentImplsInCrateQuery
hir::db::InternCallableDefQuery
hir::db::InternClosureQuery
hir::db::InternCoroutineQuery
hir::db::InternImplTraitIdQuery
hir::db::InternLifetimeParamIdQuery
hir::db::InternTypeOrConstParamIdQuery
hir::db::LayoutOfAdtQuery
hir::db::MirBodyQuery
hir::db::ProgramClausesForChalkEnvQuery hir::db::ProgramClausesForChalkEnvQuery
hir::db::ReturnTypeImplTraitsQuery
hir::db::TargetDataLayoutQuery
hir::db::TraitDatumQuery
hir::db::TraitEnvironmentQuery
hir::db::TraitImplsInBlockQuery
hir::db::TraitImplsInCrateQuery
hir::db::TraitImplsInDepsQuery
hir::db::TyQuery
hir::db::ValueTyQuery
// DefDatabase // DefDatabase
hir::db::FileItemTreeQuery hir::db::AttrsQuery
hir::db::BlockDefMapQuery hir::db::BlockDefMapQuery
hir::db::StructDataWithDiagnosticsQuery hir::db::BlockItemTreeQuery
hir::db::UnionDataWithDiagnosticsQuery hir::db::BodyQuery
hir::db::BodyWithSourceMapQuery
hir::db::ConstDataQuery
hir::db::ConstVisibilityQuery
hir::db::CrateDefMapQuery
hir::db::CrateLangItemsQuery
hir::db::CrateNotableTraitsQuery
hir::db::CrateSupportsNoStdQuery
hir::db::EnumDataQuery hir::db::EnumDataQuery
hir::db::EnumVariantDataWithDiagnosticsQuery hir::db::EnumVariantDataWithDiagnosticsQuery
hir::db::ImplDataWithDiagnosticsQuery hir::db::ExprScopesQuery
hir::db::TraitDataWithDiagnosticsQuery hir::db::ExternCrateDeclDataQuery
hir::db::TraitAliasDataQuery hir::db::FieldVisibilitiesQuery
hir::db::TypeAliasDataQuery hir::db::FieldsAttrsQuery
hir::db::FieldsAttrsSourceMapQuery
hir::db::FileItemTreeQuery
hir::db::FunctionDataQuery hir::db::FunctionDataQuery
hir::db::ConstDataQuery hir::db::FunctionVisibilityQuery
hir::db::StaticDataQuery hir::db::GenericParamsQuery
hir::db::ImplDataWithDiagnosticsQuery
hir::db::ImportMapQuery
hir::db::InternAnonymousConstQuery
hir::db::InternBlockQuery
hir::db::InternConstQuery
hir::db::InternEnumQuery
hir::db::InternExternBlockQuery
hir::db::InternExternCrateQuery
hir::db::InternFunctionQuery
hir::db::InternImplQuery
hir::db::InternInTypeConstQuery
hir::db::InternMacro2Query
hir::db::InternMacroRulesQuery
hir::db::InternProcMacroQuery
hir::db::InternStaticQuery
hir::db::InternStructQuery
hir::db::InternTraitAliasQuery
hir::db::InternTraitQuery
hir::db::InternTypeAliasQuery
hir::db::InternUnionQuery
hir::db::InternUseQuery
hir::db::LangItemQuery
hir::db::Macro2DataQuery hir::db::Macro2DataQuery
hir::db::MacroRulesDataQuery hir::db::MacroRulesDataQuery
hir::db::ProcMacroDataQuery hir::db::ProcMacroDataQuery
hir::db::BodyWithSourceMapQuery hir::db::StaticDataQuery
hir::db::BodyQuery hir::db::StructDataWithDiagnosticsQuery
hir::db::ExprScopesQuery hir::db::TraitAliasDataQuery
hir::db::GenericParamsQuery hir::db::TraitDataWithDiagnosticsQuery
hir::db::FieldsAttrsQuery hir::db::TypeAliasDataQuery
hir::db::FieldsAttrsSourceMapQuery hir::db::UnionDataWithDiagnosticsQuery
hir::db::AttrsQuery
hir::db::CrateLangItemsQuery
hir::db::LangItemQuery
hir::db::ImportMapQuery
hir::db::FieldVisibilitiesQuery
hir::db::FunctionVisibilityQuery
hir::db::ConstVisibilityQuery
hir::db::CrateSupportsNoStdQuery
hir::db::ExternCrateDeclDataQuery
hir::db::InternAnonymousConstQuery
hir::db::InternExternCrateQuery
hir::db::InternInTypeConstQuery
hir::db::InternUseQuery
// InternDatabase // InternDatabase
hir::db::InternFunctionQuery hir::db::InternFunctionQuery
@ -192,9 +211,10 @@ impl RootDatabase {
hir::db::InternMacroCallQuery hir::db::InternMacroCallQuery
hir::db::InternSyntaxContextQuery hir::db::InternSyntaxContextQuery
hir::db::MacroArgQuery hir::db::MacroArgQuery
hir::db::ParseMacroExpansionErrorQuery
hir::db::ParseMacroExpansionQuery hir::db::ParseMacroExpansionQuery
hir::db::RealSpanMapQuery
hir::db::ProcMacrosQuery hir::db::ProcMacrosQuery
hir::db::RealSpanMapQuery
// LineIndexDatabase // LineIndexDatabase
crate::LineIndexQuery crate::LineIndexQuery

View file

@ -91,8 +91,10 @@ pub fn docs_with_rangemap(
db: &dyn DefDatabase, db: &dyn DefDatabase,
attrs: &AttrsWithOwner, attrs: &AttrsWithOwner,
) -> Option<(Documentation, DocsRangeMap)> { ) -> Option<(Documentation, DocsRangeMap)> {
let docs = let docs = attrs
attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id))); .by_key("doc")
.attrs()
.filter_map(|attr| attr.string_value_unescape().map(|s| (s, attr.id)));
let indent = doc_indent(attrs); let indent = doc_indent(attrs);
let mut buf = String::new(); let mut buf = String::new();
let mut mapping = Vec::new(); let mut mapping = Vec::new();
@ -132,7 +134,7 @@ pub fn docs_with_rangemap(
} }
pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option<String> { pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option<String> {
let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value()); let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value_unescape());
let indent = doc_indent(attrs); let indent = doc_indent(attrs);
let mut buf = String::new(); let mut buf = String::new();
for doc in docs { for doc in docs {
@ -270,10 +272,9 @@ fn doc_indent(attrs: &hir::Attrs) -> usize {
attrs attrs
.by_key("doc") .by_key("doc")
.attrs() .attrs()
.filter_map(|attr| attr.string_value()) .filter_map(|attr| attr.string_value()) // no need to use unescape version here
.flat_map(|s| s.lines()) .flat_map(|s| s.lines())
.filter(|line| !line.chars().all(|c| c.is_whitespace())) .filter_map(|line| line.chars().position(|c| !c.is_whitespace()))
.map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
.min() .min()
.unwrap_or(0) .unwrap_or(0)
} }

Some files were not shown because too many files have changed in this diff Show more