Unnamed repository; edit this file 'description' to name the repository.
553 files changed, 21860 insertions, 11877 deletions
diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 0000000000..47e5bf53d0 --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,2 @@ +[profile.default] +slow-timeout = { period = "60s", terminate-after = 5 } diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c27d84fb0b..b9427e1927 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -120,7 +120,7 @@ jobs: run: cargo codegen --check - name: Run tests - run: cargo nextest run --no-fail-fast --hide-progress-bar --status-level fail + run: cargo nextest run --no-fail-fast --hide-progress-bar - name: Install cargo-machete uses: taiki-e/install-action@cargo-machete diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 9460c6a3c7..55edbbefba 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -12,19 +12,17 @@ env: jobs: coverage: runs-on: ubuntu-latest + env: + RUSTC_BOOTSTRAP: 1 steps: - uses: actions/checkout@v6 - name: Install Rust toolchain run: | - rustup update --no-self-update nightly - rustup default nightly - rustup component add --toolchain nightly rust-src rustc-dev rustfmt - # We also install a nightly rustfmt, because we use `--file-lines` in - # a test. - rustup toolchain install nightly --profile minimal --component rustfmt - - rustup toolchain install nightly --component llvm-tools-preview + rustup update --no-self-update stable + rustup default stable + rustup component add --toolchain stable rust-src rustc-dev rustfmt + rustup toolchain install stable --component llvm-tools-preview - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov diff --git a/.gitignore b/.gitignore index 7192e685e2..3beabe39bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target/ +vendor/ /dist/ **/*.rs.bk **/*.rs.pending-snap diff --git a/.typos.toml b/.typos.toml index e954b08fb1..873daa3bf3 100644 --- a/.typos.toml +++ b/.typos.toml @@ -34,6 +34,7 @@ thir = "thir" jod = "jod" tructure = "tructure" taits = "taits" +inh = "inh" [default.extend-identifiers] anc = "anc" diff --git a/Cargo.lock b/Cargo.lock index e6575c28c1..cbbeef0030 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -648,6 +648,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] name = "form_urlencoded" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -734,7 +740,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -744,6 +750,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] name = "hashlink" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -876,6 +893,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", + "bitflags 2.9.4", "cov-mark", "either", "ena", @@ -1213,6 +1231,7 @@ dependencies = [ name = "intern" version = "0.0.0" dependencies = [ + "arrayvec", "dashmap", "hashbrown 0.14.5", "rayon", @@ -1231,9 +1250,9 @@ dependencies = [ [[package]] name = "inventory" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" dependencies = [ "rustversion", ] @@ -1924,7 +1943,7 @@ dependencies = [ "libc", "perf-event", "tikv-jemalloc-ctl", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -2048,9 +2067,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "ra-ap-rustc_abi" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b917ab47d7036977be4c984321af3e0de089229404d68ea9a286f50aa464697" +checksum = "2f25a779e21ca3bba6795193b16508c8ab159f96ee4b07349893fd272065b525" dependencies = [ "bitflags 2.9.4", "ra-ap-rustc_hashes", @@ -2060,33 +2079,33 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021d80bea67458b8c90cc25bfdca6f911ea818a41905e370c1f310cced1dd07e" +checksum = "0218ca6c7b096466e85a497e6150c39be5b7bc36637fe62c1cd20370a9d9aac7" [[package]] name = "ra-ap-rustc_hashes" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb89395306ecfc980d252f77a4038d8b8bb578a25c856b545cbeeb3fde8358e" +checksum = "d6b410bacf1a7c8038f376fa6283003784d568ac012e35fc0aeefa9a5ab11a2e" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84219d028a1954c4340ddde11adffe93eb83e476e942718fe926f4d99637cbbe" +checksum = "2271b55e4a5d0cc0cbe9bdf8056c07ac69e32919a48ce66722ed0526d62588c3" dependencies = [ "ra-ap-rustc_index_macros", ] [[package]] name = "ra-ap-rustc_index_macros" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3908fdfa258c663d8ee407e6b4a205b0880e323b533c0df7edceafbd54a02fb6" +checksum = "b6a89e743fb881a1e13544e3395a5ad9ad9280d56384256a121066119abd7af2" dependencies = [ "proc-macro2", "quote", @@ -2095,9 +2114,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b50f19d5856b8e2b36150e89b53a6102ab096e8044e1f55fd6fef977b10d85" +checksum = "a6d7c9cc05e0e6b72a214a455a106d9b22b0494164d50a657b17bd319534c218" dependencies = [ "memchr", "unicode-ident", @@ -2106,9 +2125,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f83dcc451bcee8a99e284a583d5b3d82db5a200107a256a40ef132c4988f1b" +checksum = "cb3017c2f0ace80b8e6068b9c613aa56ed50e0374bf44a891447511f1264e40d" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -2119,9 +2138,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31236bdc6cbcae8af42d0b2db2fa8d812a8715b90a2ba5afb1132b37a4d0bbc" +checksum = "4a737f844bdef8ac5ab54dadf2f34704b4d06beef9236d71080bb34db697220b" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.7", @@ -2129,9 +2148,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fc4edac740e896fba4b3b4d9c423083e3eac49947732561ddfb2377e1f57829" +checksum = "6de3d4c7d6078cce3c40c55717b8b15002a80b9fa8849faea496a365324861b4" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -2142,9 +2161,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8efa119afc1bcadd821b27aa94332abf79c48ac0a972cb78932f63004ba4cdd9" +checksum = "8c5d9a4d3e7bee7313599bc6d794037247ac0165f03857379cf4fc3097199e05" dependencies = [ "arrayvec", "bitflags 2.9.4", @@ -2163,9 +2182,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.160.0" +version = "0.165.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b1dc03abfabc7179393c282892c73a3f0e4bbd5f0b6c87ff42c2b142e68f57" +checksum = "024598d1f54272acd83d28c121f8a2e82e216dd7be1e40158b66b2d12fa214c0" dependencies = [ "proc-macro2", "quote", @@ -2355,7 +2374,7 @@ dependencies = [ "vfs", "vfs-notify", "walkdir", - "windows-sys 0.60.2", + "windows-sys 0.61.0", "xflags", "xshell", ] @@ -2454,14 +2473,14 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" -version = "0.26.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77debccd43ba198e9cee23efd7f10330ff445e46a98a2b107fed9094a1ee676" +checksum = "4612ff789805e65c87e9b38cb749a293212a615af065bed8a2001086801498c3" dependencies = [ "boxcar", "crossbeam-queue", "crossbeam-utils", - "hashbrown 0.15.5", + "hashbrown 0.17.0", "hashlink", "indexmap", "intrusive-collections", @@ -2475,19 +2494,20 @@ dependencies = [ "smallvec", "thin-vec", "tracing", + "typeid", ] [[package]] name = "salsa-macro-rules" -version = "0.26.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea07adbf42d91cc076b7daf3b38bc8168c19eb362c665964118a89bc55ef19a5" +checksum = "58e354cbac6939b9b09cd9c11fb419a53e64b4a0f755d929f56a09f4cc752e41" [[package]] name = "salsa-macros" -version = "0.26.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d4d8b66451b9c75ddf740b7fc8399bc7b8ba33e854a5d7526d18708f67b05" +checksum = "3067861075c2b80608f84ad49fb88f2c7610b94cdf8b4201e79ddee87f8980c8" dependencies = [ "proc-macro2", "quote", @@ -2694,7 +2714,7 @@ dependencies = [ "libc", "miow", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -2744,6 +2764,7 @@ dependencies = [ name = "syntax-bridge" version = "0.0.0" dependencies = [ + "expect-test", "intern", "parser", "rustc-hash 2.1.1", @@ -3109,6 +3130,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] name = "unarray" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index b8dedc6c50..a08b2e412e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.91" +rust-version = "1.95" edition = "2024" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] @@ -86,14 +86,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.160", default-features = false } -ra-ap-rustc_parse_format = { version = "0.160", default-features = false } -ra-ap-rustc_index = { version = "0.160", default-features = false } -ra-ap-rustc_abi = { version = "0.160", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.160", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.160", default-features = false } -ra-ap-rustc_type_ir = { version = "0.160", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.160", default-features = false } +ra-ap-rustc_lexer = { version = "0.165", default-features = false } +ra-ap-rustc_parse_format = { version = "0.165", default-features = false } +ra-ap-rustc_index = { version = "0.165", default-features = false } +ra-ap-rustc_abi = { version = "0.165", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.165", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.165", default-features = false } +ra-ap-rustc_type_ir = { version = "0.165", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.165", default-features = false } # local crates that aren't published to crates.io. These should not have versions. @@ -135,13 +135,13 @@ rayon = "1.10.0" rowan = "=0.15.18" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { version = "0.26", default-features = false, features = [ +salsa = { version = "0.26.2", default-features = false, features = [ "rayon", "salsa_unstable", "macros", "inventory", ] } -salsa-macros = "0.26" +salsa-macros = "0.26.2" semver = "1.0.26" serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" } diff --git a/bench_data/glorious_old_parser b/bench_data/glorious_old_parser index 5022514924..a6de9daa86 100644 --- a/bench_data/glorious_old_parser +++ b/bench_data/glorious_old_parser @@ -1,4 +1,4 @@ -//- minicore: fn +//- minicore: fn, iterator, try, panic use crate::ast::{AngleBracketedArgs, ParenthesizedArgs, AttrStyle, BareFnTy}; use crate::ast::{GenericBound, TraitBoundModifier}; use crate::ast::Unsafety; diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index e438505c07..a209a0e631 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -37,7 +37,7 @@ use rustc_hash::{FxHashSet, FxHasher}; use salsa::{Durability, Setter}; pub use semver::{BuildMetadata, Prerelease, Version, VersionReq}; use triomphe::Arc; -pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}; +pub use vfs::{AbsPathBuf, AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}; pub type FxIndexSet<T> = indexmap::IndexSet<T, rustc_hash::FxBuildHasher>; pub type FxIndexMap<K, V> = diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs index 5cf5a9b6be..90fae16ccd 100644 --- a/crates/hir-def/src/attrs.rs +++ b/crates/hir-def/src/attrs.rs @@ -137,8 +137,10 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE), "lang" => attr_flags.insert(AttrFlags::LANG_ITEM), + "must_use" => attr_flags.insert(AttrFlags::IS_MUST_USE), "path" => attr_flags.insert(AttrFlags::HAS_PATH), "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), + "rustc_reservation_impl" => attr_flags.insert(AttrFlags::RUSTC_RESERVATION_IMPL), "export_name" => { if let Some(value) = attr.value_string() && *value == *"main" @@ -227,6 +229,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), "macro_export" => attr_flags.insert(AttrFlags::IS_MACRO_EXPORT), + "must_use" => attr_flags.insert(AttrFlags::IS_MUST_USE), "no_mangle" => attr_flags.insert(AttrFlags::NO_MANGLE), "pointee" => attr_flags.insert(AttrFlags::IS_POINTEE), "non_exhaustive" => attr_flags.insert(AttrFlags::NON_EXHAUSTIVE), @@ -263,6 +266,12 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow< } _ => {} }, + "diagnostic" => match &*second_segment { + "do_not_recommend" => { + attr_flags.insert(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND) + } + _ => {} + }, _ => {} }, } @@ -335,6 +344,10 @@ bitflags::bitflags! { const MACRO_STYLE_PARENTHESES = 1 << 48; const PREFER_UNDERSCORE_IMPORT = 1 << 49; + + const IS_MUST_USE = 1 << 50; + + const DIAGNOSTIC_DO_NOT_RECOMMEND = 1 << 51; } } @@ -724,52 +737,56 @@ impl AttrFlags { return None; } - return repr(db, owner); + Self::repr_assume_has(db, owner) + } - #[salsa::tracked] - fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option<ReprOptions> { - let mut result = None; - collect_attrs::<Infallible>(db, owner.into(), |attr| { - let mut current = None; - if let ast::Meta::TokenTreeMeta(attr) = &attr - && let Some(path) = attr.path() - && let Some(tt) = attr.token_tree() + /// Only call this when you've verified the type indeed has a `#[repr]` attribute! + /// + /// Prefer [`AttrFlags::repr()`] in non-perf-sensitive places as it also has a check that + /// that the ADT has repr. + #[salsa::tracked] + pub fn repr_assume_has(db: &dyn DefDatabase, owner: AdtId) -> Option<ReprOptions> { + let mut result = None; + collect_attrs::<Infallible>(db, owner.into(), |attr| { + let mut current = None; + if let ast::Meta::TokenTreeMeta(attr) = &attr + && let Some(path) = attr.path() + && let Some(tt) = attr.token_tree() + { + if path.is1("repr") + && let Some(repr) = parse_repr_tt(&tt) { - if path.is1("repr") - && let Some(repr) = parse_repr_tt(&tt) - { - current = Some(repr); - } else if path.is1("rustc_scalable_vector") - && let mut tt = TokenTreeChildren::new(&tt) - && let Some(NodeOrToken::Token(scalable)) = tt.next() - && let Some(scalable) = ast::IntNumber::cast(scalable) - && let Ok(scalable) = scalable.value() - && let Ok(scalable) = scalable.try_into() - { - current = Some(ReprOptions { - scalable: Some(rustc_abi::ScalableElt::ElementCount(scalable)), - ..ReprOptions::default() - }); - } - } else if let ast::Meta::PathMeta(attr) = &attr - && attr.path().is1("rustc_scalable_vector") + current = Some(repr); + } else if path.is1("rustc_scalable_vector") + && let mut tt = TokenTreeChildren::new(&tt) + && let Some(NodeOrToken::Token(scalable)) = tt.next() + && let Some(scalable) = ast::IntNumber::cast(scalable) + && let Ok(scalable) = scalable.value() + && let Ok(scalable) = scalable.try_into() { current = Some(ReprOptions { - scalable: Some(rustc_abi::ScalableElt::Container), + scalable: Some(rustc_abi::ScalableElt::ElementCount(scalable)), ..ReprOptions::default() }); } + } else if let ast::Meta::PathMeta(attr) = &attr + && attr.path().is1("rustc_scalable_vector") + { + current = Some(ReprOptions { + scalable: Some(rustc_abi::ScalableElt::Container), + ..ReprOptions::default() + }); + } - if let Some(current) = current { - match &mut result { - Some(existing) => merge_repr(existing, current), - None => result = Some(current), - } + if let Some(current) = current { + match &mut result { + Some(existing) => merge_repr(existing, current), + None => result = Some(current), } - ControlFlow::Continue(()) - }); - result - } + } + ControlFlow::Continue(()) + }); + result } /// Call this only if there are legacy const generics, to save memory. @@ -1143,6 +1160,28 @@ impl AttrFlags { }) } } + + /// Returns `None` if there is no `#[must_use]`, `Some(None)` if there is a `#[must_use]` without a message, + /// and `Some(Some(message))` if there is a `#[must_use]` with a message. + pub fn must_use_message(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Option<&str>> { + if !AttrFlags::query(db, owner).contains(AttrFlags::IS_MUST_USE) { + return None; + } + return Some(must_use_message(db, owner)); + + #[salsa::tracked(returns(as_deref))] + fn must_use_message(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Box<str>> { + collect_attrs(db, owner, |attr| { + if let ast::Meta::KeyValueMeta(attr) = attr + && attr.path().is1("must_use") + && let Some(message) = attr.value_string() + { + return ControlFlow::Break(Box::from(&*message)); + } + ControlFlow::Continue(()) + }) + } + } } fn merge_repr(this: &mut ReprOptions, other: ReprOptions) { diff --git a/crates/hir-def/src/attrs/docs.rs b/crates/hir-def/src/attrs/docs.rs index 9a715b1968..0d01d54786 100644 --- a/crates/hir-def/src/attrs/docs.rs +++ b/crates/hir-def/src/attrs/docs.rs @@ -333,7 +333,7 @@ struct DocExprSourceCtx<'db> { resolver: Resolver<'db>, file_id: HirFileId, ast_id_map: &'db AstIdMap, - span_map: SpanMap, + span_map: SpanMap<'db>, } fn expand_doc_expr_via_macro_pipeline<'db>( @@ -390,7 +390,7 @@ fn expand_doc_macro_call<'db>( .value?; expander.recursion_depth += 1; - let parse = expander.db.parse_macro_expansion(call_id).value.0; + let parse = expander.db.parse_macro_expansion(call_id).value.0.clone(); let expr = parse.cast::<ast::Expr>().map(|parse| parse.tree())?; expander.recursion_depth -= 1; diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 9dd7768ead..11e5c54246 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -7,87 +7,22 @@ use hir_expand::{ use triomphe::Arc; use crate::{ - AnonConstId, AnonConstLoc, AssocItemId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, - EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, - ExternCrateLoc, FunctionId, FunctionLoc, ImplId, ImplLoc, Macro2Id, Macro2Loc, MacroExpander, - MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId, - StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, - UnionLoc, UseId, UseLoc, + AssocItemId, AttrDefId, Macro2Loc, MacroExpander, MacroId, MacroRulesLoc, MacroRulesLocFlags, + TraitId, attrs::AttrFlags, - item_tree::{ItemTree, file_item_tree_query}, + item_tree::{ItemTree, file_item_tree}, nameres::crate_def_map, visibility::{self, Visibility}, }; -use salsa::plumbing::AsId; - -#[query_group::query_group(InternDatabaseStorage)] -pub trait InternDatabase: SourceDatabase { - // region: items - #[salsa::interned] - fn intern_use(&self, loc: UseLoc) -> UseId; - - #[salsa::interned] - fn intern_extern_crate(&self, loc: ExternCrateLoc) -> ExternCrateId; - - #[salsa::interned] - fn intern_function(&self, loc: FunctionLoc) -> FunctionId; - - #[salsa::interned] - fn intern_struct(&self, loc: StructLoc) -> StructId; - - #[salsa::interned] - fn intern_union(&self, loc: UnionLoc) -> UnionId; - - #[salsa::interned] - fn intern_enum(&self, loc: EnumLoc) -> EnumId; - - #[salsa::interned] - fn intern_enum_variant(&self, loc: EnumVariantLoc) -> EnumVariantId; - - #[salsa::interned] - fn intern_const(&self, loc: ConstLoc) -> ConstId; - - #[salsa::interned] - fn intern_static(&self, loc: StaticLoc) -> StaticId; - - #[salsa::interned] - fn intern_anon_const(&self, loc: AnonConstLoc) -> AnonConstId; - - #[salsa::interned] - fn intern_trait(&self, loc: TraitLoc) -> TraitId; - - #[salsa::interned] - fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId; - - #[salsa::interned] - fn intern_impl(&self, loc: ImplLoc) -> ImplId; - - #[salsa::interned] - fn intern_extern_block(&self, loc: ExternBlockLoc) -> ExternBlockId; - - #[salsa::interned] - fn intern_macro2(&self, loc: Macro2Loc) -> Macro2Id; - - #[salsa::interned] - fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId; - - #[salsa::interned] - fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId; - // endregion: items - - #[salsa::interned] - fn intern_block(&self, loc: BlockLoc) -> BlockId; -} - #[query_group::query_group] -pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { +pub trait DefDatabase: ExpandDatabase + SourceDatabase { /// Whether to expand procedural macros during name resolution. #[salsa::input] fn expand_proc_attr_macros(&self) -> bool; /// Computes an [`ItemTree`] for the given file or macro expansion. - #[salsa::invoke(file_item_tree_query)] + #[salsa::invoke(file_item_tree)] #[salsa::transparent] fn file_item_tree(&self, file_id: HirFileId, krate: Crate) -> &ItemTree; @@ -122,11 +57,7 @@ fn include_macro_invoc( .modules .values() .flat_map(|m| m.scope.iter_macro_invoc()) - .filter_map(|invoc| { - db.lookup_intern_macro_call(*invoc.1) - .include_file_id(db, *invoc.1) - .map(|x| (*invoc.1, x)) - }) + .filter_map(|invoc| invoc.1.loc(db).include_file_id(db, *invoc.1).map(|x| (*invoc.1, x))) .collect() } diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs index 497ed7d37f..4dc7267231 100644 --- a/crates/hir-def/src/expr_store.rs +++ b/crates/hir-def/src/expr_store.rs @@ -9,7 +9,10 @@ pub mod scope; #[cfg(test)] mod tests; -use std::ops::{Deref, Index}; +use std::{ + borrow::Borrow, + ops::{Deref, Index}, +}; use cfg::{CfgExpr, CfgOptions}; use either::Either; @@ -25,14 +28,18 @@ use tt::TextRange; use crate::{ AdtId, BlockId, ExpressionStoreOwnerId, GenericDefId, SyntheticSyntax, db::DefDatabase, - expr_store::path::Path, + expr_store::path::{AssociatedTypeBinding, GenericArg, GenericArgs, NormalPath, Path}, hir::{ - Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, - PatId, RecordFieldPat, RecordSpread, Statement, + Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, InlineAsm, Label, + LabelId, MatchArm, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, + Statement, }, nameres::{DefMap, block_def_map}, signatures::VariantFields, - type_ref::{LifetimeRef, LifetimeRefId, PathId, TypeRef, TypeRefId}, + type_ref::{ + ArrayType, ConstRef, FnType, LifetimeRef, LifetimeRefId, PathId, RefType, TypeBound, + TypeRef, TypeRefId, UseArgRef, + }, }; pub use self::body::{Body, BodySourceMap}; @@ -91,21 +98,15 @@ pub type TypeSource = InFile<TypePtr>; pub type LifetimePtr = AstPtr<ast::Lifetime>; pub type LifetimeSource = InFile<LifetimePtr>; -/// Describes where a const expression originated from. -/// -/// Used by signature/body inference to determine the expected type for each -/// const expression root. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum RootExprOrigin { - /// Array length expression: `[T; <expr>]` — expected type is `usize`. - ArrayLength, - /// Const parameter default value: `const N: usize = <expr>`. - ConstParam(crate::hir::generics::LocalTypeOrConstParamId), - /// Const generic argument in a path: `SomeType::<{ <expr> }>` or `some_fn::<{ <expr> }>()`. - /// Determining the expected type requires path resolution, so it is deferred. - GenericArgsPath, - /// The root expression of a body. - BodyRoot, +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct ExprRoot { + root: ExprId, + // We store, for each root, the range of exprs (and pats and bindings) it holds. + // We store only the end (exclusive), since the start can be inferred from the previous + // roots or is zero. + exprs_end: ExprId, + pats_end: PatId, + bindings_end: BindingId, } // We split the store into types-only and expressions, because most stores (e.g. generics) @@ -129,7 +130,34 @@ struct ExpressionOnlyStore { ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, /// Maps expression roots to their origin. - expr_roots: SmallVec<[(ExprId, RootExprOrigin); 1]>, + /// + /// Note: while every root expr is an inference root (aka. an `AnonConst`), there could be other roots that do not appear here. + /// This can happen when anon consts are nested, for example: + /// + /// ``` + /// [ + /// (); + /// { + /// // this repeat expr is anon const #1, and *only it* appears in this list. + /// [ + /// (); + /// { + /// // this repeat expr is anon const #2. + /// 0 + /// } + /// ]; + /// 0 + /// } + /// ] + /// ``` + /// We do this because this allows us to search this list using a binary search, + /// and it does not bother us because we use this list for two things: constructing `ExprScopes`, which + /// works fine with nested exprs, and retrieving inference results, and we copy the inner const's inference + /// into the outer const. + // FIXME: Array repeat is not problematic indeed, but this could still break with exprs in types, + // which we do not visit for `ExprScopes` (they're fine for inference though). We either need to visit them, + // or use a more complicated search. + expr_roots: SmallVec<[ExprRoot; 1]>, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -243,7 +271,7 @@ pub struct ExpressionStoreBuilder { pub types: Arena<TypeRef>, block_scopes: Vec<BlockId>, ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, - pub inference_roots: Option<SmallVec<[(ExprId, RootExprOrigin); 1]>>, + inference_roots: Option<SmallVec<[ExprRoot; 1]>>, // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected). @@ -303,6 +331,7 @@ pub enum ExpressionStoreDiagnostics { UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name }, AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String }, UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name }, + PatternArgInExternFn { node: InFile<AstPtr<ast::Pat>> }, } impl ExpressionStoreBuilder { @@ -375,8 +404,8 @@ impl ExpressionStoreBuilder { let store = { let expr_only = if has_exprs { - if let Some(const_expr_origins) = &mut expr_roots { - const_expr_origins.shrink_to_fit(); + if let Some(expr_roots) = &mut expr_roots { + expr_roots.shrink_to_fit(); } Some(Box::new(ExpressionOnlyStore { exprs, @@ -386,7 +415,8 @@ impl ExpressionStoreBuilder { binding_owners, block_scopes: block_scopes.into_boxed_slice(), ident_hygiene, - expr_roots: expr_roots.unwrap_or_default(), + expr_roots: expr_roots + .expect("should always finish with a `Some(_)` expr_roots"), })) } else { None @@ -427,6 +457,14 @@ impl ExpressionStoreBuilder { } impl ExpressionStore { + const EMPTY: &ExpressionStore = + &ExpressionStore { expr_only: None, types: Arena::new(), lifetimes: Arena::new() }; + + #[inline] + pub fn empty() -> &'static ExpressionStore { + ExpressionStore::EMPTY + } + pub fn of(db: &dyn DefDatabase, def: ExpressionStoreOwnerId) -> &ExpressionStore { match def { ExpressionStoreOwnerId::Signature(def) => { @@ -516,19 +554,35 @@ impl ExpressionStore { } /// Returns all expression root `ExprId`s found in this store. - pub fn expr_roots(&self) -> impl Iterator<Item = ExprId> { - self.const_expr_origins().iter().map(|&(id, _)| id) + pub fn expr_roots(&self) -> impl DoubleEndedIterator<Item = ExprId> { + self.expr_only + .as_ref() + .map_or(&[][..], |expr_only| &expr_only.expr_roots) + .iter() + .map(|root| root.root) } - /// Like [`Self::expr_roots`], but also returns the origin - /// of each expression. - pub fn expr_roots_with_origins(&self) -> impl Iterator<Item = (ExprId, RootExprOrigin)> { - self.const_expr_origins().iter().map(|&(id, origin)| (id, origin)) + fn find_root_for( + &self, + mut get: impl FnMut(&ExprRoot) -> la_arena::RawIdx, + find: la_arena::RawIdx, + ) -> ExprId { + let expr_only = self.assert_expr_only(); + let find = find.into_u32(); + let entry = expr_only.expr_roots.partition_point(|root| get(root).into_u32() <= find); + expr_only.expr_roots[entry].root } - /// Returns the map of const expression roots to their origins. - pub fn const_expr_origins(&self) -> &[(ExprId, RootExprOrigin)] { - self.expr_only.as_ref().map_or(&[], |it| &it.expr_roots) + pub fn find_root_for_expr(&self, expr: ExprId) -> ExprId { + self.find_root_for(|root| root.exprs_end.into_raw(), expr.into_raw()) + } + + pub fn find_root_for_pat(&self, pat: PatId) -> ExprId { + self.find_root_for(|root| root.pats_end.into_raw(), pat.into_raw()) + } + + pub fn find_root_for_binding(&self, binding: BindingId) -> ExprId { + self.find_root_for(|root| root.bindings_end.into_raw(), binding.into_raw()) } /// Returns an iterator over all block expressions in this store that define inner items. @@ -552,33 +606,46 @@ impl ExpressionStore { }); } - pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) { + pub fn visit_pat_children(&self, pat_id: PatId, mut visitor: impl StoreVisitor) { + // Do not use `..` patterns or field accesses here, only destructuring, to ensure we cover all cases + // (we've had multiple bugs with this in the past). let pat = &self[pat_id]; match pat { - Pat::Range { .. } - | Pat::Lit(..) - | Pat::Path(..) - | Pat::ConstBlock(..) - | Pat::Wild - | Pat::Missing - | Pat::Expr(_) => {} - &Pat::Bind { subpat, .. } => { - if let Some(subpat) = subpat { - f(subpat); - } + Pat::Range { start, end, range_type: _ } => { + visitor.on_expr_opt(*start); + visitor.on_expr_opt(*end); } - Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => { - args.iter().copied().for_each(f); + Pat::Lit(expr) | Pat::ConstBlock(expr) | Pat::Expr(expr) => visitor.on_expr(*expr), + Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest => {} + &Pat::Bind { subpat, id: _ } => visitor.on_pat_opt(subpat), + Pat::Or(args) | Pat::Tuple { args, ellipsis: _ } => visitor.on_pats(args), + Pat::TupleStruct { args, ellipsis: _, path } => { + visitor.on_pats(args); + visitor.on_path(path); } - Pat::Ref { pat, .. } => f(*pat), + Pat::Ref { pat, mutability: _ } => visitor.on_pat(*pat), Pat::Slice { prefix, slice, suffix } => { - let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter()); - total_iter.copied().for_each(f); + visitor.on_pats(prefix); + visitor.on_pat_opt(*slice); + visitor.on_pats(suffix); } - Pat::Record { args, .. } => { - args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat)); + Pat::Record { args, ellipsis: _, path } => { + args.iter().for_each(|RecordFieldPat { pat, name: _ }| visitor.on_pat(*pat)); + visitor.on_path(path); + } + Pat::Box { inner } | Pat::Deref { inner } => visitor.on_pat(*inner), + } + } + + pub fn walk_pats_shallow(&self, pat_id: PatId, f: impl FnMut(PatId)) { + return self.visit_pat_children(pat_id, Visitor(f)); + + struct Visitor<F>(F); + + impl<F: FnMut(PatId)> StoreVisitor for Visitor<F> { + fn on_pat(&mut self, pat: PatId) { + (self.0)(pat); } - Pat::Box { inner } => f(*inner), } } @@ -604,276 +671,212 @@ impl ExpressionStore { self.expr_only.as_ref()?.binding_owners.get(&id).copied() } - /// Walks the immediate children expressions and calls `f` for each child expression. - /// - /// Note that this does not walk const blocks. - pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) { - let expr = &self[expr_id]; - match expr { - Expr::Continue { .. } - | Expr::Const(_) - | Expr::Missing - | Expr::Path(_) - | Expr::OffsetOf(_) - | Expr::Literal(_) - | Expr::Underscore => {} - Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op { - AsmOperand::In { expr, .. } - | AsmOperand::Out { expr: Some(expr), .. } - | AsmOperand::InOut { expr, .. } - | AsmOperand::Const(expr) - | AsmOperand::Label(expr) => f(*expr), - AsmOperand::SplitInOut { in_expr, out_expr, .. } => { - f(*in_expr); - if let Some(out_expr) = out_expr { - f(*out_expr); + pub fn visit_expr_children(&self, expr_id: ExprId, mut visitor: impl StoreVisitor) { + // Do not use `..` patterns or field accesses here, only destructuring, to ensure we cover all cases + // (we've had multiple bugs with this in the past). + match &self[expr_id] { + Expr::OffsetOf(OffsetOf { container, fields: _ }) => visitor.on_type(*container), + Expr::Path(path) => visitor.on_path(path), + Expr::Continue { label: _ } | Expr::Missing | Expr::Literal(_) | Expr::Underscore => {} + Expr::InlineAsm(InlineAsm { operands, options: _, kind: _ }) => { + operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, reg: _ } + | AsmOperand::Out { expr: Some(expr), late: _, reg: _ } + | AsmOperand::InOut { expr, late: _, reg: _ } + | AsmOperand::Const(expr) + | AsmOperand::Label(expr) => visitor.on_expr(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, late: _, reg: _ } => { + visitor.on_expr(*in_expr); + visitor.on_expr_opt(*out_expr); } - } - AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (), - }), + AsmOperand::Out { expr: None, late: _, reg: _ } | AsmOperand::Sym(_) => (), + }) + } Expr::If { condition, then_branch, else_branch } => { - f(*condition); - f(*then_branch); - if let &Some(else_branch) = else_branch { - f(else_branch); - } + visitor.on_expr(*condition); + visitor.on_expr(*then_branch); + visitor.on_expr_opt(*else_branch); } Expr::Let { expr, pat } => { - self.walk_exprs_in_pat(*pat, &mut f); - f(*expr); + visitor.on_pat(*pat); + visitor.on_expr(*expr); } - Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => { - for stmt in statements.iter() { + Expr::Block { statements, tail, id: _, label: _ } + | Expr::Unsafe { statements, tail, id: _ } => { + for stmt in statements { match stmt { - Statement::Let { initializer, else_branch, pat, .. } => { - if let &Some(expr) = initializer { - f(expr); - } - if let &Some(expr) = else_branch { - f(expr); - } - self.walk_exprs_in_pat(*pat, &mut f); + Statement::Let { initializer, else_branch, pat, type_ref } => { + visitor.on_expr_opt(*initializer); + visitor.on_expr_opt(*else_branch); + visitor.on_pat(*pat); + visitor.on_type_opt(*type_ref); + } + Statement::Expr { expr: expression, has_semi: _ } => { + visitor.on_expr(*expression) } - Statement::Expr { expr: expression, .. } => f(*expression), Statement::Item(_) => (), } } - if let &Some(expr) = tail { - f(expr); - } + visitor.on_expr_opt(*tail); } - Expr::Loop { body, .. } => f(*body), - Expr::Call { callee, args, .. } => { - f(*callee); - args.iter().copied().for_each(f); + Expr::Loop { body, label: _ } => visitor.on_expr(*body), + Expr::Call { callee, args } => { + visitor.on_expr(*callee); + visitor.on_exprs(args); } - Expr::MethodCall { receiver, args, .. } => { - f(*receiver); - args.iter().copied().for_each(f); + Expr::MethodCall { receiver, args, generic_args, method_name: _ } => { + visitor.on_expr(*receiver); + visitor.on_exprs(args); + visitor.on_generic_args_opt(generic_args); } Expr::Match { expr, arms } => { - f(*expr); - arms.iter().for_each(|arm| { - f(arm.expr); - if let Some(guard) = arm.guard { - f(guard); - } - self.walk_exprs_in_pat(arm.pat, &mut f); + visitor.on_expr(*expr); + arms.iter().for_each(|MatchArm { pat, guard, expr }| { + visitor.on_expr(*expr); + visitor.on_expr_opt(*guard); + visitor.on_pat(*pat); }); } - Expr::Break { expr, .. } + Expr::Break { expr, label: _ } | Expr::Return { expr } | Expr::Yield { expr } - | Expr::Yeet { expr } => { - if let &Some(expr) = expr { - f(expr); + | Expr::Yeet { expr } => visitor.on_expr_opt(*expr), + Expr::Become { expr } => visitor.on_expr(*expr), + Expr::RecordLit { fields, spread, path } => { + for RecordLitField { name: _, expr } in fields.iter() { + visitor.on_expr(*expr); } - } - Expr::Become { expr } => f(*expr), - Expr::RecordLit { fields, spread, .. } => { - for field in fields.iter() { - f(field.expr); - } - if let RecordSpread::Expr(expr) = spread { - f(*expr); + match spread { + RecordSpread::Expr(expr) => visitor.on_expr(*expr), + RecordSpread::None | RecordSpread::FieldDefaults => {} } + visitor.on_path(path); } - Expr::Closure { body, .. } => { - f(*body); + Expr::Closure { body, args, arg_types, ret_type, capture_by: _, closure_kind: _ } => { + visitor.on_expr(*body); + visitor.on_pats(args); + arg_types.iter().for_each(|arg_type| visitor.on_type_opt(*arg_type)); + visitor.on_type_opt(*ret_type); } - Expr::BinaryOp { lhs, rhs, .. } => { - f(*lhs); - f(*rhs); + Expr::BinaryOp { lhs, rhs, op: _ } => { + visitor.on_expr(*lhs); + visitor.on_expr(*rhs); } - Expr::Range { lhs, rhs, .. } => { - if let &Some(lhs) = rhs { - f(lhs); - } - if let &Some(rhs) = lhs { - f(rhs); - } + Expr::Range { lhs, rhs, range_type: _ } => { + visitor.on_expr_opt(*lhs); + visitor.on_expr_opt(*rhs); } - Expr::Index { base, index, .. } => { - f(*base); - f(*index); + Expr::Index { base, index } => { + visitor.on_expr(*base); + visitor.on_expr(*index); } - Expr::Field { expr, .. } + Expr::Cast { expr, type_ref } => { + visitor.on_expr(*expr); + visitor.on_type(*type_ref); + } + Expr::Field { expr, name: _ } | Expr::Await { expr } - | Expr::Cast { expr, .. } - | Expr::Ref { expr, .. } - | Expr::UnaryOp { expr, .. } - | Expr::Box { expr } => { - f(*expr); + | Expr::Ref { expr, mutability: _, rawness: _ } + | Expr::UnaryOp { expr, op: _ } + | Expr::Box { expr } + | Expr::Const(expr) => { + visitor.on_expr(*expr); } - Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f), + Expr::Tuple { exprs } => visitor.on_exprs(exprs), Expr::Array(a) => match a { - Array::ElementList { elements, .. } => elements.iter().copied().for_each(f), + Array::ElementList { elements } => visitor.on_exprs(elements), Array::Repeat { initializer, repeat } => { - f(*initializer); - f(*repeat) + visitor.on_expr(*initializer); + visitor.on_anon_const_expr(*repeat) } }, &Expr::Assignment { target, value } => { - self.walk_exprs_in_pat(target, &mut f); - f(value); + visitor.on_pat(target); + visitor.on_expr(value); } } } - /// Walks the immediate children expressions and calls `f` for each child expression but does - /// not walk expressions within patterns. - /// - /// Note that this does not walk const blocks. - pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) { - let expr = &self[expr_id]; - match expr { - Expr::Continue { .. } - | Expr::Const(_) - | Expr::Missing - | Expr::Path(_) - | Expr::OffsetOf(_) - | Expr::Literal(_) - | Expr::Underscore => {} - Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op { - AsmOperand::In { expr, .. } - | AsmOperand::Out { expr: Some(expr), .. } - | AsmOperand::InOut { expr, .. } - | AsmOperand::Const(expr) - | AsmOperand::Label(expr) => f(*expr), - AsmOperand::SplitInOut { in_expr, out_expr, .. } => { - f(*in_expr); - if let Some(out_expr) = out_expr { - f(*out_expr); - } - } - AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (), - }), - Expr::If { condition, then_branch, else_branch } => { - f(*condition); - f(*then_branch); - if let &Some(else_branch) = else_branch { - f(else_branch); - } - } - Expr::Let { expr, .. } => { - f(*expr); - } - Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => { - for stmt in statements.iter() { - match stmt { - Statement::Let { initializer, else_branch, .. } => { - if let &Some(expr) = initializer { - f(expr); - } - if let &Some(expr) = else_branch { - f(expr); - } - } - Statement::Expr { expr: expression, .. } => f(*expression), - Statement::Item(_) => (), - } - } - if let &Some(expr) = tail { - f(expr); - } - } - Expr::Loop { body, .. } => f(*body), - Expr::Call { callee, args, .. } => { - f(*callee); - args.iter().copied().for_each(f); - } - Expr::MethodCall { receiver, args, .. } => { - f(*receiver); - args.iter().copied().for_each(f); + /// Walks the immediate children expressions and calls `f` for each child expression. + pub fn walk_child_exprs(&self, expr_id: ExprId, callback: impl FnMut(ExprId)) { + return self.visit_expr_children(expr_id, Visitor { callback, store: self }); + + struct Visitor<'a, F> { + callback: F, + store: &'a ExpressionStore, + } + + impl<F: FnMut(ExprId)> StoreVisitor for Visitor<'_, F> { + fn on_expr(&mut self, expr: ExprId) { + (self.callback)(expr); } - Expr::Match { expr, arms } => { - f(*expr); - arms.iter().map(|arm| arm.expr).for_each(f); + + fn on_pat(&mut self, pat: PatId) { + self.store.walk_exprs_in_pat(pat, &mut self.callback); } - Expr::Break { expr, .. } - | Expr::Return { expr } - | Expr::Yield { expr } - | Expr::Yeet { expr } => { - if let &Some(expr) = expr { - f(expr); - } + } + } + + /// Walks the immediate children expressions and calls `f` for each child expression but does + /// not walk expressions within patterns. + pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, callback: impl FnMut(ExprId)) { + return self.visit_expr_children(expr_id, Visitor { callback }); + + struct Visitor<F> { + callback: F, + } + + impl<F: FnMut(ExprId)> StoreVisitor for Visitor<F> { + fn on_expr(&mut self, expr: ExprId) { + (self.callback)(expr); } - Expr::Become { expr } => f(*expr), - Expr::RecordLit { fields, spread, .. } => { - for field in fields.iter() { - f(field.expr); - } - if let RecordSpread::Expr(expr) = spread { - f(*expr); - } + } + } + + pub fn walk_exprs_in_pat(&self, pat_id: PatId, callback: impl FnMut(ExprId)) { + return Visitor { callback, store: self }.on_pat(pat_id); + + struct Visitor<'a, F> { + callback: F, + store: &'a ExpressionStore, + } + + impl<F: FnMut(ExprId)> StoreVisitor for Visitor<'_, F> { + fn on_expr(&mut self, expr: ExprId) { + (self.callback)(expr); } - Expr::Closure { body, .. } => { - f(*body); + + fn on_pat(&mut self, pat: PatId) { + self.store.visit_pat_children(pat, self); } - Expr::BinaryOp { lhs, rhs, .. } => { - f(*lhs); - f(*rhs); + } + } + + pub fn visit_type_ref_children(&self, type_ref: TypeRefId, mut visitor: impl StoreVisitor) { + match &self[type_ref] { + TypeRef::Never | TypeRef::Placeholder | TypeRef::TypeParam(_) | TypeRef::Error => {} + TypeRef::Tuple(types) => visitor.on_types(types), + TypeRef::Path(path) => visitor.on_path(path), + TypeRef::RawPtr(inner, _) | TypeRef::Slice(inner) => visitor.on_type(*inner), + TypeRef::Reference(ref_type) => { + let RefType { ty, lifetime, mutability: _ } = &**ref_type; + visitor.on_type(*ty); + visitor.on_lifetime_opt(*lifetime); } - Expr::Range { lhs, rhs, .. } => { - if let &Some(lhs) = rhs { - f(lhs); - } - if let &Some(rhs) = lhs { - f(rhs); - } + TypeRef::Array(ArrayType { ty, len: ConstRef { expr: len } }) => { + visitor.on_type(*ty); + visitor.on_anon_const_expr(*len); } - Expr::Index { base, index, .. } => { - f(*base); - f(*index); + TypeRef::Fn(fn_type) => { + let FnType { params, is_varargs: _, is_unsafe: _, abi: _ } = &**fn_type; + params.iter().for_each(|(_, param_ty)| visitor.on_type(*param_ty)); } - Expr::Field { expr, .. } - | Expr::Await { expr } - | Expr::Cast { expr, .. } - | Expr::Ref { expr, .. } - | Expr::UnaryOp { expr, .. } - | Expr::Box { expr } => { - f(*expr); + TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { + visitor.on_type_bounds(bounds) } - Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f), - Expr::Array(a) => match a { - Array::ElementList { elements, .. } => elements.iter().copied().for_each(f), - Array::Repeat { initializer, repeat } => { - f(*initializer); - f(*repeat) - } - }, - &Expr::Assignment { target: _, value } => f(value), } } - pub fn walk_exprs_in_pat(&self, pat_id: PatId, f: &mut impl FnMut(ExprId)) { - self.walk_pats(pat_id, &mut |pat| { - if let Pat::Expr(expr) | Pat::ConstBlock(expr) = self[pat] { - f(expr); - } - }); - } - #[inline] #[track_caller] fn assert_expr_only(&self) -> &ExpressionOnlyStore { @@ -938,6 +941,128 @@ impl ExpressionStore { } } +pub trait StoreVisitor { + fn on_expr(&mut self, expr: ExprId) { + let _ = expr; + } + fn on_anon_const_expr(&mut self, expr: ExprId) { + self.on_expr(expr); + } + fn on_pat(&mut self, pat: PatId) { + let _ = pat; + } + fn on_type(&mut self, ty: TypeRefId) { + let _ = ty; + } + fn on_lifetime(&mut self, lifetime: LifetimeRefId) { + let _ = lifetime; + } +} + +impl<V: StoreVisitor> StoreVisitor for &mut V { + fn on_expr(&mut self, expr: ExprId) { + V::on_expr(self, expr); + } + fn on_anon_const_expr(&mut self, expr: ExprId) { + V::on_anon_const_expr(self, expr); + } + fn on_pat(&mut self, pat: PatId) { + V::on_pat(self, pat); + } + fn on_type(&mut self, ty: TypeRefId) { + V::on_type(self, ty); + } + fn on_lifetime(&mut self, lifetime: LifetimeRefId) { + V::on_lifetime(self, lifetime); + } +} + +trait StoreVisitorExt: StoreVisitor { + fn on_generic_args(&mut self, args: &GenericArgs) { + let GenericArgs { args, bindings, parenthesized: _, has_self_type: _ } = args; + for arg in args { + match arg { + GenericArg::Type(arg) => self.on_type(*arg), + GenericArg::Const(ConstRef { expr }) => self.on_anon_const_expr(*expr), + GenericArg::Lifetime(arg) => self.on_lifetime(*arg), + } + } + for AssociatedTypeBinding { name: _, args, type_ref, bounds } in bindings { + self.on_generic_args_opt(args); + self.on_type_opt(*type_ref); + self.on_type_bounds(bounds); + } + } + + fn on_type_bound(&mut self, bound: &TypeBound) { + match bound { + TypeBound::Path(path_id, _) => self.on_type(path_id.type_ref()), + TypeBound::ForLifetime(_, path_id) => self.on_type(path_id.type_ref()), + TypeBound::Lifetime(lifetime) => self.on_lifetime(*lifetime), + TypeBound::Use(args) => { + for arg in args { + match arg { + UseArgRef::Lifetime(lifetime) => self.on_lifetime(*lifetime), + UseArgRef::Name(_) => {} + } + } + } + TypeBound::Error => {} + } + } + + fn on_path(&mut self, path: &Path) { + match path { + Path::Normal(path) => { + let NormalPath { generic_args, type_anchor, mod_path: _ } = &**path; + generic_args.iter().for_each(|generic_arg| self.on_generic_args_opt(generic_arg)); + self.on_type_opt(*type_anchor); + } + Path::BarePath(_) | Path::LangItem(..) => {} + } + } + + fn on_expr_opt(&mut self, expr: Option<ExprId>) { + if let Some(expr) = expr { + self.on_expr(expr); + } + } + fn on_pat_opt(&mut self, pat: Option<PatId>) { + if let Some(pat) = pat { + self.on_pat(pat); + } + } + fn on_type_opt(&mut self, ty: Option<TypeRefId>) { + if let Some(ty) = ty { + self.on_type(ty); + } + } + fn on_lifetime_opt(&mut self, lifetime: Option<LifetimeRefId>) { + if let Some(lifetime) = lifetime { + self.on_lifetime(lifetime); + } + } + fn on_generic_args_opt(&mut self, args: &Option<impl Borrow<GenericArgs>>) { + if let Some(args) = args { + self.on_generic_args(args.borrow()); + } + } + + fn on_exprs(&mut self, exprs: impl IntoIterator<Item: Borrow<ExprId>>) { + exprs.into_iter().for_each(|expr| self.on_expr(*expr.borrow())); + } + fn on_pats(&mut self, pats: impl IntoIterator<Item: Borrow<PatId>>) { + pats.into_iter().for_each(|pat| self.on_pat(*pat.borrow())); + } + fn on_types(&mut self, types: impl IntoIterator<Item: Borrow<TypeRefId>>) { + types.into_iter().for_each(|ty| self.on_type(*ty.borrow())); + } + fn on_type_bounds(&mut self, bounds: impl IntoIterator<Item: Borrow<TypeBound>>) { + bounds.into_iter().for_each(|bound| self.on_type_bound(bound.borrow())); + } +} +impl<V: StoreVisitor> StoreVisitorExt for V {} + impl Index<ExprId> for ExpressionStore { type Output = Expr; diff --git a/crates/hir-def/src/expr_store/body.rs b/crates/hir-def/src/expr_store/body.rs index 6be3e49a70..2fb47e59c5 100644 --- a/crates/hir-def/src/expr_store/body.rs +++ b/crates/hir-def/src/expr_store/body.rs @@ -2,6 +2,7 @@ //! consts. use std::ops; +use arrayvec::ArrayVec; use hir_expand::{InFile, Lookup}; use span::Edition; use syntax::ast; @@ -28,7 +29,12 @@ pub struct Body { /// If this `Body` is for the body of a constant, this will just be /// empty. pub params: Box<[PatId]>, - pub self_param: Option<BindingId>, + /// The first element, if it exists, is the real `self` binding. + /// + /// The second element is used for `async fn` (or `gen fn` etc.). These functions + /// have to put a `let self = self` inside the returned coroutine, and the second element + /// points at it. + pub self_params: ArrayVec<BindingId, 2>, } impl ops::Deref for Body { @@ -74,6 +80,7 @@ impl Body { let mut params = None; let mut is_async_fn = false; + let mut is_gen_fn = false; let InFile { file_id, value: body } = { match def { DefWithBodyId::FunctionId(f) => { @@ -81,6 +88,7 @@ impl Body { let src = f.source(db); params = src.value.param_list(); is_async_fn = src.value.async_token().is_some(); + is_gen_fn = src.value.gen_token().is_some(); src.map(|it| it.body().map(ast::Expr::from)) } DefWithBodyId::ConstId(c) => { @@ -101,7 +109,8 @@ impl Body { } }; let module = def.module(db); - let (body, source_map) = lower_body(db, def, file_id, module, params, body, is_async_fn); + let (body, source_map) = + lower_body(db, def, file_id, module, params, body, is_async_fn, is_gen_fn); (Arc::new(body), source_map) } @@ -114,7 +123,19 @@ impl Body { impl Body { pub fn root_expr(&self) -> ExprId { - self.store.expr_roots().next().unwrap() + // A `Body` can also contain root expressions that aren't the body (in the param patterns), + // but the body always come last. + self.store.expr_roots().next_back().unwrap() + } + + pub fn self_param(&self) -> Option<BindingId> { + self.self_params.first().copied() + } + + /// `async fn` (or `gen fn` etc.), have to put a `let self = self` inside the returned coroutine. + /// This function returns it. + pub fn coroutine_self_binding(&self) -> Option<BindingId> { + self.self_params.get(1).copied() } pub fn pretty_print( diff --git a/crates/hir-def/src/expr_store/expander.rs b/crates/hir-def/src/expr_store/expander.rs index 2fffa02c13..c79a1db847 100644 --- a/crates/hir-def/src/expr_store/expander.rs +++ b/crates/hir-def/src/expr_store/expander.rs @@ -5,10 +5,8 @@ use std::mem; use base_db::Crate; use cfg::CfgOptions; use drop_bomb::DropBomb; -use hir_expand::AstId; -use hir_expand::span_map::SpanMapRef; use hir_expand::{ - ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId, + AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId, eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap, }; use span::{AstIdMap, SyntaxContext}; @@ -23,7 +21,7 @@ use crate::{ #[derive(Debug)] pub(super) struct Expander<'db> { - span_map: SpanMap, + span_map: SpanMap<'db>, current_file_id: HirFileId, ast_id_map: &'db AstIdMap, /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. @@ -58,11 +56,11 @@ impl<'db> Expander<'db> { } pub(super) fn hygiene_for_range(&self, db: &dyn DefDatabase, range: TextRange) -> HygieneId { - match self.span_map.as_ref() { - hir_expand::span_map::SpanMapRef::ExpansionSpanMap(span_map) => { + match self.span_map { + SpanMap::ExpansionSpanMap(span_map) => { HygieneId::new(span_map.span_at(range.start()).ctx.opaque_and_semiopaque(db)) } - hir_expand::span_map::SpanMapRef::RealSpanMap(_) => HygieneId::ROOT, + SpanMap::RealSpanMap(_) => HygieneId::ROOT, } } @@ -193,15 +191,15 @@ impl<'db> Expander<'db> { let res = db.parse_macro_expansion(call_id); - let err = err.or(res.err); + let err = err.or_else(|| res.err.clone()); ExpandResult { value: { - let parse = res.value.0.cast::<T>(); + let parse = res.value.0.clone().cast::<T>(); self.recursion_depth += 1; let old_file_id = std::mem::replace(&mut self.current_file_id, call_id.into()); let old_span_map = - std::mem::replace(&mut self.span_map, db.span_map(self.current_file_id)); + std::mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(&res.value.1)); let prev_ast_id_map = mem::replace(&mut self.ast_id_map, db.ast_id_map(self.current_file_id)); let mark = Mark { @@ -222,15 +220,15 @@ impl<'db> Expander<'db> { } #[inline] - pub(super) fn span_map(&self) -> SpanMapRef<'_> { - self.span_map.as_ref() + pub(super) fn span_map(&self) -> SpanMap<'_> { + self.span_map } } #[derive(Debug)] pub(super) struct Mark<'db> { file_id: HirFileId, - span_map: SpanMap, + span_map: SpanMap<'db>, ast_id_map: &'db AstIdMap, bomb: DropBomb, } diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 04437a59ac..8818096500 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -8,17 +8,20 @@ mod path; use std::{cell::OnceCell, mem}; +use arrayvec::ArrayVec; use base_db::FxIndexSet; use cfg::CfgOptions; use either::Either; use hir_expand::{ HirFileId, InFile, MacroDefId, + mod_path::ModPath, name::{AsName, Name}, - span_map::SpanMapRef, + span_map::SpanMap, }; use intern::{Symbol, sym}; +use rustc_abi::ExternAbi; use rustc_hash::FxHashMap; -use smallvec::smallvec; +use smallvec::SmallVec; use stdx::never; use syntax::{ AstNode, AstPtr, SyntaxNodePtr, @@ -37,17 +40,17 @@ use crate::{ attrs::AttrFlags, db::DefDatabase, expr_store::{ - Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder, + Body, BodySourceMap, ExprPtr, ExprRoot, ExpressionStore, ExpressionStoreBuilder, ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, LifetimePtr, - PatPtr, RootExprOrigin, TypePtr, + PatPtr, TypePtr, expander::Expander, lower::generics::ImplTraitLowerFn, path::{AssociatedTypeBinding, GenericArg, GenericArgs, GenericArgsParentheses, Path}, }, hir::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, - OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, + CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, + Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, generics::GenericParams, }, item_scope::BuiltinShadowMode, @@ -71,6 +74,7 @@ pub(super) fn lower_body( parameters: Option<ast::ParamList>, body: Option<ast::Expr>, is_async_fn: bool, + is_gen_fn: bool, ) -> (Body, BodySourceMap) { // We cannot leave the root span map empty and let any identifier from it be treated as root, // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved @@ -78,10 +82,10 @@ pub(super) fn lower_body( // even though they should be the same. Also, when the body comes from multiple expansions, their // hygiene is different. - let mut self_param = None; + let mut self_params = ArrayVec::new(); let mut source_map_self_param = None; let mut params = vec![]; - let mut collector = ExprCollector::body(db, module, current_file_id); + let mut collector = ExprCollector::new(db, module, current_file_id); let skip_body = AttrFlags::query( db, @@ -111,18 +115,17 @@ pub(super) fn lower_body( BindingAnnotation::new(is_mutable, false), hygiene, ); - self_param = Some(binding_id); + self_params.push(binding_id); source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); } let count = param_list.params().filter(|it| collector.check_cfg(it)).count(); params = (0..count).map(|_| collector.missing_pat()).collect(); }; - let body_expr = collector.missing_expr(); - collector.store.inference_roots = Some(smallvec![(body_expr, RootExprOrigin::BodyRoot)]); + collector.with_expr_root(|collector| collector.missing_expr()); let (store, source_map) = collector.store.finish(); return ( - Body { store, params: params.into_boxed_slice(), self_param }, + Body { store, params: params.into_boxed_slice(), self_params }, BodySourceMap { self_param: source_map_self_param, store: source_map }, ); } @@ -140,7 +143,7 @@ pub(super) fn lower_body( BindingAnnotation::new(is_mutable, false), hygiene, ); - self_param = Some(binding_id); + self_params.push(binding_id); source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); } @@ -162,25 +165,29 @@ pub(super) fn lower_body( } }; - let body_expr = collector.collect( - &mut params, - body, - if is_async_fn { - Awaitable::Yes - } else { - match owner { - DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"), - DefWithBodyId::StaticId(..) => Awaitable::No("static"), - DefWithBodyId::ConstId(..) => Awaitable::No("constant"), - DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), - } - }, - ); - collector.store.inference_roots = Some(smallvec![(body_expr, RootExprOrigin::BodyRoot)]); + collector.with_expr_root(|collector| { + collector.collect( + &mut self_params, + &mut params, + body, + if is_async_fn { + Awaitable::Yes + } else { + match owner { + DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"), + DefWithBodyId::StaticId(..) => Awaitable::No("static"), + DefWithBodyId::ConstId(..) => Awaitable::No("constant"), + DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), + } + }, + is_async_fn, + is_gen_fn, + ) + }); let (store, source_map) = collector.store.finish(); ( - Body { store, params: params.into_boxed_slice(), self_param }, + Body { store, params: params.into_boxed_slice(), self_params }, BodySourceMap { self_param: source_map_self_param, store: source_map }, ) } @@ -190,7 +197,7 @@ pub(crate) fn lower_type_ref( module: ModuleId, type_ref: InFile<Option<ast::Type>>, ) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId) { - let mut expr_collector = ExprCollector::signature(db, module, type_ref.file_id); + let mut expr_collector = ExprCollector::new(db, module, type_ref.file_id); let type_ref = expr_collector.lower_type_ref_opt(type_ref.value, &mut ExprCollector::impl_trait_allocator); let (store, source_map) = expr_collector.store.finish(); @@ -205,7 +212,7 @@ pub(crate) fn lower_generic_params( param_list: Option<ast::GenericParamList>, where_clause: Option<ast::WhereClause>, ) -> (ExpressionStore, GenericParams, ExpressionStoreSourceMap) { - let mut expr_collector = ExprCollector::signature(db, module, file_id); + let mut expr_collector = ExprCollector::new(db, module, file_id); let mut collector = generics::GenericParamsCollector::new(def); collector.lower(&mut expr_collector, param_list, where_clause); let params = collector.finish(); @@ -219,7 +226,7 @@ pub(crate) fn lower_impl( impl_syntax: InFile<ast::Impl>, impl_id: ImplId, ) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId, Option<TraitRef>, GenericParams) { - let mut expr_collector = ExprCollector::signature(db, module, impl_syntax.file_id); + let mut expr_collector = ExprCollector::new(db, module, impl_syntax.file_id); let self_ty = expr_collector.lower_type_ref_opt_disallow_impl_trait(impl_syntax.value.self_ty()); let trait_ = impl_syntax.value.trait_().and_then(|it| match &it { @@ -247,7 +254,7 @@ pub(crate) fn lower_trait( trait_syntax: InFile<ast::Trait>, trait_id: TraitId, ) -> (ExpressionStore, ExpressionStoreSourceMap, GenericParams) { - let mut expr_collector = ExprCollector::signature(db, module, trait_syntax.file_id); + let mut expr_collector = ExprCollector::new(db, module, trait_syntax.file_id); let mut collector = generics::GenericParamsCollector::with_self_param( &mut expr_collector, trait_id.into(), @@ -270,7 +277,7 @@ pub(crate) fn lower_type_alias( type_alias_id: TypeAliasId, ) -> (ExpressionStore, ExpressionStoreSourceMap, GenericParams, Box<[TypeBound]>, Option<TypeRefId>) { - let mut expr_collector = ExprCollector::signature(db, module, alias.file_id); + let mut expr_collector = ExprCollector::new(db, module, alias.file_id); let bounds = alias .value .type_bound_list() @@ -312,7 +319,7 @@ pub(crate) fn lower_function( bool, bool, ) { - let mut expr_collector = ExprCollector::signature(db, module, fn_.file_id); + let mut expr_collector = ExprCollector::new(db, module, fn_.file_id); let mut collector = generics::GenericParamsCollector::new(function_id.into()); collector.lower(&mut expr_collector, fn_.value.generic_param_list(), fn_.value.where_clause()); let mut params = vec![]; @@ -375,12 +382,20 @@ pub(crate) fn lower_function( expr_collector.lower_type_ref_opt(ret_type.ty(), &mut ExprCollector::impl_trait_allocator) }); - let return_type = if fn_.value.async_token().is_some() { - let path = hir_expand::mod_path::path![core::future::Future]; + let return_type = if fn_.value.async_token().is_some() || fn_.value.gen_token().is_some() { + let (path, assoc_name) = + match (fn_.value.async_token().is_some(), fn_.value.gen_token().is_some()) { + (true, true) => { + (hir_expand::mod_path::path![core::async_iter::AsyncIterator], sym::Item) + } + (true, false) => (hir_expand::mod_path::path![core::future::Future], sym::Output), + (false, true) => (hir_expand::mod_path::path![core::iter::Iterator], sym::Item), + (false, false) => unreachable!(), + }; let mut generic_args: Vec<_> = std::iter::repeat_n(None, path.segments().len() - 1).collect(); let binding = AssociatedTypeBinding { - name: Name::new_symbol_root(sym::Output), + name: Name::new_symbol_root(assoc_name), args: None, type_ref: Some( return_type @@ -531,20 +546,7 @@ impl BindingList { } impl<'db> ExprCollector<'db> { - /// Creates a collector for a signature store, this will populate `const_expr_origins` to any - /// top level const arg roots. - pub fn signature( - db: &dyn DefDatabase, - module: ModuleId, - current_file_id: HirFileId, - ) -> ExprCollector<'_> { - let mut this = Self::body(db, module, current_file_id); - this.store.inference_roots = Some(Default::default()); - this - } - - /// Creates a collector for a bidy store. - pub fn body( + pub fn new( db: &dyn DefDatabase, module: ModuleId, current_file_id: HirFileId, @@ -552,7 +554,7 @@ impl<'db> ExprCollector<'db> { let (def_map, local_def_map) = module.local_def_map(db); let expander = Expander::new(db, current_file_id, def_map); let krate = module.krate(db); - ExprCollector { + let mut result = ExprCollector { db, cfg_options: krate.cfg_options(db), module, @@ -570,7 +572,9 @@ impl<'db> ExprCollector<'db> { outer_impl_trait: false, krate, name_generator_index: 0, - } + }; + result.store.inference_roots = Some(SmallVec::new()); + result } fn generate_new_name(&mut self) -> Name { @@ -585,7 +589,7 @@ impl<'db> ExprCollector<'db> { } #[inline] - pub(crate) fn span_map(&self) -> SpanMapRef<'_> { + pub(crate) fn span_map(&self) -> SpanMap<'_> { self.expander.span_map() } @@ -639,9 +643,6 @@ impl<'db> ExprCollector<'db> { } ast::Type::ArrayType(inner) => { let len = self.lower_const_arg_opt(inner.const_arg()); - if let Some(const_expr_origins) = &mut self.store.inference_roots { - const_expr_origins.push((len.expr, RootExprOrigin::ArrayLength)); - } TypeRef::Array(ArrayType { ty: self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn), len, @@ -684,15 +685,13 @@ impl<'db> ExprCollector<'db> { } else { Vec::with_capacity(1) }; - fn lower_abi(abi: ast::Abi) -> Symbol { - match abi.abi_string() { - Some(tok) => Symbol::intern(tok.text_without_quotes()), - // `extern` default to be `extern "C"`. - _ => sym::C, - } + fn lower_abi(abi: ast::Abi) -> ExternAbi { + abi.abi_string() + .and_then(|abi| abi.text_without_quotes().parse().ok()) + .unwrap_or(ExternAbi::FALLBACK) } - let abi = inner.abi().map(lower_abi); + let abi = inner.abi().map(lower_abi).unwrap_or(ExternAbi::Rust); params.push((None, ret_ty)); TypeRef::Fn(Box::new(FnType { is_varargs, @@ -926,9 +925,6 @@ impl<'db> ExprCollector<'db> { } ast::GenericArg::ConstArg(arg) => { let arg = self.lower_const_arg(arg); - if let Some(const_expr_origins) = &mut self.store.inference_roots { - const_expr_origins.push((arg.expr, RootExprOrigin::GenericArgsPath)); - } args.push(GenericArg::Const(arg)) } } @@ -949,46 +945,125 @@ impl<'db> ExprCollector<'db> { /// into the body. This is to make sure that the future actually owns the /// arguments that are passed to the function, and to ensure things like /// drop order are stable. - fn lower_async_block_with_moved_arguments( + fn lower_coroutine_body_with_moved_arguments( &mut self, + self_params: &mut ArrayVec<BindingId, 2>, params: &mut [PatId], body: ExprId, + kind: CoroutineKind, coroutine_source: CoroutineSource, ) -> ExprId { + // Async function parameters are lowered into the closure body so that they are + // captured and so that the drop order matches the equivalent non-async functions. + // + // from: + // + // async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) { + // <body> + // } + // + // into: + // + // fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) { + // async move { + // let __arg2 = __arg2; + // let <pattern> = __arg2; + // let __arg1 = __arg1; + // let <pattern> = __arg1; + // let __arg0 = __arg0; + // let <pattern> = __arg0; + // drop-temps { <body> } // see comments later in fn for details + // } + // } + // + // If `<pattern>` is a simple ident, then it is lowered to a single + // `let <pattern> = <pattern>;` statement as an optimization. + let mut statements = Vec::new(); + + if let Some(&self_param) = self_params.first() { + let Binding { ref name, mode, hygiene, .. } = self.store.bindings[self_param]; + let name = name.clone(); + let child_binding_id = self.alloc_binding(name.clone(), mode, hygiene); + let child_pat_id = + self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None }); + self.add_definition_to_binding(child_binding_id, child_pat_id); + let expr = self.alloc_expr_desugared(Expr::Path(name.into())); + if !hygiene.is_root() { + self.store.ident_hygiene.insert(expr.into(), hygiene); + } + statements.push(Statement::Let { + pat: child_pat_id, + type_ref: None, + initializer: Some(expr), + else_branch: None, + }); + self_params.push(child_binding_id); + } + for param in params { - let (name, hygiene) = match self.store.pats[*param] { - Pat::Bind { id, .. } + let (name, hygiene, is_simple_parameter) = match self.store.pats[*param] { + // Check if this is a binding pattern, if so, we can optimize and avoid adding a + // `let <pat> = __argN;` statement. In this case, we do not rename the parameter. + Pat::Bind { id, subpat: None, .. } if matches!( self.store.bindings[id].mode, BindingAnnotation::Unannotated | BindingAnnotation::Mutable ) => { - // If this is a direct binding, we can leave it as-is, as it'll always be captured anyway. - continue; + (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, true) } Pat::Bind { id, .. } => { // If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display. - (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene) + (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, false) } - _ => (self.generate_new_name(), HygieneId::ROOT), + _ => (self.generate_new_name(), HygieneId::ROOT, false), }; - let binding_id = self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); - let pat_id = self.alloc_pat_desugared(Pat::Bind { id: binding_id, subpat: None }); - let expr = self.alloc_expr_desugared(Expr::Path(name.into())); + let pat_syntax = self.store.pat_map_back.get(*param).copied(); + let child_binding_id = + self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); + let child_pat_id = + self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None }); + self.add_definition_to_binding(child_binding_id, child_pat_id); + if let Some(pat_syntax) = pat_syntax { + self.store.pat_map_back.insert(child_pat_id, pat_syntax); + } + let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into())); if !hygiene.is_root() { self.store.ident_hygiene.insert(expr.into(), hygiene); } statements.push(Statement::Let { - pat: *param, + pat: child_pat_id, type_ref: None, initializer: Some(expr), else_branch: None, }); - *param = pat_id; + if !is_simple_parameter { + let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into())); + if !hygiene.is_root() { + self.store.ident_hygiene.insert(expr.into(), hygiene); + } + statements.push(Statement::Let { + pat: *param, + type_ref: None, + initializer: Some(expr), + else_branch: None, + }); + + let parent_binding_id = + self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene); + let parent_pat_id = + self.alloc_pat_desugared(Pat::Bind { id: parent_binding_id, subpat: None }); + self.add_definition_to_binding(parent_binding_id, parent_pat_id); + if let Some(pat_syntax) = pat_syntax { + self.store.pat_map_back.insert(parent_pat_id, pat_syntax); + } + *param = parent_pat_id; + } } - let async_ = self.async_block( + let coroutine = self.desugared_coroutine_expr( + kind, coroutine_source, // The default capture mode here is by-ref. Later on during upvar analysis, // we will force the captured arguments to by-move, but for async closures, @@ -1000,11 +1075,12 @@ impl<'db> ExprCollector<'db> { Some(body), ); // It's important that this comes last, see the lowering of async closures for why. - self.alloc_expr_desugared(async_) + self.alloc_expr_desugared(coroutine) } - fn async_block( + fn desugared_coroutine_expr( &mut self, + kind: CoroutineKind, source: CoroutineSource, capture_by: CaptureBy, id: Option<BlockId>, @@ -1017,22 +1093,37 @@ impl<'db> ExprCollector<'db> { arg_types: Box::default(), ret_type: None, body: block, - closure_kind: ClosureKind::AsyncBlock { source }, + closure_kind: ClosureKind::Coroutine { kind, source }, capture_by, } } fn collect( &mut self, + self_params: &mut ArrayVec<BindingId, 2>, params: &mut [PatId], expr: Option<ast::Expr>, awaitable: Awaitable, + is_async_fn: bool, + is_gen_fn: bool, ) -> ExprId { self.awaitable_context.replace(awaitable); self.with_label_rib(RibKind::Closure, |this| { let body = this.collect_expr_opt(expr); - if awaitable == Awaitable::Yes { - this.lower_async_block_with_moved_arguments(params, body, CoroutineSource::Fn) + if is_async_fn || is_gen_fn { + let kind = match (is_async_fn, is_gen_fn) { + (true, true) => CoroutineKind::AsyncGen, + (true, false) => CoroutineKind::Async, + (false, true) => CoroutineKind::Gen, + (false, false) => unreachable!(), + }; + this.lower_coroutine_body_with_moved_arguments( + self_params, + params, + body, + kind, + CoroutineSource::Fn, + ) } else { body } @@ -1109,17 +1200,17 @@ impl<'db> ExprCollector<'db> { } fn lower_const_arg_opt(&mut self, arg: Option<ast::ConstArg>) -> ConstRef { - let const_expr_origins = self.store.inference_roots.take(); - let r = ConstRef { expr: self.collect_expr_opt(arg.and_then(|it| it.expr())) }; - self.store.inference_roots = const_expr_origins; - r + ConstRef { + expr: self.with_fresh_binding_expr_root(|this| { + this.collect_expr_opt(arg.and_then(|arg| arg.expr())) + }), + } } pub fn lower_const_arg(&mut self, arg: ast::ConstArg) -> ConstRef { - let const_expr_origins = self.store.inference_roots.take(); - let r = ConstRef { expr: self.collect_expr_opt(arg.expr()) }; - self.store.inference_roots = const_expr_origins; - r + ConstRef { + expr: self.with_fresh_binding_expr_root(|this| this.collect_expr_opt(arg.expr())), + } } fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { @@ -1191,7 +1282,44 @@ impl<'db> ExprCollector<'db> { self.with_label_rib(RibKind::Closure, |this| { this.with_awaitable_block(Awaitable::Yes, |this| { this.collect_block_(e, |this, id, statements, tail| { - this.async_block( + this.desugared_coroutine_expr( + CoroutineKind::Async, + CoroutineSource::Block, + capture_by, + id, + statements, + tail, + ) + }) + }) + }) + } + Some(ast::BlockModifier::Gen(_)) => { + let capture_by = + if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; + self.with_label_rib(RibKind::Closure, |this| { + this.with_awaitable_block(Awaitable::No("non-async gen block"), |this| { + this.collect_block_(e, |this, id, statements, tail| { + this.desugared_coroutine_expr( + CoroutineKind::Gen, + CoroutineSource::Block, + capture_by, + id, + statements, + tail, + ) + }) + }) + }) + } + Some(ast::BlockModifier::AsyncGen(_)) => { + let capture_by = + if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; + self.with_label_rib(RibKind::Closure, |this| { + this.with_awaitable_block(Awaitable::Yes, |this| { + this.collect_block_(e, |this, id, statements, tail| { + this.desugared_coroutine_expr( + CoroutineKind::AsyncGen, CoroutineSource::Block, capture_by, id, @@ -1212,14 +1340,6 @@ impl<'db> ExprCollector<'db> { }) }) } - // FIXME - Some(ast::BlockModifier::AsyncGen(_)) => { - self.with_awaitable_block(Awaitable::Yes, |this| this.collect_block(e)) - } - Some(ast::BlockModifier::Gen(_)) => self - .with_awaitable_block(Awaitable::No("non-async gen block"), |this| { - this.collect_block(e) - }), None => self.collect_block(e), }, ast::Expr::LoopExpr(e) => { @@ -1347,8 +1467,10 @@ impl<'db> ExprCollector<'db> { ast::Expr::RecordExpr(e) => { let path = e .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return Some(self.missing_expr()); + }; let record_lit = if let Some(nfl) = e.record_expr_field_list() { let fields = nfl .fields() @@ -1425,11 +1547,11 @@ impl<'db> ExprCollector<'db> { } } ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| { - this.with_binding_owner_and_return(|this| { + let mut is_coroutine_closure = false; + let closure = this.with_binding_owner_and_return(|this| { let mut args = Vec::new(); let mut arg_types = Vec::new(); // For coroutine closures, the body, aka. the coroutine is the bindings owner, and not the closure. - let mut body_is_bindings_owner = false; if let Some(pl) = e.param_list() { let num_params = pl.params().count(); args.reserve_exact(num_params); @@ -1457,25 +1579,38 @@ impl<'db> ExprCollector<'db> { }; let mut body = this .with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body())); - - let closure_kind = if this.is_lowering_coroutine { - let movability = if e.static_token().is_some() { - Movability::Static + let kind = { + if e.async_token().is_some() && e.gen_token().is_some() { + Some(CoroutineKind::AsyncGen) + } else if e.async_token().is_some() { + Some(CoroutineKind::Async) + } else if e.gen_token().is_some() { + Some(CoroutineKind::Gen) } else { - Movability::Movable - }; - ClosureKind::Coroutine(movability) - } else if e.async_token().is_some() { + None + } + }; + + let closure_kind = if let Some(kind) = kind { // It's important that this expr is allocated immediately before the closure. // We rely on it for `coroutine_for_closure()`. - body = this.lower_async_block_with_moved_arguments( + body = this.lower_coroutine_body_with_moved_arguments( + &mut ArrayVec::new(), &mut args, body, + kind, CoroutineSource::Closure, ); - body_is_bindings_owner = true; + is_coroutine_closure = true; - ClosureKind::AsyncClosure + ClosureKind::CoroutineClosure(kind) + } else if this.is_lowering_coroutine { + let movability = if e.static_token().is_some() { + Movability::Static + } else { + Movability::Movable + }; + ClosureKind::OldCoroutine(movability) } else { ClosureKind::Closure }; @@ -1495,8 +1630,23 @@ impl<'db> ExprCollector<'db> { syntax_ptr, ); - (if body_is_bindings_owner { body } else { closure }, closure) - }) + (if is_coroutine_closure { body } else { closure }, closure) + }); + + if is_coroutine_closure { + let Expr::Closure { args, .. } = &this.store.exprs[closure] else { + unreachable!() + }; + for &arg in args { + let Pat::Bind { id, .. } = this.store.pats[arg] else { + never!("`lower_coroutine_body_with_moved_arguments()` should make sure the coroutine closure only have simple bind args"); + continue; + }; + this.store.binding_owners.insert(id, closure); + } + } + + closure }), ast::Expr::BinExpr(e) => { let op = e.op_kind(); @@ -1611,6 +1761,37 @@ impl<'db> ExprCollector<'db> { }) } + /// Whether this path should be lowered as destructuring assignment, or as a normal assignment. + fn path_is_destructuring_assignment(&self, path: &ModPath) -> bool { + // rustc has access to a full resolver here, including local variables and generic params, and it checks the following + // criteria: a path not lowered as destructuring assignment if it can *fully resolve* to something that is *not* + // a const, a unit struct or a variant. + // We don't have access to a full resolver here. So we should do the same as rustc, but assuming that local variables + // could be resolved to nothing (fortunately, there cannot be a local variable shadowing a unit struct/variant/const, + // as that is an error). We don't need to consider const params as it's an error to refer to these in patterns. + let (resolution, unresolved_idx, _) = self.def_map.resolve_path_locally( + self.local_def_map, + self.db, + self.module, + path, + BuiltinShadowMode::Other, + ); + match unresolved_idx { + Some(_) => { + // If `Some(_)`, path could be resolved to unit struct/variant/const with type information, i.e. an assoc type or const. + // If `None`, path could be a local variable. + resolution.take_types().is_some() + } + None => match resolution.take_values() { + // We don't need to consider non-unit structs/variants, as those are not value types. + Some(ModuleDefId::EnumVariantId(_)) + | Some(ModuleDefId::AdtId(_)) + | Some(ModuleDefId::ConstId(_)) => true, + _ => false, + }, + } + } + fn collect_expr_as_pat_opt(&mut self, expr: Option<ast::Expr>) -> PatId { match expr { Some(expr) => self.collect_expr_as_pat(expr), @@ -1670,21 +1851,25 @@ impl<'db> ExprCollector<'db> { let path = collect_path(self, e.expr()?)?; let path = path .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return Some(self.missing_pat()); + }; let (ellipsis, args) = collect_tuple(self, e.arg_list()?.args()); self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr) } ast::Expr::PathExpr(e) => { - let (path, hygiene) = self - .collect_expr_path(e.clone()) - .map(|(path, hygiene)| (Pat::Path(path), hygiene)) - .unwrap_or((Pat::Missing, HygieneId::ROOT)); - let pat_id = self.alloc_pat_from_expr(path, syntax_ptr); - if !hygiene.is_root() { - self.store.ident_hygiene.insert(pat_id.into(), hygiene); + let (path, hygiene) = self.collect_expr_path(e.clone())?; + let mod_path = path.mod_path().expect("should not lower to lang path"); + if self.path_is_destructuring_assignment(mod_path) { + let pat_id = self.alloc_pat_from_expr(Pat::Path(path), syntax_ptr); + if !hygiene.is_root() { + self.store.ident_hygiene.insert(pat_id.into(), hygiene); + } + pat_id + } else { + return None; } - pat_id } ast::Expr::MacroExpr(e) => { let e = e.macro_call()?; @@ -1699,11 +1884,15 @@ impl<'db> ExprCollector<'db> { ast::Expr::RecordExpr(e) => { let path = e .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return Some(self.missing_pat()); + }; let record_field_list = e.record_expr_field_list()?; let ellipsis = record_field_list.dotdot_token().is_some(); - // FIXME: Report an error here if `record_field_list.spread().is_some()`. + // We wanted to emit an error here if `record_field_list.spread().is_some()`, + // but that's already a syntax error in rustc, so we decided not to. + // See https://github.com/rust-lang/rust-analyzer/pull/22206#discussion_r3156097370 let args = record_field_list .fields() .filter_map(|f| { @@ -1954,24 +2143,27 @@ impl<'db> ExprCollector<'db> { /// ``` fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId { let lang_items = self.lang_items(); - let into_iter_fn = self.lang_path(lang_items.IntoIterIntoIter); - let iter_next_fn = self.lang_path(lang_items.IteratorNext); - let option_some = self.lang_path(lang_items.OptionSome); - let option_none = self.lang_path(lang_items.OptionNone); + let (Some(into_iter_fn), Some(iter_next_fn), Some(option_some), Some(option_none)) = ( + self.lang_path(lang_items.IntoIterIntoIter), + self.lang_path(lang_items.IteratorNext), + self.lang_path(lang_items.OptionSome), + self.lang_path(lang_items.OptionNone), + ) else { + return self.missing_expr(); + }; let head = self.collect_expr_opt(e.iterable()); - let into_iter_fn_expr = - self.alloc_expr(into_iter_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr); let iterator = self.alloc_expr( Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) }, syntax_ptr, ); let none_arm = MatchArm { - pat: self.alloc_pat_desugared(option_none.map_or(Pat::Missing, Pat::Path)), + pat: self.alloc_pat_desugared(Pat::Path(option_none)), guard: None, expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr), }; let some_pat = Pat::TupleStruct { - path: option_some.map(Box::new), + path: option_some, args: Box::new([self.collect_pat_top(e.pat())]), ellipsis: None, }; @@ -1991,8 +2183,7 @@ impl<'db> ExprCollector<'db> { Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, syntax_ptr, ); - let iter_next_fn_expr = - self.alloc_expr(iter_next_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr); let iter_next_expr = self.alloc_expr( Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) }, syntax_ptr, @@ -2040,11 +2231,15 @@ impl<'db> ExprCollector<'db> { /// ``` fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId { let lang_items = self.lang_items(); - let try_branch = self.lang_path(lang_items.TryTraitBranch); - let cf_continue = self.lang_path(lang_items.ControlFlowContinue); - let cf_break = self.lang_path(lang_items.ControlFlowBreak); + let (Some(try_branch), Some(cf_continue), Some(cf_break)) = ( + self.lang_path(lang_items.TryTraitBranch), + self.lang_path(lang_items.ControlFlowContinue), + self.lang_path(lang_items.ControlFlowBreak), + ) else { + return self.missing_expr(); + }; let operand = self.collect_expr_opt(e.expr()); - let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr); let expr = self .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr); let continue_name = self.generate_new_name(); @@ -2058,7 +2253,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(continue_binding, continue_bpat); let continue_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::TupleStruct { - path: cf_continue.map(Box::new), + path: cf_continue, args: Box::new([continue_bpat]), ellipsis: None, }), @@ -2072,7 +2267,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(break_binding, break_bpat); let break_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::TupleStruct { - path: cf_break.map(Box::new), + path: cf_break, args: Box::new([break_bpat]), ellipsis: None, }), @@ -2306,7 +2501,7 @@ impl<'db> ExprCollector<'db> { ) -> ExprId { let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| { let ast_id = self.expander.in_file(file_local_id); - self.db.intern_block(BlockLoc { ast_id, module: self.module }) + BlockId::new(self.db, BlockLoc { ast_id, module: self.module }) }); let (module, def_map) = @@ -2373,9 +2568,7 @@ impl<'db> ExprCollector<'db> { let Some(pat) = pat else { return self.missing_pat() }; match &pat { - ast::Pat::IdentPat(bp) => { - // FIXME: Emit an error if `!bp.is_simple_ident()`. - + ast::Pat::IdentPat(bp) if bp.is_simple_ident() => { let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); let hygiene = bp .name() @@ -2387,8 +2580,12 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(binding, pat); pat } - // FIXME: Emit an error. - _ => self.missing_pat(), + _ => { + self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn { + node: self.expander.in_file(AstPtr::new(&pat)), + }); + self.missing_pat() + } } } @@ -2465,8 +2662,10 @@ impl<'db> ExprCollector<'db> { ast::Pat::TupleStructPat(p) => { let path = p .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return self.missing_pat(); + }; let (args, ellipsis) = self.collect_tuple_pat( p.fields(), comma_follows_token(p.l_paren_token()), @@ -2531,8 +2730,10 @@ impl<'db> ExprCollector<'db> { ast::Pat::RecordPat(p) => { let path = p .path() - .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)) - .map(Box::new); + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + let Some(path) = path else { + return self.missing_pat(); + }; let record_pat_field_list = &p.record_pat_field_list().expect("every struct should have a field list"); let args = record_pat_field_list @@ -2572,19 +2773,15 @@ impl<'db> ExprCollector<'db> { let expr_id = self.alloc_expr(expr, expr_ptr); Pat::Lit(expr_id) } - ast::Pat::RestPat(_) => { - // `RestPat` requires special handling and should not be mapped - // to a Pat. Here we are using `Pat::Missing` as a fallback for - // when `RestPat` is mapped to `Pat`, which can easily happen - // when the source code being analyzed has a malformed pattern - // which includes `..` in a place where it isn't valid. - - Pat::Missing - } + ast::Pat::RestPat(_) => Pat::Rest, ast::Pat::BoxPat(boxpat) => { let inner = self.collect_pat_opt(boxpat.pat(), binding_list); Pat::Box { inner } } + ast::Pat::DerefPat(inner) => { + let inner = self.collect_pat_opt(inner.pat(), binding_list); + Pat::Deref { inner } + } ast::Pat::ConstBlockPat(const_block_pat) => { if let Some(block) = const_block_pat.block_expr() { let expr_id = self.with_label_rib(RibKind::Constant, |this| { @@ -2712,7 +2909,7 @@ impl<'db> ExprCollector<'db> { // endregion: patterns - /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when + /// Returns `false` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `true` when /// not. fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool { let enabled = self.expander.is_cfg_enabled(owner, self.cfg_options); @@ -2756,7 +2953,7 @@ impl<'db> ExprCollector<'db> { None } else { hygiene_id.syntax_context().outer_expn(self.db).map(|expansion| { - let expansion = self.db.lookup_intern_macro_call(expansion.into()); + let expansion = hir_expand::MacroCallId::from(expansion).loc(self.db); (hygiene_id.syntax_context().parent(self.db), expansion.def) }) }; @@ -2786,7 +2983,7 @@ impl<'db> ExprCollector<'db> { hygiene_id = HygieneId::new(parent_ctx.opaque_and_semiopaque(self.db)); hygiene_info = parent_ctx.outer_expn(self.db).map(|expansion| { - let expansion = self.db.lookup_intern_macro_call(expansion.into()); + let expansion = hir_expand::MacroCallId::from(expansion).loc(self.db); (parent_ctx.parent(self.db), expansion.def) }); } @@ -2897,6 +3094,31 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> } impl ExprCollector<'_> { + fn with_fresh_binding_expr_root(&mut self, f: impl FnOnce(&mut Self) -> ExprId) -> ExprId { + self.with_expr_root(|this| this.with_binding_owner(f)) + } + + fn with_expr_root(&mut self, f: impl FnOnce(&mut Self) -> ExprId) -> ExprId { + let inference_roots = self.store.inference_roots.take(); + let root = f(self); + self.store.inference_roots = inference_roots; + + if let Some(inference_roots) = &mut self.store.inference_roots { + inference_roots.push(ExprRoot { + root, + exprs_end: end(&self.store.exprs), + pats_end: end(&self.store.pats), + bindings_end: end(&self.store.bindings), + }); + } + + return root; + + fn end<T>(arena: &la_arena::Arena<T>) -> la_arena::Idx<T> { + la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(arena.len() as u32)) + } + } + fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { let src = self.expander.in_file(ptr); let id = self.store.exprs.alloc(expr); diff --git a/crates/hir-def/src/expr_store/lower/format_args.rs b/crates/hir-def/src/expr_store/lower/format_args.rs index 51616afb38..b058ad6d9a 100644 --- a/crates/hir-def/src/expr_store/lower/format_args.rs +++ b/crates/hir-def/src/expr_store/lower/format_args.rs @@ -30,12 +30,14 @@ impl<'db> ExprCollector<'db> { ) -> ExprId { let mut args = FormatArgumentsCollector::default(); f.args().for_each(|arg| { + let expr = arg.expr(); args.add(FormatArgument { kind: match arg.arg_name() { Some(name) => FormatArgumentKind::Named(Name::new_root(name.name().text())), None => FormatArgumentKind::Normal, }, - expr: self.collect_expr_opt(arg.expr()), + syntax: expr.as_ref().map(AstPtr::new), + expr: self.collect_expr_opt(expr), }); }); let template = f.template(); @@ -53,8 +55,10 @@ impl<'db> ExprCollector<'db> { Some(((s, is_direct_literal), template)) => { let call_ctx = SyntaxContext::root(self.def_map.edition()); let hygiene = self.hygiene_id_for(s.syntax().text_range()); + let template_ptr = AstPtr::new(&template); let fmt = format_args::parse( &s, + template_ptr, fmt_snippet, args, is_direct_literal, @@ -65,10 +69,7 @@ impl<'db> ExprCollector<'db> { .template_map .get_or_insert_with(Default::default) .implicit_capture_to_source - .insert( - expr_id, - self.expander.in_file((AstPtr::new(&template), range)), - ); + .insert(expr_id, self.expander.in_file((template_ptr, range))); } if !hygiene.is_root() { self.store.ident_hygiene.insert(expr_id.into(), hygiene); @@ -340,7 +341,8 @@ impl<'db> ExprCollector<'db> { expr: args_ident_expr, name: Name::new_tuple_field(arg_index), }); - self.make_argument(arg, ty) + let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax); + self.make_argument(arg_ptr, arg, ty) }) .collect(); let args = @@ -557,7 +559,8 @@ impl<'db> ExprCollector<'db> { rawness: Rawness::Ref, mutability: Mutability::Shared, }); - self.make_argument(arg, ty) + let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax); + self.make_argument(arg_ptr, arg, ty) }) .collect(); let array = @@ -649,7 +652,8 @@ impl<'db> ExprCollector<'db> { rawness: Rawness::Ref, mutability: Mutability::Shared, }); - self.make_argument(ref_arg, ty) + let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax); + self.make_argument(arg_ptr, ref_arg, ty) }) .collect(); let args = @@ -716,7 +720,8 @@ impl<'db> ExprCollector<'db> { expr: args_ident_expr, name: Name::new_tuple_field(arg_index), }); - self.make_argument(arg, ty) + let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax); + self.make_argument(arg_ptr, arg, ty) }) .collect(); let array = @@ -867,11 +872,14 @@ impl<'db> ExprCollector<'db> { }; let width = RecordLitField { name: Name::new_symbol_root(sym::width), expr: width_expr }; - self.alloc_expr_desugared(Expr::RecordLit { - path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new), - fields: Box::new([position, flags, precision, width]), - spread: RecordSpread::None, - }) + match self.lang_path(lang_items.FormatPlaceholder) { + Some(path) => self.alloc_expr_desugared(Expr::RecordLit { + path, + fields: Box::new([position, flags, precision, width]), + spread: RecordSpread::None, + }), + None => self.missing_expr(), + } } else { let format_placeholder_new = self.ty_rel_lang_path_desugared_expr(lang_items.FormatPlaceholder, sym::new); @@ -973,11 +981,16 @@ impl<'db> ExprCollector<'db> { /// ```text /// <core::fmt::Argument>::new_…(arg) /// ``` - fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId { + fn make_argument( + &mut self, + arg_ptr: Option<AstPtr<ast::Expr>>, + arg: ExprId, + ty: ArgumentType, + ) -> ExprId { use ArgumentType::*; use FormatTrait::*; - let new_fn = self.ty_rel_lang_path_desugared_expr( + let new_fn = self.ty_rel_lang_path( self.lang_items().FormatArgument, match ty { Format(Display) => sym::new_display, @@ -992,6 +1005,22 @@ impl<'db> ExprCollector<'db> { Usize => sym::from_usize, }, ); + let new_fn = match new_fn { + Some(new_fn) => { + let new_fn = self.store.exprs.alloc(Expr::Path(new_fn)); + if let Some(arg_ptr) = arg_ptr { + // Trait errors (the argument does not implement the expected fmt trait) will show + // on this path, so to not end up with synthetic syntax we insert this mapping. We + // don't want to insert the other way's mapping in order to not override the source + // for the argument. + self.store + .expr_map_back + .insert(new_fn, self.expander.in_file(arg_ptr.wrap_left())); + } + new_fn + } + None => self.missing_expr(), + }; self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) }) } diff --git a/crates/hir-def/src/expr_store/lower/generics.rs b/crates/hir-def/src/expr_store/lower/generics.rs index 5ffc4f5851..7ef9c80de1 100644 --- a/crates/hir-def/src/expr_store/lower/generics.rs +++ b/crates/hir-def/src/expr_store/lower/generics.rs @@ -128,15 +128,7 @@ impl GenericParamsCollector { ); let default = const_param.default_val().map(|it| ec.lower_const_arg(it)); let param = ConstParamData { name, ty, default }; - let idx = self.type_or_consts.alloc(param.into()); - if let Some(default) = default - && let Some(const_expr_origins) = &mut ec.store.inference_roots - { - const_expr_origins.push(( - default.expr, - crate::expr_store::RootExprOrigin::ConstParam(idx), - )); - } + self.type_or_consts.alloc(param.into()); } ast::GenericParam::LifetimeParam(lifetime_param) => { let lifetime = ec.lower_lifetime_ref_opt(lifetime_param.lifetime()); diff --git a/crates/hir-def/src/expr_store/lower/path.rs b/crates/hir-def/src/expr_store/lower/path.rs index 579465e10f..236255c404 100644 --- a/crates/hir-def/src/expr_store/lower/path.rs +++ b/crates/hir-def/src/expr_store/lower/path.rs @@ -217,7 +217,7 @@ pub(super) fn lower_path( { let syn_ctxt = collector.expander.ctx_for_range(path.segment()?.syntax().text_range()); if let Some(macro_call_id) = syn_ctxt.outer_expn(collector.db) - && collector.db.lookup_intern_macro_call(macro_call_id.into()).def.local_inner + && hir_expand::MacroCallId::from(macro_call_id).loc(collector.db).def.local_inner { kind = match resolve_crate_root(collector.db, syn_ctxt) { Some(crate_root) => PathKind::DollarCrate(crate_root), diff --git a/crates/hir-def/src/expr_store/lower/path/tests.rs b/crates/hir-def/src/expr_store/lower/path/tests.rs index 6819eb3deb..f507841a91 100644 --- a/crates/hir-def/src/expr_store/lower/path/tests.rs +++ b/crates/hir-def/src/expr_store/lower/path/tests.rs @@ -21,7 +21,7 @@ fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option<Path>) { let (db, file_id) = TestDB::with_single_file(""); let krate = db.fetch_test_crate(); let mut ctx = - ExprCollector::signature(&db, crate_def_map(&db, krate).root_module_id(), file_id.into()); + ExprCollector::new(&db, crate_def_map(&db, krate).root_module_id(), file_id.into()); let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator); let (store, _) = ctx.store.finish(); (db, store, lowered_path) diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 70ea54c734..34cedbd728 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/crates/hir-def/src/expr_store/pretty.rs @@ -8,6 +8,7 @@ use std::{ use hir_expand::{Lookup, mod_path::PathKind}; use itertools::Itertools; +use rustc_abi::ExternAbi; use span::Edition; use stdx::never; use syntax::ast::{HasName, RangeOp}; @@ -17,8 +18,8 @@ use crate::{ attrs::AttrFlags, expr_store::path::{GenericArg, GenericArgs}, hir::{ - Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, RecordSpread, - Statement, + Array, BindingAnnotation, CaptureBy, ClosureKind, CoroutineKind, Literal, Movability, + RecordSpread, Statement, generics::{GenericParams, WherePredicate}, }, lang_item::LangItemTarget, @@ -91,7 +92,7 @@ pub fn print_body_hir( }; if let DefWithBodyId::FunctionId(_) = owner { p.buf.push('('); - if let Some(self_param) = body.self_param { + if let Some(self_param) = body.self_param() { p.print_binding(self_param); p.buf.push_str(", "); } @@ -292,7 +293,7 @@ pub fn print_function( if flags.contains(FnFlags::EXPLICIT_SAFE) { w!(p, "safe "); } - if let Some(abi) = abi { + if *abi != ExternAbi::Rust { w!(p, "extern \"{}\" ", abi.as_str()); } w!(p, "fn "); @@ -668,10 +669,7 @@ impl Printer<'_> { } } Expr::RecordLit { path, fields, spread } => { - match path { - Some(path) => self.print_path(path), - None => w!(self, "�"), - } + self.print_path(path); w!(self, "{{"); let edition = self.edition; @@ -764,28 +762,36 @@ impl Printer<'_> { let mut body = *body; let mut print_pipes = true; match closure_kind { - ClosureKind::Coroutine(Movability::Static) => { + ClosureKind::OldCoroutine(Movability::Static) => { w!(self, "static "); } - ClosureKind::AsyncClosure => { + ClosureKind::CoroutineClosure(kind) => { if let Expr::Closure { body: inner_body, - closure_kind: ClosureKind::AsyncBlock { .. }, + closure_kind: ClosureKind::Coroutine { .. }, .. } = self.store[body] { body = inner_body; } else { - never!("async closure should always have an async block body"); + never!("coroutine closure should always have a coroutine body"); } - w!(self, "async "); + match kind { + CoroutineKind::Async => w!(self, "async "), + CoroutineKind::Gen => w!(self, "gen "), + CoroutineKind::AsyncGen => w!(self, "async gen "), + } } - ClosureKind::AsyncBlock { .. } => { - w!(self, "async "); + ClosureKind::Coroutine { kind, .. } => { + match kind { + CoroutineKind::Async => w!(self, "async "), + CoroutineKind::Gen => w!(self, "gen "), + CoroutineKind::AsyncGen => w!(self, "async gen "), + } print_pipes = false; } - ClosureKind::Closure | ClosureKind::Coroutine(Movability::Movable) => (), + ClosureKind::Closure | ClosureKind::OldCoroutine(Movability::Movable) => (), } match capture_by { CaptureBy::Value => { @@ -898,6 +904,7 @@ impl Printer<'_> { match pat { Pat::Missing => w!(self, "�"), + Pat::Rest => w!(self, ".."), Pat::Wild => w!(self, "_"), Pat::Tuple { args, ellipsis } => { w!(self, "("); @@ -923,10 +930,7 @@ impl Printer<'_> { w!(self, ")"); } Pat::Record { path, args, ellipsis } => { - match path { - Some(path) => self.print_path(path), - None => w!(self, "�"), - } + self.print_path(path); w!(self, " {{"); let edition = self.edition; @@ -1004,10 +1008,7 @@ impl Printer<'_> { } } Pat::TupleStruct { path, args, ellipsis } => { - match path { - Some(path) => self.print_path(path), - None => w!(self, "�"), - } + self.print_path(path); w!(self, "("); for (i, arg) in args.iter().enumerate() { if i != 0 { @@ -1031,6 +1032,11 @@ impl Printer<'_> { w!(self, "box "); self.print_pat(*inner); } + Pat::Deref { inner } => { + w!(self, "deref!("); + self.print_pat(*inner); + w!(self, ")"); + } Pat::ConstBlock(c) => { w!(self, "const "); self.print_expr(*c); @@ -1310,9 +1316,9 @@ impl Printer<'_> { if fn_.is_unsafe { w!(self, "unsafe "); } - if let Some(abi) = &fn_.abi { + if fn_.abi != ExternAbi::Rust { w!(self, "extern "); - w!(self, "{}", abi.as_str()); + w!(self, "{}", fn_.abi.as_str()); w!(self, " "); } w!(self, "fn("); diff --git a/crates/hir-def/src/expr_store/scope.rs b/crates/hir-def/src/expr_store/scope.rs index c6ba0241b7..ddb8285137 100644 --- a/crates/hir-def/src/expr_store/scope.rs +++ b/crates/hir-def/src/expr_store/scope.rs @@ -7,7 +7,7 @@ use crate::{ db::DefDatabase, expr_store::{Body, ExpressionStore, HygieneId}, hir::{ - Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement, + Array, Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement, generics::GenericParams, }, signatures::VariantFields, @@ -147,11 +147,11 @@ impl ExprScopes { ), }; let mut root = scopes.root_scope(); - if let Some(self_param) = body.self_param { + if let Some(self_param) = body.self_param() { scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param)); } scopes.add_params_bindings(body, root, &body.params); - compute_expr_scopes(body.root_expr(), body, &mut scopes, &mut root); + compute_expr_scopes(body.root_expr(), body, &mut scopes, &mut { root }, &mut root); scopes } @@ -166,7 +166,7 @@ impl ExprScopes { let root = scopes.root_scope(); for root_expr in roots { let mut scope = scopes.new_scope(root); - compute_expr_scopes(root_expr, store, &mut scopes, &mut scope); + compute_expr_scopes(root_expr, store, &mut scopes, &mut { scope }, &mut scope); } scopes } @@ -270,31 +270,33 @@ fn compute_block_scopes( store: &ExpressionStore, scopes: &mut ExprScopes, scope: &mut ScopeId, + const_scope: &mut ScopeId, ) { for stmt in statements { match stmt { Statement::Let { pat, initializer, else_branch, .. } => { if let Some(expr) = initializer { - compute_expr_scopes(*expr, store, scopes, scope); + compute_expr_scopes(*expr, store, scopes, scope, const_scope); } if let Some(expr) = else_branch { - compute_expr_scopes(*expr, store, scopes, scope); + compute_expr_scopes(*expr, store, scopes, scope, const_scope); } *scope = scopes.new_scope(*scope); scopes.add_pat_bindings(store, *scope, *pat); } Statement::Expr { expr, .. } => { - compute_expr_scopes(*expr, store, scopes, scope); + compute_expr_scopes(*expr, store, scopes, scope, const_scope); } Statement::Item(Item::MacroDef(macro_id)) => { *scope = scopes.new_macro_def_scope(*scope, macro_id.clone()); + *const_scope = scopes.new_macro_def_scope(*const_scope, macro_id.clone()); } Statement::Item(Item::Other) => (), } } if let Some(expr) = tail { - compute_expr_scopes(expr, store, scopes, scope); + compute_expr_scopes(expr, store, scopes, scope, const_scope); } } @@ -303,69 +305,86 @@ fn compute_expr_scopes( store: &ExpressionStore, scopes: &mut ExprScopes, scope: &mut ScopeId, + const_scope: &mut ScopeId, ) { - let make_label = - |label: &Option<LabelId>| label.map(|label| (label, store[label].name.clone())); + let make_label = |label: Option<LabelId>| label.map(|label| (label, store[label].name.clone())); - let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| { - compute_expr_scopes(expr, store, scopes, scope) + let compute_expr_scopes = + |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId, const_scope: &mut ScopeId| { + compute_expr_scopes(expr, store, scopes, scope, const_scope) + }; + let handle_block = |id, + statements, + tail, + label, + scopes: &mut ExprScopes, + scope: &mut ScopeId, + const_scope: &mut ScopeId| { + let mut scope = scopes.new_block_scope(*scope, id, make_label(label)); + let mut const_scope = if id.is_some() { + scopes.new_block_scope(*const_scope, id, None) + } else { + // We don't need to allocate a new scope, since only items matter to us. + *const_scope + }; + // Overwrite the old scope for the block expr, so that every block scope can be found + // via the block itself (important for blocks that only contain items, no expressions). + scopes.set_scope(expr, scope); + compute_block_scopes(statements, tail, store, scopes, &mut scope, &mut const_scope); }; scopes.set_scope(expr, *scope); match &store[expr] { Expr::Block { statements, tail, id, label } => { - let mut scope = scopes.new_block_scope(*scope, *id, make_label(label)); - // Overwrite the old scope for the block expr, so that every block scope can be found - // via the block itself (important for blocks that only contain items, no expressions). - scopes.set_scope(expr, scope); - compute_block_scopes(statements, *tail, store, scopes, &mut scope); + handle_block(*id, statements, *tail, *label, scopes, scope, const_scope); } Expr::Const(id) => { - let mut scope = scopes.root_scope(); - compute_expr_scopes(scopes, *id, &mut scope); + let mut scope = *const_scope; + compute_expr_scopes(scopes, *id, &mut scope, const_scope); + } + Expr::Array(Array::Repeat { initializer, repeat }) => { + compute_expr_scopes(scopes, *initializer, scope, const_scope); + let mut repeat_scope = *const_scope; + compute_expr_scopes(scopes, *repeat, &mut repeat_scope, const_scope); } Expr::Unsafe { id, statements, tail } => { - let mut scope = scopes.new_block_scope(*scope, *id, None); - // Overwrite the old scope for the block expr, so that every block scope can be found - // via the block itself (important for blocks that only contain items, no expressions). - scopes.set_scope(expr, scope); - compute_block_scopes(statements, *tail, store, scopes, &mut scope); + handle_block(*id, statements, *tail, None, scopes, scope, const_scope); } Expr::Loop { body: body_expr, label } => { - let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); - compute_expr_scopes(scopes, *body_expr, &mut scope); + let mut scope = scopes.new_labeled_scope(*scope, make_label(*label)); + compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); } Expr::Closure { args, body: body_expr, .. } => { let mut scope = scopes.new_scope(*scope); scopes.add_params_bindings(store, scope, args); - compute_expr_scopes(scopes, *body_expr, &mut scope); + compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); } Expr::Match { expr, arms } => { - compute_expr_scopes(scopes, *expr, scope); + compute_expr_scopes(scopes, *expr, scope, const_scope); for arm in arms.iter() { let mut scope = scopes.new_scope(*scope); scopes.add_pat_bindings(store, scope, arm.pat); if let Some(guard) = arm.guard { scope = scopes.new_scope(scope); - compute_expr_scopes(scopes, guard, &mut scope); + compute_expr_scopes(scopes, guard, &mut scope, const_scope); } - compute_expr_scopes(scopes, arm.expr, &mut scope); + compute_expr_scopes(scopes, arm.expr, &mut scope, const_scope); } } &Expr::If { condition, then_branch, else_branch } => { let mut then_branch_scope = scopes.new_scope(*scope); - compute_expr_scopes(scopes, condition, &mut then_branch_scope); - compute_expr_scopes(scopes, then_branch, &mut then_branch_scope); + compute_expr_scopes(scopes, condition, &mut then_branch_scope, const_scope); + compute_expr_scopes(scopes, then_branch, &mut then_branch_scope, const_scope); if let Some(else_branch) = else_branch { - compute_expr_scopes(scopes, else_branch, scope); + compute_expr_scopes(scopes, else_branch, scope, const_scope); } } &Expr::Let { pat, expr } => { - compute_expr_scopes(scopes, expr, scope); + compute_expr_scopes(scopes, expr, scope, const_scope); *scope = scopes.new_scope(*scope); scopes.add_pat_bindings(store, *scope, pat); } - _ => store.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)), + _ => store.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope, const_scope)), }; } diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs index db12775df9..e97718ca22 100644 --- a/crates/hir-def/src/expr_store/tests/body.rs +++ b/crates/hir-def/src/expr_store/tests/body.rs @@ -652,9 +652,16 @@ fn async_fn_weird_param_patterns() { async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, 123: i32) {} "#, expect![[r#" - fn main(self, param1, mut param2, mut <ra@gennew>0, param4 @ _, mut <ra@gennew>1) async { + fn main(self, param1, mut param2, mut <ra@gennew>0, mut param4, mut <ra@gennew>1) async { + let self = self; + let mut param1 = param1; + let mut param2 = param2; let ref mut param2 = param2; + let mut <ra@gennew>0 = <ra@gennew>0; let _ = <ra@gennew>0; + let mut param4 = param4; + let param4 @ _ = param4; + let mut <ra@gennew>1 = <ra@gennew>1; let 123 = <ra@gennew>1; {} }"#]], diff --git a/crates/hir-def/src/expr_store/tests/signatures.rs b/crates/hir-def/src/expr_store/tests/signatures.rs index 5e0184dfad..24bdc6ece3 100644 --- a/crates/hir-def/src/expr_store/tests/signatures.rs +++ b/crates/hir-def/src/expr_store/tests/signatures.rs @@ -210,3 +210,17 @@ fn foo(v: for<'a> Trait1 + Trait2) {} "#]], ); } + +#[test] +fn extern_block_abi() { + lower_and_print( + r#" +extern "C" { + fn extern_fn(); +} + "#, + expect![[r#" + extern "C" fn extern_fn() {...} + "#]], + ); +} diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index 8308203693..2a1b7f7cb0 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -177,7 +177,7 @@ fn find_path_for_module( path: ModPath::from_segments(PathKind::Crate, None), path_text_len: 5, stability: Stable, - prefer_due_to_prelude: false, + has_prelude_segment: false, }); } // - otherwise if the item is the crate root of a dependency crate, return the name from the extern prelude @@ -205,7 +205,7 @@ fn find_path_for_module( } else { PathKind::Plain }; - return Some(Choice::new(ctx.cfg.prefer_prelude, kind, name.clone(), Stable)); + return Some(Choice::new(kind, name.clone(), Stable)); } } @@ -223,12 +223,7 @@ fn find_path_for_module( ); if let Some(scope_name) = scope_name { // - if the item is already in scope, return the name under which it is - return Some(Choice::new( - ctx.cfg.prefer_prelude, - ctx.prefix.path_kind(), - scope_name, - Stable, - )); + return Some(Choice::new(ctx.prefix.path_kind(), scope_name, Stable)); } } @@ -240,7 +235,7 @@ fn find_path_for_module( path: ModPath::from_segments(kind, None), path_text_len: path_kind_len(kind), stability: Stable, - prefer_due_to_prelude: false, + has_prelude_segment: false, }); } @@ -302,7 +297,7 @@ fn find_in_prelude( }); if found_and_same_def.unwrap_or(true) { - Some(Choice::new(false, PathKind::Plain, name.clone(), Stable)) + Some(Choice::new(PathKind::Plain, name.clone(), Stable)) } else { None } @@ -439,13 +434,7 @@ fn find_in_dep( // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? - let choice = find_path_for_module( - ctx, - visited_modules, - info.container, - true, - best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1, - ); + let choice = find_path_for_module(ctx, visited_modules, info.container, true, max_len - 1); let Some(mut choice) = choice else { continue; }; @@ -471,15 +460,11 @@ fn calculate_best_path_local( ) { // FIXME: cache the `find_local_import_locations` output? find_local_import_locations(ctx, item, visited_modules, |visited_modules, name, module_id| { - // we are looking for paths of length up to best_path_len, any longer will make it be - // less optimal. The -1 is due to us pushing name onto it afterwards. - if let Some(choice) = find_path_for_module( - ctx, - visited_modules, - module_id, - false, - best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1, - ) { + // The container path may be at most `max_len - 1` segments since we push + // `name` on top of it. + if let Some(choice) = + find_path_for_module(ctx, visited_modules, module_id, false, max_len - 1) + { Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone()); } }); @@ -492,23 +477,23 @@ struct Choice { path_text_len: usize, /// The stability of the path stability: Stability, - /// Whether this path contains a prelude segment and preference for it has been signaled - prefer_due_to_prelude: bool, + /// Whether any segment of this path is named `prelude` + has_prelude_segment: bool, } impl Choice { - fn new(prefer_prelude: bool, kind: PathKind, name: Name, stability: Stability) -> Self { + fn new(kind: PathKind, name: Name, stability: Stability) -> Self { Self { path_text_len: path_kind_len(kind) + name.as_str().len(), stability, - prefer_due_to_prelude: prefer_prelude && name == sym::prelude, + has_prelude_segment: name == sym::prelude, path: ModPath::from_segments(kind, iter::once(name)), } } - fn push(mut self, prefer_prelude: bool, name: Name) -> Self { + fn push(mut self, name: Name) -> Self { self.path_text_len += name.as_str().len(); - self.prefer_due_to_prelude |= prefer_prelude && name == sym::prelude; + self.has_prelude_segment |= name == sym::prelude; self.path.push_segment(name); self } @@ -520,13 +505,19 @@ impl Choice { name: Name, ) { let Some(current) = current else { - *current = Some(other.push(prefer_prelude, name)); + *current = Some(other.push(name)); return; }; match other .stability .cmp(¤t.stability) - .then_with(|| other.prefer_due_to_prelude.cmp(¤t.prefer_due_to_prelude)) + .then_with(|| { + if prefer_prelude { + other.has_prelude_segment.cmp(¤t.has_prelude_segment) + } else { + current.has_prelude_segment.cmp(&other.has_prelude_segment) + } + }) .then_with(|| (current.path.len()).cmp(&(other.path.len() + 1))) { Ordering::Less => return, @@ -684,7 +675,7 @@ mod tests { let ast_path = parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); let mod_path = ModPath::from_src(&db, ast_path, &mut |range| { - db.span_map(pos.file_id.into()).as_ref().span_for_range(range).ctx + db.span_map(pos.file_id.into()).span_for_range(range).ctx }) .unwrap(); @@ -2032,6 +2023,50 @@ pub mod foo { } #[test] + fn avoids_prelude_when_prefer_prelude_false() { + let ra_fixture = r#" +//- /main.rs crate:main deps:krate +$0 +//- /krate.rs crate:krate +pub mod prelude { + pub use crate::module::sub::*; +} +pub mod module { + pub mod sub { + pub struct Foo; + } +} +"#; + // krate::prelude::Foo (3 segs) is shorter than krate::module::sub::Foo (4 segs), + // but prefer_prelude=false should pick the longer canonical path. + check_found_path( + ra_fixture, + "krate::module::sub::Foo", + expect![[r#" + Plain (imports ✔): krate::module::sub::Foo + Plain (imports ✖): krate::module::sub::Foo + ByCrate(imports ✔): krate::module::sub::Foo + ByCrate(imports ✖): krate::module::sub::Foo + BySelf (imports ✔): krate::module::sub::Foo + BySelf (imports ✖): krate::module::sub::Foo + "#]], + ); + // prefer_prelude=true should still pick the shorter prelude path. + check_found_path_prelude( + ra_fixture, + "krate::prelude::Foo", + expect![[r#" + Plain (imports ✔): krate::prelude::Foo + Plain (imports ✖): krate::prelude::Foo + ByCrate(imports ✔): krate::prelude::Foo + ByCrate(imports ✖): krate::prelude::Foo + BySelf (imports ✔): krate::prelude::Foo + BySelf (imports ✖): krate::prelude::Foo + "#]], + ); + } + + #[test] fn respects_absolute_setting() { let ra_fixture = r#" //- /main.rs crate:main deps:krate diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 9e51d0eac9..6eba859264 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -259,7 +259,7 @@ pub enum Expr { expr: Option<ExprId>, }, RecordLit { - path: Option<Box<Path>>, + path: Path, fields: Box<[RecordLitField]>, spread: RecordSpread, }, @@ -524,12 +524,19 @@ pub enum InlineAsmRegOrRegClass { RegClass(Symbol), } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum CoroutineKind { + Async, + Gen, + AsyncGen, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ClosureKind { Closure, - Coroutine(Movability), - AsyncBlock { source: CoroutineSource }, - AsyncClosure, + OldCoroutine(Movability), + Coroutine { kind: CoroutineKind, source: CoroutineSource }, + CoroutineClosure(CoroutineKind), } /// In the case of a coroutine created as part of an async/gen construct, @@ -557,7 +564,7 @@ pub enum CaptureBy { Ref, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Movability { Static, Movable, @@ -666,6 +673,8 @@ pub struct RecordFieldPat { #[derive(Debug, Clone, Eq, PartialEq)] pub enum Pat { Missing, + /// A rest pattern. Not valid outside special context. + Rest, Wild, Tuple { args: Box<[PatId]>, @@ -673,7 +682,7 @@ pub enum Pat { }, Or(Box<[PatId]>), Record { - path: Option<Box<Path>>, + path: Path, args: Box<[RecordFieldPat]>, ellipsis: bool, }, @@ -687,7 +696,6 @@ pub enum Pat { slice: Option<PatId>, suffix: Box<[PatId]>, }, - /// This might refer to a variable if a single segment path (specifically, on destructuring assignment). Path(Path), Lit(ExprId), Bind { @@ -695,7 +703,7 @@ pub enum Pat { subpat: Option<PatId>, }, TupleStruct { - path: Option<Box<Path>>, + path: Path, args: Box<[PatId]>, ellipsis: Option<u32>, }, @@ -706,6 +714,9 @@ pub enum Pat { Box { inner: PatId, }, + Deref { + inner: PatId, + }, ConstBlock(ExprId), /// An expression inside a pattern. That can only occur inside assignments. /// @@ -722,6 +733,7 @@ impl Pat { | Pat::ConstBlock(..) | Pat::Wild | Pat::Missing + | Pat::Rest | Pat::Expr(_) => {} Pat::Bind { subpat, .. } => { subpat.iter().copied().for_each(f); @@ -737,7 +749,7 @@ impl Pat { Pat::Record { args, .. } => { args.iter().map(|f| f.pat).for_each(f); } - Pat::Box { inner } => f(*inner), + Pat::Box { inner } | Pat::Deref { inner } => f(*inner), } } } diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs index 271484da7b..366857f233 100644 --- a/crates/hir-def/src/hir/format_args.rs +++ b/crates/hir-def/src/hir/format_args.rs @@ -7,7 +7,7 @@ use rustc_parse_format as parse; use span::SyntaxContext; use stdx::TupleExt; use syntax::{ - TextRange, + AstPtr, TextRange, ast::{self, IsString}, }; @@ -146,6 +146,7 @@ pub enum FormatCount { pub struct FormatArgument { pub kind: FormatArgumentKind, pub expr: ExprId, + pub syntax: Option<AstPtr<ast::Expr>>, } #[derive(Clone, PartialEq, Eq, Debug)] @@ -171,6 +172,7 @@ use PositionUsedAs::*; #[allow(clippy::unnecessary_lazy_evaluations)] pub(crate) fn parse( s: &ast::String, + string_ptr: AstPtr<ast::Expr>, fmt_snippet: Option<String>, mut args: FormatArgumentsCollector, is_direct_literal: bool, @@ -273,6 +275,11 @@ pub(crate) fn parse( // FIXME: This is problematic, we might want to synthesize a dummy // expression proper and/or desugar these. expr: synth(name, span), + // FIXME: This will lead us to show failed trait bounds (e.g. `T: Display`) + // on the whole template string for implicit arguments instead of just their name. + // Fixing this is hard since we don't have an `AstPtr` for the arg, and it's + // only part of an expression. + syntax: Some(string_ptr), })) } } diff --git a/crates/hir-def/src/hir/generics.rs b/crates/hir-def/src/hir/generics.rs index 43dd7d1c54..04ccc7bf90 100644 --- a/crates/hir-def/src/hir/generics.rs +++ b/crates/hir-def/src/hir/generics.rs @@ -188,6 +188,11 @@ impl GenericParams { pub const SELF_PARAM_ID_IN_SELF: la_arena::Idx<TypeOrConstParamData> = LocalTypeOrConstParamId::from_raw(RawIdx::from_u32(0)); + #[inline] + pub fn empty() -> &'static GenericParams { + LazyLock::force(&EMPTY) + } + pub fn of(db: &dyn DefDatabase, def: GenericDefId) -> &GenericParams { Self::with_store(db, def).0 } diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index b64199fa26..43e29c1f5f 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -2,16 +2,13 @@ //! be directly created from an ast::TypeRef, without further queries. use hir_expand::name::Name; -use intern::Symbol; use la_arena::Idx; +use rustc_abi::ExternAbi; use thin_vec::ThinVec; use crate::{ LifetimeParamId, TypeParamId, - expr_store::{ - ExpressionStore, - path::{GenericArg, Path}, - }, + expr_store::{ExpressionStore, path::Path}, hir::ExprId, }; @@ -100,7 +97,7 @@ pub struct FnType { pub params: Box<[(Option<Name>, TypeRefId)]>, pub is_varargs: bool, pub is_unsafe: bool, - pub abi: Option<Symbol>, + pub abi: ExternAbi, } impl FnType { @@ -191,71 +188,6 @@ impl TypeRef { pub(crate) fn unit() -> TypeRef { TypeRef::Tuple(ThinVec::new()) } - - pub fn walk(this: TypeRefId, map: &ExpressionStore, f: &mut impl FnMut(TypeRefId, &TypeRef)) { - go(this, f, map); - - fn go( - type_ref_id: TypeRefId, - f: &mut impl FnMut(TypeRefId, &TypeRef), - map: &ExpressionStore, - ) { - let type_ref = &map[type_ref_id]; - f(type_ref_id, type_ref); - match type_ref { - TypeRef::Fn(fn_) => { - fn_.params.iter().for_each(|&(_, param_type)| go(param_type, f, map)) - } - TypeRef::Tuple(types) => types.iter().for_each(|&t| go(t, f, map)), - TypeRef::RawPtr(type_ref, _) | TypeRef::Slice(type_ref) => go(*type_ref, f, map), - TypeRef::Reference(it) => go(it.ty, f, map), - TypeRef::Array(it) => go(it.ty, f, map), - TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => { - for bound in bounds { - match bound { - &TypeBound::Path(path, _) | &TypeBound::ForLifetime(_, path) => { - go_path(&map[path], f, map) - } - TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (), - } - } - } - TypeRef::Path(path) => go_path(path, f, map), - TypeRef::Never | TypeRef::Placeholder | TypeRef::Error | TypeRef::TypeParam(_) => {} - }; - } - - fn go_path(path: &Path, f: &mut impl FnMut(TypeRefId, &TypeRef), map: &ExpressionStore) { - if let Some(type_ref) = path.type_anchor() { - go(type_ref, f, map); - } - for segment in path.segments().iter() { - if let Some(args_and_bindings) = segment.args_and_bindings { - for arg in args_and_bindings.args.iter() { - match arg { - GenericArg::Type(type_ref) => { - go(*type_ref, f, map); - } - GenericArg::Const(_) | GenericArg::Lifetime(_) => {} - } - } - for binding in args_and_bindings.bindings.iter() { - if let Some(type_ref) = binding.type_ref { - go(type_ref, f, map); - } - for bound in binding.bounds.iter() { - match bound { - &TypeBound::Path(path, _) | &TypeBound::ForLifetime(_, path) => { - go_path(&map[path], f, map) - } - TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (), - } - } - } - } - } - } - } } impl TypeBound { diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index e7ab2b390f..4b50161c04 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -61,7 +61,6 @@ use span::{ use stdx::never; use syntax::{SourceFile, SyntaxKind, ast, match_ast}; use thin_vec::ThinVec; -use triomphe::Arc; use tt::TextRange; use crate::{BlockId, Lookup, attrs::parse_extra_crate_attrs, db::DefDatabase}; @@ -121,14 +120,30 @@ fn lower_extra_crate_attrs<'a>( AttrsOrCfg::lower(db, &crate_attrs_as_src, cfg_options, span_map) } -#[salsa_macros::tracked(returns(deref))] -pub(crate) fn file_item_tree_query( +pub(crate) fn file_item_tree(db: &dyn DefDatabase, file_id: HirFileId, krate: Crate) -> &ItemTree { + match file_item_tree_query(db, file_id, krate) { + Some(item_tree) => item_tree, + None => { + static EMPTY: OnceLock<ItemTree> = OnceLock::new(); + EMPTY.get_or_init(|| ItemTree { + top_level: Box::new([]), + attrs: FxHashMap::default(), + small_data: FxHashMap::default(), + big_data: FxHashMap::default(), + top_attrs: AttrsOrCfg::empty(), + vis: ItemVisibilities { arena: ThinVec::new() }, + }) + } + } +} + +#[salsa_macros::tracked(returns(ref))] +fn file_item_tree_query( db: &dyn DefDatabase, file_id: HirFileId, krate: Crate, -) -> Arc<ItemTree> { +) -> Option<Box<ItemTree>> { let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered(); - static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new(); let ctx = lower::Ctx::new(db, file_id, krate); let syntax = db.parse_or_expand(file_id); @@ -174,21 +189,10 @@ pub(crate) fn file_item_tree_query( && top_attrs.is_empty() && vis.arena.is_empty() { - EMPTY - .get_or_init(|| { - Arc::new(ItemTree { - top_level: Box::new([]), - attrs: FxHashMap::default(), - small_data: FxHashMap::default(), - big_data: FxHashMap::default(), - top_attrs: AttrsOrCfg::empty(), - vis: ItemVisibilities { arena: ThinVec::new() }, - }) - }) - .clone() + None } else { item_tree.shrink_to_fit(); - Arc::new(item_tree) + Some(Box::new(item_tree)) } } @@ -343,7 +347,7 @@ impl TreeId { pub(crate) fn item_tree<'db>(&self, db: &'db dyn DefDatabase, krate: Crate) -> &'db ItemTree { match self.block { Some(block) => block_item_tree_query(db, block, krate), - None => file_item_tree_query(db, self.file, krate), + None => file_item_tree(db, self.file, krate), } } diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 31e409d86e..91c2e60fd7 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -4,12 +4,7 @@ use std::cell::OnceCell; use base_db::{Crate, FxIndexSet}; use cfg::CfgOptions; -use hir_expand::{ - HirFileId, - mod_path::PathKind, - name::AsName, - span_map::{SpanMap, SpanMapRef}, -}; +use hir_expand::{HirFileId, mod_path::PathKind, name::AsName, span_map::SpanMap}; use la_arena::Arena; use span::{AstIdMap, FileAstId, SyntaxContext}; use syntax::{ @@ -32,7 +27,7 @@ pub(super) struct Ctx<'db> { pub(super) db: &'db dyn DefDatabase, tree: ItemTree, source_ast_id_map: &'db AstIdMap, - span_map: OnceCell<SpanMap>, + span_map: OnceCell<SpanMap<'db>>, file: HirFileId, cfg_options: OnceCell<&'db CfgOptions>, krate: Crate, @@ -60,12 +55,12 @@ impl<'db> Ctx<'db> { self.cfg_options.get_or_init(|| self.krate.cfg_options(self.db)) } - pub(super) fn span_map(&self) -> SpanMapRef<'_> { - self.span_map.get_or_init(|| self.db.span_map(self.file)).as_ref() + pub(super) fn span_map(&self) -> SpanMap<'_> { + *self.span_map.get_or_init(|| self.db.span_map(self.file)) } pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree { - self.top_level = item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect(); + self.top_level = item_owner.items().filter_map(|item| self.lower_mod_item(&item)).collect(); self.tree.vis.arena = self.visibilities.into_iter().collect(); self.tree.top_level = self.top_level.into_boxed_slice(); self.tree @@ -89,7 +84,7 @@ impl<'db> Ctx<'db> { _ => None, } }) - .flat_map(|item| self.lower_mod_item(&item)) + .filter_map(|item| self.lower_mod_item(&item)) .collect(); if let Some(ast::Expr::MacroExpr(tail_macro)) = stmts.expr() @@ -245,7 +240,9 @@ impl<'db> Ctx<'db> { ModKind::Inline { items: module .item_list() - .map(|list| list.items().flat_map(|item| self.lower_mod_item(&item)).collect()) + .map(|list| { + list.items().filter_map(|item| self.lower_mod_item(&item)).collect() + }) .unwrap_or_else(|| { cov_mark::hit!(name_res_works_for_broken_modules); Box::new([]) as Box<[_]> diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 37d70b1e33..c2cbb9eda7 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -7,8 +7,8 @@ use intern::{Symbol, sym}; use stdx::impl_from; use crate::{ - AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, MacroId, - ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, + AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, + ItemContainerId, MacroId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, attrs::AttrFlags, db::DefDatabase, nameres::{DefMap, assoc::TraitItems, crate_def_map, crate_local_def_map}, @@ -40,7 +40,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt let crate_def_map = crate_def_map(db, krate); - if !crate_def_map.is_unstable_feature_enabled(&sym::lang_items) { + if !crate_def_map.features().lang_items { return None; } @@ -102,6 +102,8 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt lang_items.fill_non_lang_core_items(db, crate_def_map); } + lang_items.resolve_manually(db); + if lang_items.is_empty() { None } else { Some(Box::new(lang_items)) } } @@ -190,6 +192,122 @@ fn resolve_core_macro( current.scope.makro(&Name::new_symbol_root(name)) } +impl LangItems { + fn resolve_manually(&mut self, db: &dyn DefDatabase) { + let parent_trait = + |lang_item: &mut Option<TraitId>, def: Option<FunctionId>| match def?.loc(db).container + { + ItemContainerId::TraitId(trait_) => { + *lang_item = Some(trait_); + Some(trait_) + } + _ => None, + }; + let assoc_types = + |trait_: TraitId, assoc_types: &mut [(&mut Option<TypeAliasId>, Symbol)]| { + let trait_items = trait_.trait_items(db); + for (assoc_type, name) in assoc_types { + **assoc_type = + trait_items.associated_type_by_name(&Name::new_symbol_root(name.clone())); + } + }; + let methods = |trait_: TraitId, assoc_types: &mut [(&mut Option<FunctionId>, Symbol)]| { + let trait_items = trait_.trait_items(db); + for (assoc_type, name) in assoc_types { + **assoc_type = trait_items.method_by_name(&Name::new_symbol_root(name.clone())); + } + }; + (|| { + let into_future = parent_trait(&mut self.IntoFuture, self.IntoFutureIntoFuture)?; + assoc_types(into_future, &mut [(&mut self.IntoFutureOutput, sym::Output)]); + Some(()) + })(); + + (|| { + let into_iterator = parent_trait(&mut self.IntoIterator, self.IntoIterIntoIter)?; + assoc_types( + into_iterator, + &mut [ + (&mut self.IntoIteratorItem, sym::Item), + (&mut self.IntoIterIntoIterType, sym::IntoIter), + ], + ); + Some(()) + })(); + + (|| { + assoc_types(self.Iterator?, &mut [(&mut self.IteratorItem, sym::Item)]); + Some(()) + })(); + + (|| { + assoc_types(self.AsyncIterator?, &mut [(&mut self.AsyncIteratorItem, sym::Item)]); + Some(()) + })(); + + for (op_trait, op_method, op_method_name) in [ + (self.Fn, &mut self.Fn_call, sym::call), + (self.FnMut, &mut self.FnMut_call_mut, sym::call_mut), + (self.FnOnce, &mut self.FnOnce_call_once, sym::call_once), + (self.AsyncFn, &mut self.AsyncFn_async_call, sym::async_call), + (self.AsyncFnMut, &mut self.AsyncFnMut_async_call_mut, sym::async_call_mut), + (self.AsyncFnOnce, &mut self.AsyncFnOnce_async_call_once, sym::async_call_once), + (self.Not, &mut self.Not_not, sym::not), + (self.Neg, &mut self.Neg_neg, sym::neg), + (self.Add, &mut self.Add_add, sym::add), + (self.Mul, &mut self.Mul_mul, sym::mul), + (self.Sub, &mut self.Sub_sub, sym::sub), + (self.Div, &mut self.Div_div, sym::div), + (self.Rem, &mut self.Rem_rem, sym::rem), + (self.Shl, &mut self.Shl_shl, sym::shl), + (self.Shr, &mut self.Shr_shr, sym::shr), + (self.BitXor, &mut self.BitXor_bitxor, sym::bitxor), + (self.BitOr, &mut self.BitOr_bitor, sym::bitor), + (self.BitAnd, &mut self.BitAnd_bitand, sym::bitand), + (self.AddAssign, &mut self.AddAssign_add_assign, sym::add_assign), + (self.MulAssign, &mut self.MulAssign_mul_assign, sym::mul_assign), + (self.SubAssign, &mut self.SubAssign_sub_assign, sym::sub_assign), + (self.DivAssign, &mut self.DivAssign_div_assign, sym::div_assign), + (self.RemAssign, &mut self.RemAssign_rem_assign, sym::rem_assign), + (self.ShlAssign, &mut self.ShlAssign_shl_assign, sym::shl_assign), + (self.ShrAssign, &mut self.ShrAssign_shr_assign, sym::shr_assign), + (self.BitXorAssign, &mut self.BitXorAssign_bitxor_assign, sym::bitxor_assign), + (self.BitOrAssign, &mut self.BitOrAssign_bitor_assign, sym::bitor_assign), + (self.BitAndAssign, &mut self.BitAndAssign_bitand_assign, sym::bitand_assign), + (self.Drop, &mut self.Drop_drop, sym::drop), + (self.Debug, &mut self.Debug_fmt, sym::fmt), + (self.Deref, &mut self.Deref_deref, sym::deref), + (self.DerefMut, &mut self.DerefMut_deref_mut, sym::deref_mut), + (self.Index, &mut self.Index_index, sym::index), + (self.IndexMut, &mut self.IndexMut_index_mut, sym::index_mut), + ] { + (|| { + methods(op_trait?, &mut [(op_method, op_method_name)]); + Some(()) + })(); + } + (|| { + methods( + self.PartialEq?, + &mut [(&mut self.PartialEq_eq, sym::eq), (&mut self.PartialEq_ne, sym::ne)], + ); + Some(()) + })(); + (|| { + methods( + self.PartialOrd?, + &mut [ + (&mut self.PartialOrd_le, sym::le), + (&mut self.PartialOrd_lt, sym::lt), + (&mut self.PartialOrd_ge, sym::ge), + (&mut self.PartialOrd_gt, sym::gt), + ], + ); + Some(()) + })(); + } +} + #[salsa::tracked(returns(as_deref))] pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option<Box<[TraitId]>> { let mut traits = Vec::new(); @@ -221,6 +339,10 @@ macro_rules! language_item_table { @non_lang_core_macros: $( core::$($non_lang_macro_module:ident)::*, $non_lang_macro:ident, $non_lang_macro_field:ident; )* + + @resolve_manually: + + $( $resolve_manually:ident, $resolve_manually_type:ident; )* ) => { #[allow(non_snake_case)] // FIXME: Should we remove this? #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] @@ -235,6 +357,9 @@ macro_rules! language_item_table { $( pub $non_lang_macro_field: Option<MacroId>, )* + $( + pub $resolve_manually: Option<$resolve_manually_type>, + )* } impl LangItems { @@ -247,6 +372,7 @@ macro_rules! language_item_table { $( self.$lang_item = self.$lang_item.or(other.$lang_item); )* $( self.$non_lang_trait = self.$non_lang_trait.or(other.$non_lang_trait); )* $( self.$non_lang_macro_field = self.$non_lang_macro_field.or(other.$non_lang_macro_field); )* + $( self.$resolve_manually = self.$resolve_manually.or(other.$resolve_manually); )* } fn assign_lang_item(&mut self, name: Symbol, target: LangItemTarget) { @@ -365,9 +491,10 @@ language_item_table! { LangItems => Deref, sym::deref, TraitId; DerefMut, sym::deref_mut, TraitId; + DerefPure, sym::deref_pure, TraitId; DerefTarget, sym::deref_target, TypeAliasId; Receiver, sym::receiver, TraitId; - ReceiverTarget, sym::receiver_target, TypeAliasId; + ReceiverTarget, sym::receiver_target, TypeAliasId; Fn, sym::fn_, TraitId; FnMut, sym::fn_mut, TraitId; @@ -385,6 +512,7 @@ language_item_table! { LangItems => FnOnceOutput, sym::fn_once_output, TypeAliasId; Future, sym::future_trait, TraitId; + AsyncIterator, sym::async_iterator, TraitId; CoroutineState, sym::coroutine_state, EnumId; Coroutine, sym::coroutine, TraitId; CoroutineReturn, sym::coroutine_return, TypeAliasId; @@ -494,7 +622,6 @@ language_item_table! { LangItems => IteratorNext, sym::next, FunctionId; Iterator, sym::iterator, TraitId; FusedIterator, sym::fused_iterator, TraitId; - AsyncIterator, sym::async_iterator, TraitId; PinNewUnchecked, sym::new_unchecked, FunctionId; @@ -537,4 +664,55 @@ language_item_table! { LangItems => core::marker, CoercePointee, CoercePointeeDerive; core::marker, Copy, CopyDerive; core::clone, Clone, CloneDerive; + + @resolve_manually: + + IntoFuture, TraitId; + IntoFutureOutput, TypeAliasId; + IntoIterator, TraitId; + IntoIteratorItem, TypeAliasId; + IntoIterIntoIterType, TypeAliasId; + IteratorItem, TypeAliasId; + AsyncIteratorItem, TypeAliasId; + + Fn_call, FunctionId; + FnMut_call_mut, FunctionId; + FnOnce_call_once, FunctionId; + AsyncFn_async_call, FunctionId; + AsyncFnMut_async_call_mut, FunctionId; + AsyncFnOnce_async_call_once, FunctionId; + Not_not, FunctionId; + Neg_neg, FunctionId; + Add_add, FunctionId; + Mul_mul, FunctionId; + Sub_sub, FunctionId; + Div_div, FunctionId; + Rem_rem, FunctionId; + Shl_shl, FunctionId; + Shr_shr, FunctionId; + BitXor_bitxor, FunctionId; + BitOr_bitor, FunctionId; + BitAnd_bitand, FunctionId; + AddAssign_add_assign, FunctionId; + MulAssign_mul_assign, FunctionId; + SubAssign_sub_assign, FunctionId; + DivAssign_div_assign, FunctionId; + RemAssign_rem_assign, FunctionId; + ShlAssign_shl_assign, FunctionId; + ShrAssign_shr_assign, FunctionId; + BitXorAssign_bitxor_assign, FunctionId; + BitOrAssign_bitor_assign, FunctionId; + BitAndAssign_bitand_assign, FunctionId; + PartialEq_eq, FunctionId; + PartialEq_ne, FunctionId; + PartialOrd_le, FunctionId; + PartialOrd_lt, FunctionId; + PartialOrd_ge, FunctionId; + PartialOrd_gt, FunctionId; + Drop_drop, FunctionId; + Debug_fmt, FunctionId; + Deref_deref, FunctionId; + DerefMut_deref_mut, FunctionId; + Index_index, FunctionId; + IndexMut_index_mut, FunctionId; } diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 22c9073a58..3aaf89102f 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -15,8 +15,8 @@ extern crate rustc_parse_format; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_parse_format as rustc_parse_format; -extern crate ra_ap_rustc_abi as rustc_abi; pub extern crate ra_ap_rustc_abi as layout; +pub extern crate ra_ap_rustc_abi as rustc_abi; pub mod db; @@ -33,6 +33,7 @@ pub mod item_tree; pub mod builtin_derive; pub mod lang_item; +pub mod unstable_features; pub mod hir; pub use self::hir::type_ref; @@ -47,7 +48,8 @@ pub mod find_path; pub mod import_map; pub mod visibility; -use intern::{Interned, Symbol}; +use intern::Interned; +use rustc_abi::ExternAbi; use thin_vec::ThinVec; pub use crate::signatures::LocalFieldId; @@ -85,19 +87,14 @@ use crate::{ builtin_type::BuiltinType, db::DefDatabase, expr_store::ExpressionStoreSourceMap, - hir::{ - ExprId, - generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId}, - }, + hir::generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId}, nameres::{ LocalDefMap, assoc::{ImplItems, TraitItems}, block_def_map, crate_def_map, crate_local_def_map, diagnostics::DefDiagnostics, }, - signatures::{ - ConstSignature, EnumVariants, InactiveEnumVariantCode, StaticSignature, VariantFields, - }, + signatures::{EnumVariants, InactiveEnumVariantCode, VariantFields}, }; type FxIndexMap<K, V> = indexmap::IndexMap<K, V, rustc_hash::FxBuildHasher>; @@ -223,9 +220,9 @@ impl<N: AstIdNode> AstIdLoc for AssocItemLoc<N> { } macro_rules! impl_intern { - ($id:ident, $loc:ident, $intern:ident, $lookup:ident) => { + ($id:ident, $loc:ident) => { impl_intern_key!($id, $loc); - impl_intern_lookup!(DefDatabase, $id, $loc, $intern, $lookup); + impl_intern_lookup!(DefDatabase, $id, $loc); }; } @@ -252,10 +249,10 @@ macro_rules! impl_loc { } type FunctionLoc = AssocItemLoc<ast::Fn>; -impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function); +impl_intern!(FunctionId, FunctionLoc); type StructLoc = ItemLoc<ast::Struct>; -impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct); +impl_intern!(StructId, StructLoc); impl StructId { pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { @@ -272,7 +269,7 @@ impl StructId { } pub type UnionLoc = ItemLoc<ast::Union>; -impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union); +impl_intern!(UnionId, UnionLoc); impl UnionId { pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { @@ -289,7 +286,7 @@ impl UnionId { } pub type EnumLoc = ItemLoc<ast::Enum>; -impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum); +impl_intern!(EnumId, EnumLoc); impl EnumId { #[inline] @@ -307,26 +304,13 @@ impl EnumId { } type ConstLoc = AssocItemLoc<ast::Const>; -impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const); +impl_intern!(ConstId, ConstLoc); pub type StaticLoc = AssocItemLoc<ast::Static>; -impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static); - -/// An anonymous const expression that appears in a type position (e.g., array lengths, -/// const generic arguments like `{ N + 1 }`). Unlike named constants, these don't have -/// their own `Body` — their expressions live in the parent's signature `ExpressionStore`. -#[derive(Debug, Hash, PartialEq, Eq, Clone)] -pub struct AnonConstLoc { - /// The owner store containing this expression. - pub owner: ExpressionStoreOwnerId, - /// The ExprId within the owner's ExpressionStore that is the root - /// of this anonymous const expression. - pub expr: ExprId, -} -impl_intern!(AnonConstId, AnonConstLoc, intern_anon_const, lookup_intern_anon_const); +impl_intern!(StaticId, StaticLoc); pub type TraitLoc = ItemLoc<ast::Trait>; -impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait); +impl_intern!(TraitId, TraitLoc); impl TraitId { #[inline] @@ -336,10 +320,10 @@ impl TraitId { } type TypeAliasLoc = AssocItemLoc<ast::TypeAlias>; -impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); +impl_intern!(TypeAliasId, TypeAliasLoc); type ImplLoc = ItemLoc<ast::Impl>; -impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); +impl_intern!(ImplId, ImplLoc); impl ImplId { #[inline] @@ -369,18 +353,16 @@ pub struct BuiltinDeriveImplId { } type UseLoc = ItemLoc<ast::Use>; -impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use); +impl_intern!(UseId, UseLoc); type ExternCrateLoc = ItemLoc<ast::ExternCrate>; -impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate); +impl_intern!(ExternCrateId, ExternCrateLoc); type ExternBlockLoc = ItemLoc<ast::ExternBlock>; -impl_intern!(ExternBlockId, ExternBlockLoc, intern_extern_block, lookup_intern_extern_block); +impl_intern!(ExternBlockId, ExternBlockLoc); -#[salsa::tracked] impl ExternBlockId { - #[salsa::tracked] - pub fn abi(self, db: &dyn DefDatabase) -> Option<Symbol> { + pub fn abi(self, db: &dyn DefDatabase) -> ExternAbi { signatures::extern_block_abi(db, self) } } @@ -391,7 +373,7 @@ pub struct EnumVariantLoc { pub parent: EnumId, pub index: u32, } -impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_enum_variant); +impl_intern!(EnumVariantId, EnumVariantLoc); impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId); impl EnumVariantId { @@ -416,7 +398,7 @@ pub struct Macro2Loc { pub allow_internal_unsafe: bool, pub edition: Edition, } -impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2); +impl_intern!(Macro2Id, Macro2Loc); impl_loc!(Macro2Loc, id: MacroDef, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -427,7 +409,7 @@ pub struct MacroRulesLoc { pub flags: MacroRulesLocFlags, pub edition: Edition, } -impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macro_rules); +impl_intern!(MacroRulesId, MacroRulesLoc); impl_loc!(MacroRulesLoc, id: MacroRules, container: ModuleId); bitflags::bitflags! { @@ -455,7 +437,7 @@ pub struct ProcMacroLoc { pub kind: ProcMacroKind, pub edition: Edition, } -impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_macro); +impl_intern!(ProcMacroId, ProcMacroLoc); impl_loc!(ProcMacroLoc, id: Fn, container: ModuleId); #[derive(Debug, Hash, PartialEq, Eq, Clone)] @@ -464,7 +446,7 @@ pub struct BlockLoc { /// The containing module. pub module: ModuleId, } -impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block); +impl_intern!(BlockId, BlockLoc); #[salsa_macros::tracked(debug)] #[derive(PartialOrd, Ord)] @@ -720,42 +702,6 @@ impl From<DefWithBodyId> for ModuleDefId { } } -/// A constant, which might appears as a const item, an anonymous const block in expressions -/// or patterns, or as a constant in types with const generics. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)] -pub enum GeneralConstId { - ConstId(ConstId), - StaticId(StaticId), - AnonConstId(AnonConstId), -} - -impl_from!(ConstId, StaticId, AnonConstId for GeneralConstId); - -impl GeneralConstId { - pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> { - match self { - GeneralConstId::ConstId(it) => Some(it.into()), - GeneralConstId::StaticId(it) => Some(it.into()), - GeneralConstId::AnonConstId(it) => Some(it.lookup(db).owner.generic_def(db)), - } - } - - pub fn name(self, db: &dyn DefDatabase) -> String { - match self { - GeneralConstId::StaticId(it) => { - StaticSignature::of(db, it).name.display(db, Edition::CURRENT).to_string() - } - GeneralConstId::ConstId(const_id) => { - ConstSignature::of(db, const_id).name.as_ref().map_or_else( - || "_".to_owned(), - |name| name.display(db, Edition::CURRENT).to_string(), - ) - } - GeneralConstId::AnonConstId(_) => "{anon const}".to_owned(), - } - } -} - /// The defs which have a body. #[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)] pub enum DefWithBodyId { @@ -777,12 +723,12 @@ impl From<EnumVariantId> for DefWithBodyId { } impl DefWithBodyId { - pub fn as_generic_def_id(self, db: &dyn DefDatabase) -> Option<GenericDefId> { + pub fn generic_def(self, db: &dyn DefDatabase) -> GenericDefId { match self { - DefWithBodyId::FunctionId(f) => Some(f.into()), - DefWithBodyId::StaticId(s) => Some(s.into()), - DefWithBodyId::ConstId(c) => Some(c.into()), - DefWithBodyId::VariantId(c) => Some(c.lookup(db).parent.into()), + DefWithBodyId::FunctionId(f) => f.into(), + DefWithBodyId::StaticId(s) => s.into(), + DefWithBodyId::ConstId(c) => c.into(), + DefWithBodyId::VariantId(c) => c.lookup(db).parent.into(), } } } diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index eabdada67c..1d6812ad56 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -22,7 +22,7 @@ use hir_expand::{ builtin::quote::quote, db::ExpandDatabase, proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind}, - span_map::SpanMapRef, + span_map::SpanMap, }; use intern::{Symbol, sym}; use itertools::Itertools; @@ -64,7 +64,7 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) .filter_map(|macro_call| { let errors = db.parse_macro_expansion_error(macro_call)?; let errors = errors.err.as_ref()?.render_to_string(&db); - let macro_loc = db.lookup_intern_macro_call(macro_call); + let macro_loc = macro_call.loc(&db); let ast_id = match macro_loc.kind { MacroCallKind::FnLike { ast_id, .. } => ast_id.map(|it| it.erase()), MacroCallKind::Derive { ast_id, .. } => ast_id.map(|it| it.erase()), @@ -142,10 +142,10 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream } let mut expn_text = String::new(); - if let Some(err) = exp.err { + if let Some(err) = &exp.err { format_to!(expn_text, "/* error: {} */", err.render_to_string(&db).message); } - let (parse, token_map) = exp.value; + let (parse, token_map) = &exp.value; if expect_errors { assert!(!parse.errors().is_empty(), "no parse errors in expansion"); for e in parse.errors() { @@ -161,7 +161,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream } let pp = pretty_print_macro_expansion( parse.syntax_node(), - SpanMapRef::ExpansionSpanMap(&token_map), + SpanMap::ExpansionSpanMap(token_map), show_spans, show_ctxt, ); @@ -215,7 +215,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream } let pp = pretty_print_macro_expansion( src.value, - db.span_map(src.file_id).as_ref(), + db.span_map(src.file_id), show_spans, show_ctxt, ); @@ -230,7 +230,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream if let Some(macro_file) = src.file_id.macro_file() { let pp = pretty_print_macro_expansion( src.value.syntax().clone(), - db.span_map(macro_file.into()).as_ref(), + db.span_map(macro_file.into()), false, false, ); @@ -245,7 +245,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream { let pp = pretty_print_macro_expansion( src.value.syntax().clone(), - db.span_map(macro_file.into()).as_ref(), + db.span_map(macro_file.into()), false, false, ); @@ -309,7 +309,7 @@ fn reindent(indent: IndentLevel, pp: String) -> String { fn pretty_print_macro_expansion( expn: SyntaxNode, - map: SpanMapRef<'_>, + map: SpanMap<'_>, show_spans: bool, show_ctxt: bool, ) -> String { diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 56b3f03f7b..88e3408a33 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -68,7 +68,7 @@ use hir_expand::{ }; use intern::Symbol; use itertools::Itertools; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashMap; use span::{Edition, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; use syntax::{AstNode, SmolStr, SyntaxNode, ToSmolStr, ast}; @@ -83,6 +83,7 @@ use crate::{ item_tree::TreeId, nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, per_ns::PerNs, + unstable_features::UnstableFeatures, visibility::{Visibility, VisibilityExplicitness}, }; @@ -216,7 +217,7 @@ struct DefMapCrateData { /// Custom tool modules registered with `#![register_tool]`. registered_tools: Vec<Symbol>, /// Unstable features of Rust enabled with `#![feature(A, B)]`. - unstable_features: FxHashSet<Symbol>, + unstable_features: UnstableFeatures, /// `#[rustc_coherence_is_core]` rustc_coherence_is_core: bool, no_core: bool, @@ -233,7 +234,7 @@ impl DefMapCrateData { fn_proc_macro_mapping: FxHashMap::default(), fn_proc_macro_mapping_back: FxHashMap::default(), registered_tools: PREDEFINED_TOOLS.iter().map(|it| Symbol::intern(it)).collect(), - unstable_features: FxHashSet::default(), + unstable_features: UnstableFeatures::default(), rustc_coherence_is_core: false, no_core: false, no_std: false, @@ -554,8 +555,9 @@ impl DefMap { &self.data.registered_tools } - pub fn is_unstable_feature_enabled(&self, feature: &Symbol) -> bool { - self.data.unstable_features.contains(feature) + #[inline] + pub fn features(&self) -> &UnstableFeatures { + &self.data.unstable_features } pub fn is_rustc_coherence_is_core(&self) -> bool { diff --git a/crates/hir-def/src/nameres/assoc.rs b/crates/hir-def/src/nameres/assoc.rs index f5a852b39c..b1d554738f 100644 --- a/crates/hir-def/src/nameres/assoc.rs +++ b/crates/hir-def/src/nameres/assoc.rs @@ -138,7 +138,7 @@ struct AssocItemCollector<'db> { def_map: &'db DefMap, local_def_map: &'db LocalDefMap, ast_id_map: &'db AstIdMap, - span_map: SpanMap, + span_map: SpanMap<'db>, cfg_options: &'db CfgOptions, file_id: HirFileId, diagnostics: Vec<DefDiagnostic>, @@ -191,19 +191,18 @@ impl<'db> AssocItemCollector<'db> { fn collect_item(&mut self, item: ast::AssocItem) { let ast_id = self.ast_id_map.ast_id(&item); - let attrs = - match AttrsOrCfg::lower(self.db, &item, &|| self.cfg_options, self.span_map.as_ref()) { - AttrsOrCfg::Enabled { attrs } => attrs, - AttrsOrCfg::CfgDisabled(cfg) => { - self.diagnostics.push(DefDiagnostic::unconfigured_code( - self.module_id, - InFile::new(self.file_id, ast_id.erase()), - cfg.0, - self.cfg_options.clone(), - )); - return; - } - }; + let attrs = match AttrsOrCfg::lower(self.db, &item, &|| self.cfg_options, self.span_map) { + AttrsOrCfg::Enabled { attrs } => attrs, + AttrsOrCfg::CfgDisabled(cfg) => { + self.diagnostics.push(DefDiagnostic::unconfigured_code( + self.module_id, + InFile::new(self.file_id, ast_id.erase()), + cfg.0, + self.cfg_options.clone(), + )); + return; + } + }; let ast_id = InFile::new(self.file_id, ast_id.upcast()); 'attrs: for (attr_id, attr) in attrs.as_ref().iter() { @@ -218,7 +217,7 @@ impl<'db> AssocItemCollector<'db> { attr_id, ) { Ok(ResolvedAttr::Macro(call_id)) => { - let loc = self.db.lookup_intern_macro_call(call_id); + let loc = call_id.loc(self.db); if let MacroDefKind::ProcMacro(_, exp, _) = loc.def.kind { // If there's no expander for the proc macro (e.g. the // proc macro is ignored, or building the proc macro @@ -357,7 +356,7 @@ impl<'db> AssocItemCollector<'db> { return; } - let (syntax, span_map) = self.db.parse_macro_expansion(macro_call_id).value; + let (syntax, span_map) = &self.db.parse_macro_expansion(macro_call_id).value; let old_file_id = mem::replace(&mut self.file_id, macro_call_id.into()); let old_ast_id_map = mem::replace(&mut self.ast_id_map, self.db.ast_id_map(self.file_id)); let old_span_map = mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(span_map)); diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs index 062b55fcef..5aabd7dbc6 100644 --- a/crates/hir-def/src/nameres/attr_resolution.rs +++ b/crates/hir-def/src/nameres/attr_resolution.rs @@ -9,7 +9,6 @@ use hir_expand::{ }; use span::SyntaxContext; use syntax::ast; -use triomphe::Arc; use crate::{ AstIdWithPath, MacroId, ModuleId, UnresolvedMacro, @@ -126,7 +125,7 @@ pub(super) fn attr_macro_as_call_id( krate, MacroCallKind::Attr { ast_id: item_attr.ast_id, - attr_args: arg.map(Arc::new), + attr_args: arg.map(Box::new), censored_attr_ids, }, macro_attr.ctxt, diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 703f070dba..5ef51dbb2c 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -316,7 +316,7 @@ impl<'db> DefCollector<'db> { _ => None, }, ); - crate_data.unstable_features.extend(features); + features.for_each(|feature| crate_data.unstable_features.enable(feature)); } () if *attr_name == sym::register_tool => { if let Some(ident) = attr.single_ident_value() { @@ -2840,6 +2840,6 @@ foo!(KABOOM); assert_eq!(def_map.recursion_limit(), 4); assert!(def_map.is_no_core()); assert!(def_map.is_no_std()); - assert!(def_map.is_unstable_feature_enabled(&sym::register_tool)); + assert!(def_map.features().is_enabled(&sym::register_tool)); } } diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs index 0f1828abce..08f672aa0c 100644 --- a/crates/hir-def/src/nameres/tests/incremental.rs +++ b/crates/hir-def/src/nameres/tests/incremental.rs @@ -168,15 +168,15 @@ fn no() {} "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "EnumVariants::of_", ] "#]], @@ -185,7 +185,7 @@ fn no() {} "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "EnumVariants::of_", ] "#]], @@ -226,21 +226,21 @@ pub struct S {} "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", - "decl_macro_expander_shim", + "real_span_map", + "DeclarativeMacroExpander::expander_", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", - "macro_arg_shim", + "parse_macro_expansion", + "macro_arg", ] "#]], expect![[r#" @@ -248,9 +248,9 @@ pub struct S {} "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", - "macro_arg_shim", - "parse_macro_expansion_shim", + "real_span_map", + "macro_arg", + "parse_macro_expansion", "ast_id_map", "file_item_tree_query", ] @@ -284,27 +284,27 @@ fn f() { foo } "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "proc_macros_for_crate_shim", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", - "expand_proc_macro_shim", - "macro_arg_shim", + "parse_macro_expansion", + "expand_proc_macro", + "macro_arg", "proc_macro_span_shim", ] "#]], @@ -313,10 +313,10 @@ fn f() { foo } "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", - "macro_arg_shim", - "expand_proc_macro_shim", - "parse_macro_expansion_shim", + "real_span_map", + "macro_arg", + "expand_proc_macro", + "parse_macro_expansion", "ast_id_map", "file_item_tree_query", ] @@ -408,39 +408,39 @@ pub struct S {} "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "proc_macros_for_crate_shim", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", - "decl_macro_expander_shim", + "real_span_map", + "DeclarativeMacroExpander::expander_", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", - "macro_arg_shim", - "decl_macro_expander_shim", + "parse_macro_expansion", + "macro_arg", + "DeclarativeMacroExpander::expander_", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", - "macro_arg_shim", + "parse_macro_expansion", + "macro_arg", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", - "expand_proc_macro_shim", - "macro_arg_shim", + "parse_macro_expansion", + "expand_proc_macro", + "macro_arg", "proc_macro_span_shim", ] "#]], @@ -449,11 +449,11 @@ pub struct S {} "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", - "macro_arg_shim", - "decl_macro_expander_shim", - "macro_arg_shim", - "macro_arg_shim", + "real_span_map", + "macro_arg", + "DeclarativeMacroExpander::expander_", + "macro_arg", + "macro_arg", ] "#]], ); @@ -518,36 +518,36 @@ m!(Z); [crate_def_map.modules_for_file(&db, pos.file_id.file_id(&db)).next().unwrap()]; assert_eq!(module_data.scope.resolutions().count(), 4); }, - &[("file_item_tree_query", 6), ("parse_macro_expansion_shim", 3)], + &[("file_item_tree_query", 6), ("parse_macro_expansion", 3)], expect![[r#" [ "crate_local_def_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", - "decl_macro_expander_shim", + "real_span_map", + "DeclarativeMacroExpander::expander_", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "macro_def_shim", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", - "macro_arg_shim", + "parse_macro_expansion", + "macro_arg", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", - "macro_arg_shim", + "parse_macro_expansion", + "macro_arg", "file_item_tree_query", "ast_id_map", - "parse_macro_expansion_shim", - "macro_arg_shim", + "parse_macro_expansion", + "macro_arg", ] "#]], ); @@ -568,16 +568,16 @@ m!(Z); [crate_def_map.modules_for_file(&db, pos.file_id.file_id(&db)).next().unwrap()]; assert_eq!(module_data.scope.resolutions().count(), 4); }, - &[("file_item_tree_query", 1), ("parse_macro_expansion_shim", 0)], + &[("file_item_tree_query", 1), ("parse_macro_expansion", 0)], expect![[r#" [ "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", - "macro_arg_shim", - "macro_arg_shim", - "macro_arg_shim", + "real_span_map", + "macro_arg", + "macro_arg", + "macro_arg", ] "#]], ); @@ -612,7 +612,7 @@ pub type Ty = (); "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", ] "#]], ); @@ -632,7 +632,7 @@ pub type Ty = (); "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", ] "#]], ); diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index bb292ac1a6..8320221ffc 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -936,7 +936,7 @@ fn handle_macro_def_scope( // and use its parent expansion. *hygiene_id = HygieneId::new(parent_ctx.opaque_and_semiopaque(db)); *hygiene_info = parent_ctx.outer_expn(db).map(|expansion| { - let expansion = db.lookup_intern_macro_call(expansion.into()); + let expansion = hir_expand::MacroCallId::from(expansion).loc(db); (parent_ctx.parent(db), expansion.def) }); } @@ -950,7 +950,7 @@ fn hygiene_info( if !hygiene_id.is_root() { let ctx = hygiene_id.syntax_context(); ctx.outer_expn(db).map(|expansion| { - let expansion = db.lookup_intern_macro_call(expansion.into()); + let expansion = hir_expand::MacroCallId::from(expansion).loc(db); (ctx.parent(db), expansion.def) }) } else { diff --git a/crates/hir-def/src/signatures.rs b/crates/hir-def/src/signatures.rs index 6d704274f4..e58befae20 100644 --- a/crates/hir-def/src/signatures.rs +++ b/crates/hir-def/src/signatures.rs @@ -8,9 +8,9 @@ use hir_expand::{ InFile, Intern, Lookup, name::{AsName, Name}, }; -use intern::{Symbol, sym}; +use intern::sym; use la_arena::{Arena, Idx}; -use rustc_abi::{IntegerType, ReprOptions}; +use rustc_abi::{ExternAbi, IntegerType, ReprOptions}; use syntax::{ AstNode, NodeOrToken, SyntaxNodePtr, T, ast::{self, HasGenericParams, HasName, HasVisibility, IsString}, @@ -128,13 +128,11 @@ impl StructSignature { source_map, ) } -} -impl StructSignature { #[inline] pub fn repr(&self, db: &dyn DefDatabase, id: StructId) -> Option<ReprOptions> { if self.flags.contains(StructFlags::HAS_REPR) { - AttrFlags::repr(db, id.into()) + AttrFlags::repr_assume_has(db, id.into()) } else { None } @@ -202,6 +200,15 @@ impl UnionSignature { source_map, ) } + + #[inline] + pub fn repr(&self, db: &dyn DefDatabase, id: UnionId) -> Option<ReprOptions> { + if self.flags.contains(StructFlags::HAS_REPR) { + AttrFlags::repr_assume_has(db, id.into()) + } else { + None + } + } } bitflags! { @@ -211,6 +218,8 @@ bitflags! { const HAS_REPR = 1 << 0; /// Indicates whether the enum has a `#[rustc_has_incoherent_inherent_impls]` attribute. const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1; + /// Whether this enum has `#[fundamental]`. + const FUNDAMENTAL = 1 << 2; } } @@ -243,6 +252,9 @@ impl EnumSignature { if attrs.contains(AttrFlags::HAS_REPR) { flags |= EnumFlags::HAS_REPR; } + if attrs.contains(AttrFlags::FUNDAMENTAL) { + flags |= EnumFlags::FUNDAMENTAL; + } let InFile { file_id, value: source } = loc.source(db); let (store, generic_params, source_map) = lower_generic_params( @@ -276,7 +288,11 @@ impl EnumSignature { #[inline] pub fn repr(&self, db: &dyn DefDatabase, id: EnumId) -> Option<ReprOptions> { - if self.flags.contains(EnumFlags::HAS_REPR) { AttrFlags::repr(db, id.into()) } else { None } + if self.flags.contains(EnumFlags::HAS_REPR) { + AttrFlags::repr_assume_has(db, id.into()) + } else { + None + } } } bitflags::bitflags! { @@ -567,19 +583,20 @@ bitflags! { const DEFAULT = 1 << 2; const CONST = 1 << 3; const ASYNC = 1 << 4; - const UNSAFE = 1 << 5; - const HAS_VARARGS = 1 << 6; - const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7; - const HAS_SELF_PARAM = 1 << 8; + const GEN = 1 << 5; + const UNSAFE = 1 << 6; + const HAS_VARARGS = 1 << 7; + const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 8; + const HAS_SELF_PARAM = 1 << 9; /// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396), /// but keeping it for all functions will consume a lot of memory when there are /// only very few functions with it. So we only encode its existence here, and lookup /// it if needed. - const HAS_TARGET_FEATURE = 1 << 9; - const DEPRECATED_SAFE_2024 = 1 << 10; - const EXPLICIT_SAFE = 1 << 11; - const HAS_LEGACY_CONST_GENERICS = 1 << 12; - const RUSTC_INTRINSIC = 1 << 13; + const HAS_TARGET_FEATURE = 1 << 10; + const DEPRECATED_SAFE_2024 = 1 << 11; + const EXPLICIT_SAFE = 1 << 12; + const HAS_LEGACY_CONST_GENERICS = 1 << 13; + const RUSTC_INTRINSIC = 1 << 14; } } @@ -590,7 +607,7 @@ pub struct FunctionSignature { pub store: ExpressionStore, pub params: Box<[TypeRefId]>, pub ret_type: Option<TypeRefId>, - pub abi: Option<Symbol>, + pub abi: ExternAbi, pub flags: FnFlags, } @@ -638,6 +655,9 @@ impl FunctionSignature { if source.value.async_token().is_some() { flags.insert(FnFlags::ASYNC); } + if source.value.gen_token().is_some() { + flags.insert(FnFlags::GEN); + } if source.value.const_token().is_some() { flags.insert(FnFlags::CONST); } @@ -652,9 +672,23 @@ impl FunctionSignature { } let name = as_name_opt(source.value.name()); - let abi = source.value.abi().map(|abi| { - abi.abi_string().map_or_else(|| sym::C, |it| Symbol::intern(it.text_without_quotes())) - }); + let abi = source + .value + .abi() + .map(|abi| { + abi.abi_string() + .and_then(|abi| abi.text_without_quotes().parse().ok()) + .unwrap_or(ExternAbi::FALLBACK) + }) + .or_else(|| match loc.container { + ItemContainerId::ExternBlockId(extern_block) => { + Some(extern_block_abi(db, extern_block)) + } + ItemContainerId::ModuleId(_) + | ItemContainerId::ImplId(_) + | ItemContainerId::TraitId(_) => None, + }) + .unwrap_or(ExternAbi::Rust); let (store, source_map, generic_params, params, ret_type, self_param, variadic) = lower_function(db, module, source, id); if self_param { @@ -701,6 +735,10 @@ impl FunctionSignature { self.flags.contains(FnFlags::ASYNC) } + pub fn is_gen(&self) -> bool { + self.flags.contains(FnFlags::GEN) + } + pub fn is_unsafe(&self) -> bool { self.flags.contains(FnFlags::UNSAFE) } @@ -737,16 +775,6 @@ impl FunctionSignature { pub fn is_intrinsic(db: &dyn DefDatabase, id: FunctionId) -> bool { let data = FunctionSignature::of(db, id); data.flags.contains(FnFlags::RUSTC_INTRINSIC) - // Keep this around for a bit until extern "rustc-intrinsic" abis are no longer used - || match &data.abi { - Some(abi) => *abi == sym::rust_dash_intrinsic, - None => match id.lookup(db).container { - ItemContainerId::ExternBlockId(block) => { - block.abi(db) == Some(sym::rust_dash_intrinsic) - } - _ => false, - }, - } } } @@ -958,7 +986,7 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>( override_visibility: Option<Option<ast::Visibility>>, ) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> { let cfg_options = module.krate(db).cfg_options(db); - let mut col = ExprCollector::signature(db, module, fields.file_id); + let mut col = ExprCollector::new(db, module, fields.file_id); let override_visibility = override_visibility.map(|vis| { LazyCell::new(|| { let span_map = db.span_map(fields.file_id); @@ -1110,16 +1138,12 @@ impl EnumVariants { } } -pub(crate) fn extern_block_abi( - db: &dyn DefDatabase, - extern_block: ExternBlockId, -) -> Option<Symbol> { +#[salsa::tracked] +pub(crate) fn extern_block_abi(db: &dyn DefDatabase, extern_block: ExternBlockId) -> ExternAbi { let source = extern_block.lookup(db).source(db); - source.value.abi().map(|abi| { - match abi.abi_string() { - Some(tok) => Symbol::intern(tok.text_without_quotes()), - // `extern` default to be `extern "C"`. - _ => sym::C, - } - }) + source + .value + .abi() + .and_then(|abi| abi.abi_string()?.text_without_quotes().parse().ok()) + .unwrap_or(ExternAbi::FALLBACK) } diff --git a/crates/hir-def/src/unstable_features.rs b/crates/hir-def/src/unstable_features.rs new file mode 100644 index 0000000000..f581f02617 --- /dev/null +++ b/crates/hir-def/src/unstable_features.rs @@ -0,0 +1,96 @@ +//! Handling of unstable features. +//! +//! We define two kinds of handling: we have a map of all unstable features for a crate +//! as `Symbol`s. This is mostly for external consumers. +//! +//! For analysis, we store them as a struct of bools, for fast access. + +use std::fmt; + +use base_db::Crate; +use intern::{Symbol, sym}; +use rustc_hash::FxHashSet; + +use crate::db::DefDatabase; + +impl fmt::Debug for UnstableFeatures { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(&self.all).finish() + } +} + +impl PartialEq for UnstableFeatures { + fn eq(&self, other: &Self) -> bool { + self.all == other.all + } +} + +impl Eq for UnstableFeatures {} + +impl UnstableFeatures { + #[inline] + pub fn is_enabled(&self, feature: &Symbol) -> bool { + self.all.contains(feature) + } + + #[inline] + pub fn iter(&self) -> impl Iterator<Item = Symbol> { + self.all.iter().cloned() + } + + pub(crate) fn shrink_to_fit(&mut self) { + self.all.shrink_to_fit(); + } +} + +#[salsa::tracked] +impl UnstableFeatures { + /// Query unstable features for a crate. + /// + /// This is also available as `DefMap::features()`. Use that if you have a DefMap available. + /// Otherwise, use this, to not draw a dependency to the def map. + #[salsa::tracked(returns(ref))] + pub fn query(db: &dyn DefDatabase, krate: Crate) -> UnstableFeatures { + crate::crate_def_map(db, krate).features().clone() + } +} + +macro_rules! define_unstable_features { + ( $( $feature:ident, )* ) => { + #[derive(Clone, Default)] + pub struct UnstableFeatures { + all: FxHashSet<Symbol>, + + $( pub $feature: bool, )* + } + + impl UnstableFeatures { + pub(crate) fn enable(&mut self, feature: Symbol) { + match () { + $( () if feature == sym::$feature => self.$feature = true, )* + _ => {} + } + + self.all.insert(feature); + } + } + }; +} + +define_unstable_features! { + lang_items, + exhaustive_patterns, + generic_associated_type_extended, + arbitrary_self_types, + arbitrary_self_types_pointers, + supertrait_item_shadowing, + new_range, + never_type_fallback, + specialization, + min_specialization, + ref_pat_eat_one_layer_2024, + ref_pat_eat_one_layer_2024_structural, + deref_patterns, + mut_ref, + type_changing_struct_update, +} diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index d1f962f7ff..395b997972 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -32,7 +32,7 @@ use crate::{ AstId, db::ExpandDatabase, mod_path::ModPath, - span_map::SpanMapRef, + span_map::SpanMap, tt::{self, TopSubtree}, }; @@ -422,7 +422,7 @@ impl AttrId { )); let tt = syntax_bridge::syntax_node_to_token_tree( tt.syntax(), - SpanMapRef::RealSpanMap(&span_map), + SpanMap::RealSpanMap(&span_map), span_map.span_for_range(tt.syntax().text_range()), DocCommentDesugarMode::ProcMacro, ); diff --git a/crates/hir-expand/src/builtin/attr_macro.rs b/crates/hir-expand/src/builtin/attr_macro.rs index c94663ca0c..9b13f9fb00 100644 --- a/crates/hir-expand/src/builtin/attr_macro.rs +++ b/crates/hir-expand/src/builtin/attr_macro.rs @@ -122,7 +122,7 @@ fn derive_expand( tt: &tt::TopSubtree, span: Span, ) -> ExpandResult<tt::TopSubtree> { - let loc = db.lookup_intern_macro_call(id); + let loc = id.loc(db); let derives = match &loc.kind { MacroCallKind::Attr { attr_args: Some(attr_args), .. } if loc.def.is_attribute_derive() => { attr_args diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs index 8f513a2bcf..8b031e3647 100644 --- a/crates/hir-expand/src/builtin/derive_macro.rs +++ b/crates/hir-expand/src/builtin/derive_macro.rs @@ -25,7 +25,6 @@ use syntax::{ HasName, HasTypeBounds, make, }, syntax_editor::{GetOrCreateWhereClause, SyntaxEditor}, - ted, }; macro_rules! register_builtin { @@ -1007,7 +1006,8 @@ fn coerce_pointee_expand( return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err); } }; - let adt = adt.clone_for_update(); + let (editor, adt) = SyntaxEditor::with_ast_node(&adt); + let make = editor.make(); let ast::Adt::Struct(strukt) = &adt else { return ExpandResult::new( tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), @@ -1078,7 +1078,7 @@ fn coerce_pointee_expand( if is_pointee { // Remove the `#[pointee]` attribute so it won't be present in the generated // impls (where we cannot resolve it). - ted::remove(attr.syntax()); + editor.delete(attr.syntax()); } is_pointee }) @@ -1181,25 +1181,28 @@ fn coerce_pointee_expand( // If the target type is the pointee, duplicate the bound as whole. // Otherwise, duplicate only bounds that mention the pointee. let is_pointee = param_name.text() == pointee_param_name.text(); - let new_bounds = bounds - .bounds() - .map(|bound| bound.clone_subtree().clone_for_update()) - .filter(|bound| { - bound.ty().is_some_and(|ty| { - substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM) - || is_pointee - }) - }); + let new_bounds = bounds.bounds().filter_map(|bound| { + let new_bound = substitute_type_bound( + bound.clone(), + &pointee_param_name.text(), + ADDED_PARAM, + ); + + if is_pointee { + return new_bound.or(Some(bound)); + } + new_bound + }); + let new_bounds_target = if is_pointee { - make::name_ref(ADDED_PARAM) + make.name_ref(ADDED_PARAM) } else { - make::name_ref(¶m_name.text()) + make.name_ref(¶m_name.text()) }; - new_predicates.push(make::where_pred( - Either::Right(make::ty_path(make::path_from_segments( - [make::path_segment(new_bounds_target)], - false, - ))), + new_predicates.push(make.where_pred( + Either::Right( + make.ty_path_from_segments([make.path_segment(new_bounds_target)], false), + ), new_bounds, )); } @@ -1232,37 +1235,19 @@ fn coerce_pointee_expand( // We should also write a few new `where` bounds from `#[pointee] T` to `__S` // as well as any bound that indirectly involves the `#[pointee] T` type. for predicate in strukt.where_clause().into_iter().flat_map(|wc| wc.predicates()) { - let predicate = predicate.clone_subtree().clone_for_update(); let Some(pred_target) = predicate.ty() else { continue }; // If the target type references the pointee, duplicate the bound as whole. // Otherwise, duplicate only bounds that mention the pointee. - if substitute_type_in_bound( - pred_target.clone(), - &pointee_param_name.text(), - ADDED_PARAM, - ) { - if let Some(bounds) = predicate.type_bound_list() { - for bound in bounds.bounds() { - if let Some(ty) = bound.ty() { - substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM); - } - } - } - - new_predicates.push(predicate); + if let Some(predicate_with_substituted_target) = + substitute_where_pred(&predicate, &pointee_param_name.text(), ADDED_PARAM) + { + new_predicates.push(predicate_with_substituted_target); } else if let Some(bounds) = predicate.type_bound_list() { - let new_bounds = bounds - .bounds() - .map(|bound| bound.clone_subtree().clone_for_update()) - .filter(|bound| { - bound.ty().is_some_and(|ty| { - substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM) - }) - }); - new_predicates.push( - make::where_pred(Either::Right(pred_target), new_bounds).clone_for_update(), - ); + let new_bounds = bounds.bounds().filter_map(|bound| { + substitute_type_bound(bound, &pointee_param_name.text(), ADDED_PARAM) + }); + new_predicates.push(make.where_pred(Either::Right(pred_target), new_bounds)); } } } @@ -1271,35 +1256,34 @@ fn coerce_pointee_expand( // # Add `Unsize<__S>` bound to `#[pointee]` at the generic parameter location // // Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it. - new_predicates.push(make::where_pred( - Either::Right(make::ty_path(make::path_from_segments( - [make::path_segment(make::name_ref(&pointee_param_name.text()))], - false, - ))), - [make::type_bound(make::ty_path(make::path_from_segments( - [ - make::path_segment(make::name_ref("core")), - make::path_segment(make::name_ref("marker")), - make::generic_ty_path_segment( - make::name_ref("Unsize"), - [make::type_arg(make::ty_path(make::path_from_segments( - [make::path_segment(make::name_ref(ADDED_PARAM))], - false, - ))) - .into()], + new_predicates.push( + make.where_pred( + Either::Right(make.ty_path_from_segments( + [make.path_segment(make.name_ref(&pointee_param_name.text()))], + false, + )), + [make.type_bound( + make.ty_path_from_segments( + [ + make.path_segment(make.name_ref("core")), + make.path_segment(make.name_ref("marker")), + make.generic_ty_path_segment( + make.name_ref("Unsize"), + [make + .type_arg(make.ty_path_from_segments( + [make.path_segment(make.name_ref(ADDED_PARAM))], + false, + )) + .into()], + ), + ], + true, ), - ], - true, - )))], - )); + )], + ), + ); } - let (editor, strukt) = SyntaxEditor::with_ast_node(strukt); - strukt.get_or_create_where_clause(&editor, new_predicates.into_iter()); - let edit = editor.finish(); - let strukt = ast::Struct::cast(edit.new_root().clone()).unwrap(); - let adt = ast::Adt::Struct(strukt.clone()); - let self_for_traits = { // Replace the `#[pointee]` with `__S`. let mut type_param_idx = 0; @@ -1310,37 +1294,38 @@ fn coerce_pointee_expand( .filter_map(|param| { Some(match param { ast::GenericParam::ConstParam(param) => { - ast::GenericArg::ConstArg(make::expr_const_value(¶m.name()?.text())) + ast::GenericArg::ConstArg(make.expr_const_value(¶m.name()?.text())) } ast::GenericParam::LifetimeParam(param) => { - make::lifetime_arg(param.lifetime()?).into() + make.lifetime_arg(param.lifetime()?).into() } ast::GenericParam::TypeParam(param) => { let name = if pointee_param_idx == type_param_idx { - make::name_ref(ADDED_PARAM) + make.name_ref(ADDED_PARAM) } else { - make::name_ref(¶m.name()?.text()) + make.name_ref(¶m.name()?.text()) }; type_param_idx += 1; - make::type_arg(make::ty_path(make::path_from_segments( - [make::path_segment(name)], - false, - ))) - .into() + make.type_arg(make.ty_path_from_segments([make.path_segment(name)], false)) + .into() } }) }); - make::path_from_segments( - [make::generic_ty_path_segment( - make::name_ref(&struct_name.text()), + make.path_from_segments( + [make.generic_ty_path_segment( + make.name_ref(&struct_name.text()), self_params_for_traits, )], false, ) - .clone_for_update() }; + strukt.get_or_create_where_clause(&editor, new_predicates.into_iter()); + let edit = editor.finish(); + let strukt = ast::Struct::cast(edit.new_root().clone()).unwrap(); + let adt = ast::Adt::Struct(strukt.clone()); + let mut span_map = span::SpanMap::empty(); // One span for them all. span_map.push(adt.syntax().text_range().end(), span); @@ -1403,37 +1388,84 @@ fn coerce_pointee_expand( } /// Returns true if any substitution was performed. - fn substitute_type_in_bound(ty: ast::Type, param_name: &str, replacement: &str) -> bool { + fn substitute_type_bound( + bound: ast::TypeBound, + param_name: &str, + replacement: &str, + ) -> Option<ast::TypeBound> { + let (editor, bound) = SyntaxEditor::with_ast_node(&bound); + let substituted = bound + .ty() + .is_some_and(|ty| substitute_type_in_bound(&editor, ty, param_name, replacement)); + if !substituted { + return None; + } + + let edit = editor.finish(); + Some(ast::TypeBound::cast(edit.new_root().clone()).unwrap()) + } + + fn substitute_where_pred( + predicate: &ast::WherePred, + param_name: &str, + replacement: &str, + ) -> Option<ast::WherePred> { + let (editor, predicate) = SyntaxEditor::with_ast_node(predicate); + let substituted = predicate + .ty() + .is_some_and(|ty| substitute_type_in_bound(&editor, ty, param_name, replacement)); + if substituted && let Some(bounds) = predicate.type_bound_list() { + for bound in bounds.bounds() { + if let Some(ty) = bound.ty() { + substitute_type_in_bound(&editor, ty, param_name, replacement); + } + } + } + if !substituted { + return None; + } + + let edit = editor.finish(); + Some(ast::WherePred::cast(edit.new_root().clone()).unwrap()) + } + + fn substitute_type_in_bound( + editor: &SyntaxEditor, + ty: ast::Type, + param_name: &str, + replacement: &str, + ) -> bool { return match ty { - ast::Type::ArrayType(ty) => { - ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + ast::Type::ArrayType(ty) => ty + .ty() + .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)), + ast::Type::DynTraitType(ty) => { + go_bounds(editor, ty.type_bound_list(), param_name, replacement) } - ast::Type::DynTraitType(ty) => go_bounds(ty.type_bound_list(), param_name, replacement), ast::Type::FnPtrType(ty) => any_long( ty.param_list() .into_iter() .flat_map(|params| params.params().filter_map(|param| param.ty())) .chain(ty.ret_type().and_then(|it| it.ty())), - |ty| substitute_type_in_bound(ty, param_name, replacement), + |ty| substitute_type_in_bound(editor, ty, param_name, replacement), ), - ast::Type::ForType(ty) => { - ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) - } + ast::Type::ForType(ty) => ty + .ty() + .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)), ast::Type::ImplTraitType(ty) => { - go_bounds(ty.type_bound_list(), param_name, replacement) - } - ast::Type::ParenType(ty) => { - ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + go_bounds(editor, ty.type_bound_list(), param_name, replacement) } + ast::Type::ParenType(ty) => ty + .ty() + .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)), ast::Type::PathType(ty) => ty.path().is_some_and(|path| { if path.as_single_name_ref().is_some_and(|name| name.text() == param_name) { - ted::replace( + editor.replace( path.syntax(), make::path_from_segments( [make::path_segment(make::name_ref(replacement))], false, ) - .clone_for_update() .syntax(), ); return true; @@ -1447,34 +1479,35 @@ fn coerce_pointee_expand( ast::GenericArg::TypeArg(ty) => ty.ty(), _ => None, }), - |ty| substitute_type_in_bound(ty, param_name, replacement), + |ty| substitute_type_in_bound(editor, ty, param_name, replacement), ) }), - ast::Type::PtrType(ty) => { - ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) - } - ast::Type::RefType(ty) => { - ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) - } - ast::Type::SliceType(ty) => { - ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) - } - ast::Type::TupleType(ty) => { - any_long(ty.fields(), |ty| substitute_type_in_bound(ty, param_name, replacement)) - } + ast::Type::PtrType(ty) => ty + .ty() + .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)), + ast::Type::RefType(ty) => ty + .ty() + .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)), + ast::Type::SliceType(ty) => ty + .ty() + .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)), + ast::Type::TupleType(ty) => any_long(ty.fields(), |ty| { + substitute_type_in_bound(editor, ty, param_name, replacement) + }), ast::Type::InferType(_) | ast::Type::MacroType(_) | ast::Type::NeverType(_) => false, }; fn go_bounds( + editor: &SyntaxEditor, bounds: Option<ast::TypeBoundList>, param_name: &str, replacement: &str, ) -> bool { bounds.is_some_and(|bounds| { any_long(bounds.bounds(), |bound| { - bound - .ty() - .is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement)) + bound.ty().is_some_and(|ty| { + substitute_type_in_bound(editor, ty, param_name, replacement) + }) }) }) } diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs index 9962677a9d..eb7175c686 100644 --- a/crates/hir-expand/src/builtin/fn_macro.rs +++ b/crates/hir-expand/src/builtin/fn_macro.rs @@ -357,7 +357,7 @@ fn cfg_select_expand( tt: &tt::TopSubtree, span: Span, ) -> ExpandResult<tt::TopSubtree> { - let loc = db.lookup_intern_macro_call(id); + let loc = id.loc(db); let cfg_options = loc.krate.cfg_options(db); let mut iter = tt.iter(); @@ -446,7 +446,7 @@ fn cfg_expand( tt: &tt::TopSubtree, span: Span, ) -> ExpandResult<tt::TopSubtree> { - let loc = db.lookup_intern_macro_call(id); + let loc = id.loc(db); let expr = CfgExpr::parse(tt); let enabled = loc.krate.cfg_options(db).check(&expr) != Some(false); let expanded = if enabled { quote!(span=>true) } else { quote!(span=>false) }; @@ -518,7 +518,7 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool { let Some(expn) = span.ctx.outer_expn(db) else { break false; }; - let expn = db.lookup_intern_macro_call(expn.into()); + let expn = crate::MacroCallId::from(expn).loc(db); // FIXME: Record allow_internal_unstable in the macro def (not been done yet because it // would consume quite a bit extra memory for all call locs...) // if let Some(features) = expn.def.allow_internal_unstable { @@ -764,7 +764,7 @@ fn relative_file( allow_recursion: bool, err_span: Span, ) -> Result<EditionedFileId, ExpandError> { - let lookup = db.lookup_intern_macro_call(call_id); + let lookup = call_id.loc(db); let call_site = lookup.kind.file_id().original_file_respecting_includes(db).file_id(db); let path = AnchoredPath { anchor: call_site, path: path_str }; let res: FileId = db @@ -900,7 +900,7 @@ fn include_str_expand( } fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option<String> { - let krate = db.lookup_intern_macro_call(arg_id).krate; + let krate = arg_id.loc(db).krate; krate.env(db).get(key.as_str()) } diff --git a/crates/hir-expand/src/builtin/quote.rs b/crates/hir-expand/src/builtin/quote.rs index 51c4e22516..d84756377f 100644 --- a/crates/hir-expand/src/builtin/quote.rs +++ b/crates/hir-expand/src/builtin/quote.rs @@ -199,9 +199,9 @@ impl<T: ToTokenTree + Clone> ToTokenTree for &T { } impl_to_to_tokentrees! { - span: u32 => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; - span: usize => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; - span: i32 => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; + span: u32 => self { crate::tt::Literal{text_and_suffix: sym::Integer::get(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; + span: usize => self { crate::tt::Literal{text_and_suffix: sym::Integer::get(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; + span: i32 => self { crate::tt::Literal{text_and_suffix: sym::Integer::get(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } }; span: bool => self { crate::tt::Ident{sym: if self { sym::true_ } else { sym::false_ }, span, is_raw: tt::IdentIsRaw::No } }; _span: crate::tt::Leaf => self { self }; _span: crate::tt::Literal => self { self }; diff --git a/crates/hir-expand/src/cfg_process.rs b/crates/hir-expand/src/cfg_process.rs index 6258fac0e9..81edc9f2cf 100644 --- a/crates/hir-expand/src/cfg_process.rs +++ b/crates/hir-expand/src/cfg_process.rs @@ -16,7 +16,7 @@ use crate::{ attrs::{AstPathExt, AttrId, expand_cfg_attr, is_item_tree_filtered_attr}, db::ExpandDatabase, fixup::{self, SyntaxFixupUndoInfo}, - span_map::SpanMapRef, + span_map::SpanMap, tt::{self, DelimSpan, Span}, }; @@ -51,7 +51,7 @@ fn macro_input_callback( censor_item_tree_attr_ids: &[AttrId], krate: Crate, default_span: Span, - span_map: SpanMapRef<'_>, + span_map: SpanMap<'_>, ) -> impl FnMut(&mut PreorderWithTokens, &WalkEvent<SyntaxElement>) -> (bool, Vec<tt::Leaf>) { let cfg_options = OnceCell::new(); let cfg_options = move || *cfg_options.get_or_init(|| krate.cfg_options(db)); @@ -295,7 +295,7 @@ fn macro_input_callback( pub(crate) fn attr_macro_input_to_token_tree( db: &dyn ExpandDatabase, node: &SyntaxNode, - span_map: SpanMapRef<'_>, + span_map: SpanMap<'_>, span: Span, is_derive: bool, censor_item_tree_attr_ids: &[AttrId], diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index 8dddddfabb..beae6e843e 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -3,6 +3,7 @@ use base_db::{Crate, SourceDatabase}; use mbe::MatchedArmIndex; use span::{AstIdMap, Edition, Span, SyntaxContext}; +use std::borrow::Cow; use syntax::{AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, ast}; use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree}; use triomphe::Arc; @@ -17,11 +18,11 @@ use crate::{ fixup::{self, SyntaxFixupUndoInfo}, hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt}, proc_macro::{CrateProcMacros, CustomProcMacroExpander, ProcMacros}, - span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef}, + span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, tt, }; /// This is just to ensure the types of smart_macro_arg and macro_arg are the same -type MacroArgResult = (Arc<tt::TopSubtree>, SyntaxFixupUndoInfo, Span); +type MacroArgResult = (tt::TopSubtree, SyntaxFixupUndoInfo, Span); /// Total limit on the number of tokens produced by any macro invocation. /// /// If an invocation produces more tokens than this limit, it will not be stored in the database and @@ -30,10 +31,10 @@ type MacroArgResult = (Arc<tt::TopSubtree>, SyntaxFixupUndoInfo, Span); /// Actual max for `analysis-stats .` at some point: 30672. const TOKEN_LIMIT: usize = 2_097_152; -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum TokenExpander { +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum TokenExpander<'db> { /// Old-style `macro_rules` or the new macros 2.0 - DeclarativeMacro(Arc<DeclarativeMacroExpander>), + DeclarativeMacro(&'db DeclarativeMacroExpander), /// Stuff like `line!` and `file!`. BuiltIn(BuiltinFnLikeExpander), /// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.) @@ -67,65 +68,59 @@ pub trait ExpandDatabase: SourceDatabase { fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode; /// Implementation for the macro case. - #[salsa::lru(512)] + #[salsa::transparent] fn parse_macro_expansion( &self, macro_file: MacroCallId, - ) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>; + ) -> &ExpandResult<(Parse<SyntaxNode>, ExpansionSpanMap)>; #[salsa::transparent] #[salsa::invoke(SpanMap::new)] - fn span_map(&self, file_id: HirFileId) -> SpanMap; + fn span_map(&self, file_id: HirFileId) -> SpanMap<'_>; #[salsa::transparent] #[salsa::invoke(crate::span_map::expansion_span_map)] - fn expansion_span_map(&self, file_id: MacroCallId) -> Arc<ExpansionSpanMap>; + fn expansion_span_map(&self, file_id: MacroCallId) -> &ExpansionSpanMap; #[salsa::invoke(crate::span_map::real_span_map)] - fn real_span_map(&self, file_id: EditionedFileId) -> Arc<RealSpanMap>; - - /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the - /// reason why we use salsa at all. - /// - /// We encode macro definitions into ids of macro calls, this what allows us - /// to be incremental. - #[salsa::transparent] - fn intern_macro_call(&self, macro_call: MacroCallLoc) -> MacroCallId; #[salsa::transparent] - fn lookup_intern_macro_call(&self, macro_call: MacroCallId) -> MacroCallLoc; + fn real_span_map(&self, file_id: EditionedFileId) -> &RealSpanMap; /// Lowers syntactic macro call to a token tree representation. That's a firewall /// query, only typing in the macro call itself changes the returned /// subtree. #[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"] #[salsa::invoke(macro_arg)] - fn macro_arg(&self, id: MacroCallId) -> MacroArgResult; + #[salsa::transparent] + fn macro_arg(&self, id: MacroCallId) -> &MacroArgResult; #[salsa::transparent] - fn macro_arg_considering_derives( - &self, + fn macro_arg_considering_derives<'db>( + &'db self, id: MacroCallId, kind: &MacroCallKind, - ) -> MacroArgResult; + ) -> &'db MacroArgResult; /// Fetches the expander for this macro. #[salsa::transparent] #[salsa::invoke(TokenExpander::macro_expander)] - fn macro_expander(&self, id: MacroDefId) -> TokenExpander; + fn macro_expander(&self, id: MacroDefId) -> TokenExpander<'_>; /// Fetches (and compiles) the expander of this decl macro. #[salsa::invoke(DeclarativeMacroExpander::expander)] + #[salsa::transparent] fn decl_macro_expander( &self, def_crate: Crate, id: AstId<ast::Macro>, - ) -> Arc<DeclarativeMacroExpander>; + ) -> &DeclarativeMacroExpander; /// Special case of the previous query for procedural macros. We can't LRU /// proc macros, since they are not deterministic in general, and /// non-determinism breaks salsa in a very, very, very bad way. /// @edwin0cheng heroically debugged this once! See #4315 for details #[salsa::invoke(expand_proc_macro)] - fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<Arc<tt::TopSubtree>>; + #[salsa::transparent] + fn expand_proc_macro(&self, call: MacroCallId) -> &ExpandResult<tt::TopSubtree>; /// Retrieves the span to be used for a proc-macro expansions spans. /// This is a firewall query as it requires parsing the file, which we don't want proc-macros to /// directly depend on as that would cause to frequent invalidations, mainly because of the @@ -134,12 +129,12 @@ pub trait ExpandDatabase: SourceDatabase { #[salsa::invoke_interned(proc_macro_span)] fn proc_macro_span(&self, fun: AstId<ast::Fn>) -> Span; - /// Firewall query that returns the errors from the `parse_macro_expansion` query. #[salsa::invoke(parse_macro_expansion_error)] + #[salsa::transparent] fn parse_macro_expansion_error( &self, macro_call: MacroCallId, - ) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>; + ) -> Option<ExpandResult<Arc<[SyntaxError]>>>; #[salsa::transparent] fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContext; @@ -154,7 +149,7 @@ fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> match file { HirFileId::FileId(_) => SyntaxContext::root(edition), HirFileId::MacroFile(m) => { - let kind = db.lookup_intern_macro_call(m).kind; + let kind = m.loc(db).kind; db.macro_arg_considering_derives(m, &kind).2.ctx } } @@ -177,11 +172,11 @@ pub fn expand_speculative( speculative_args: &SyntaxNode, token_to_map: SyntaxToken, ) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> { - let loc = db.lookup_intern_macro_call(actual_macro_call); - let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind); + let loc = actual_macro_call.loc(db); + let (_, _, span) = *db.macro_arg_considering_derives(actual_macro_call, &loc.kind); let span_map = RealSpanMap::absolute(span.anchor.file_id); - let span_map = SpanMapRef::RealSpanMap(&span_map); + let span_map = SpanMap::RealSpanMap(&span_map); // Build the subtree and token mapping for the speculative args let (mut tt, undo_info) = match &loc.kind { @@ -358,48 +353,42 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode { // FIXME: We should verify that the parsed node is one of the many macro node variants we expect // instead of having it be untyped +#[salsa_macros::tracked(returns(ref), lru = 512)] fn parse_macro_expansion( db: &dyn ExpandDatabase, macro_file: MacroCallId, -) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> { +) -> ExpandResult<(Parse<SyntaxNode>, ExpansionSpanMap)> { let _p = tracing::info_span!("parse_macro_expansion").entered(); - let loc = db.lookup_intern_macro_call(macro_file); + let loc = macro_file.loc(db); let expand_to = loc.expand_to(); let mbe::ValueResult { value: (tt, matched_arm), err } = macro_expand(db, macro_file, loc); - let (parse, mut rev_token_map) = token_tree_to_syntax_node( - db, - match &tt { - CowArc::Arc(it) => it, - CowArc::Owned(it) => it, - }, - expand_to, - ); + let (parse, mut rev_token_map) = token_tree_to_syntax_node(db, &tt, expand_to); rev_token_map.matched_arm = matched_arm; - ExpandResult { value: (parse, Arc::new(rev_token_map)), err } + ExpandResult { value: (parse, rev_token_map), err } } fn parse_macro_expansion_error( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, -) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>> { +) -> Option<ExpandResult<Arc<[SyntaxError]>>> { let e: ExpandResult<Arc<[SyntaxError]>> = - db.parse_macro_expansion(macro_call_id).map(|it| Arc::from(it.0.errors())); - if e.value.is_empty() && e.err.is_none() { None } else { Some(Arc::new(e)) } + db.parse_macro_expansion(macro_call_id).as_ref().map(|it| Arc::from(it.0.errors())); + if e.value.is_empty() && e.err.is_none() { None } else { Some(e) } } pub(crate) fn parse_with_map( db: &dyn ExpandDatabase, file_id: HirFileId, -) -> (Parse<SyntaxNode>, SpanMap) { +) -> (Parse<SyntaxNode>, SpanMap<'_>) { match file_id { HirFileId::FileId(file_id) => { (file_id.parse(db).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id))) } HirFileId::MacroFile(macro_file) => { - let (parse, map) = db.parse_macro_expansion(macro_file).value; - (parse, SpanMap::ExpansionSpanMap(map)) + let (parse, map) = &db.parse_macro_expansion(macro_file).value; + (parse.clone(), SpanMap::ExpansionSpanMap(map)) } } } @@ -409,11 +398,11 @@ pub(crate) fn parse_with_map( /// /// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is #[allow(deprecated)] // we are macro_arg_considering_derives -fn macro_arg_considering_derives( - db: &dyn ExpandDatabase, +fn macro_arg_considering_derives<'db>( + db: &'db dyn ExpandDatabase, id: MacroCallId, kind: &MacroCallKind, -) -> MacroArgResult { +) -> &'db MacroArgResult { match kind { // Get the macro arg for the derive macro MacroCallKind::Derive { derive_macro_id, .. } => db.macro_arg(*derive_macro_id), @@ -422,8 +411,9 @@ fn macro_arg_considering_derives( } } +#[salsa_macros::tracked(returns(ref))] fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { - let loc = db.lookup_intern_macro_call(id); + let loc = id.loc(db); if let MacroCallLoc { def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. }, @@ -447,10 +437,10 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let dummy_tt = |kind| { ( - Arc::new(tt::TopSubtree::from_token_trees( + tt::TopSubtree::from_token_trees( tt::Delimiter { open: span, close: span, kind }, tt::TokenTreesView::empty(), - )), + ), SyntaxFixupUndoInfo::default(), span, ) @@ -487,7 +477,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let mut tt = syntax_bridge::syntax_node_to_token_tree( tt.syntax(), - map.as_ref(), + map, span, if loc.def.is_proc_macro() { DocCommentDesugarMode::ProcMacro @@ -499,7 +489,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { // proc macros expect their inputs without parentheses, MBEs expect it with them included tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); } - return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span); + return (tt, SyntaxFixupUndoInfo::NONE, span); } // MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro MacroCallKind::Derive { .. } => { @@ -522,7 +512,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let (mut tt, undo_info) = attr_macro_input_to_token_tree( db, item_node.syntax(), - map.as_ref(), + map, span, is_derive, censor_item_tree_attr_ids, @@ -534,11 +524,11 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); } - (Arc::new(tt), undo_info, span) + (tt, undo_info, span) } -impl TokenExpander { - fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander { +impl<'db> TokenExpander<'db> { + fn macro_expander(db: &'db dyn ExpandDatabase, id: MacroDefId) -> TokenExpander<'db> { match id.kind { MacroDefKind::Declarative(ast_id, _) => { TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id)) @@ -552,27 +542,26 @@ impl TokenExpander { } } -enum CowArc<T> { - Arc(Arc<T>), - Owned(T), -} - fn macro_expand( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, loc: MacroCallLoc, -) -> ExpandResult<(CowArc<tt::TopSubtree>, MatchedArmIndex)> { +) -> ExpandResult<(Cow<'_, tt::TopSubtree>, MatchedArmIndex)> { let _p = tracing::info_span!("macro_expand").entered(); 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).zip_val(None); + return db + .expand_proc_macro(macro_call_id) + .as_ref() + .map(|it| (Cow::Borrowed(it), None)); } _ => { let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(macro_call_id, &loc.kind); + let span = *span; - let arg = &*macro_arg; + let arg = macro_arg; let res = match loc.def.kind { MacroDefKind::Declarative(id, _) => db .decl_macro_expander(loc.def.krate, id) @@ -592,7 +581,7 @@ fn macro_expand( // As such we just return the input subtree here. let eager = match &loc.kind { MacroCallKind::FnLike { eager: None, .. } => { - return ExpandResult::ok(CowArc::Arc(macro_arg.clone())).zip_val(None); + return ExpandResult::ok(Cow::Borrowed(macro_arg)).zip_val(None); } MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager), _ => None, @@ -608,7 +597,7 @@ fn macro_expand( } MacroDefKind::BuiltInAttr(_, it) => { 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.zip_val(None) } MacroDefKind::ProcMacro(_, _, _) => unreachable!(), @@ -622,12 +611,12 @@ fn macro_expand( // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { return value - .map(|()| CowArc::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))) + .map(|()| Cow::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))) .zip_val(matched_arm); } } - ExpandResult { value: (CowArc::Owned(tt), matched_arm), err } + ExpandResult { value: (Cow::Owned(tt), matched_arm), err } } fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span { @@ -641,11 +630,9 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span { span_map.span_for_range(range) } -fn expand_proc_macro( - db: &dyn ExpandDatabase, - id: MacroCallId, -) -> ExpandResult<Arc<tt::TopSubtree>> { - let loc = db.lookup_intern_macro_call(id); +#[salsa_macros::tracked(returns(ref))] +fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::TopSubtree> { + let loc = id.loc(db); let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind); let (ast, expander) = match loc.def.kind { @@ -664,7 +651,7 @@ fn expand_proc_macro( db, loc.def.krate, loc.krate, - ¯o_arg, + macro_arg, attr_arg, span_with_def_site_ctxt(db, span, id.into(), loc.def.edition), span_with_call_site_ctxt(db, span, id.into(), loc.def.edition), @@ -674,12 +661,12 @@ fn expand_proc_macro( // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { - return value.map(|()| Arc::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)))); + return value.map(|()| tt::TopSubtree::empty(tt::DelimSpan::from_single(*span))); } - fixup::reverse_fixups(&mut tt, &undo_info); + fixup::reverse_fixups(&mut tt, undo_info); - ExpandResult { value: Arc::new(tt), err } + ExpandResult { value: tt, err } } pub(crate) fn token_tree_to_syntax_node( @@ -714,11 +701,3 @@ fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> { }) } } - -fn intern_macro_call(db: &dyn ExpandDatabase, macro_call: MacroCallLoc) -> MacroCallId { - MacroCallId::new(db, macro_call) -} - -fn lookup_intern_macro_call(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> MacroCallLoc { - macro_call.loc(db) -} diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs index 4b2c6e7351..99db0dbcb9 100644 --- a/crates/hir-expand/src/declarative.rs +++ b/crates/hir-expand/src/declarative.rs @@ -10,7 +10,6 @@ use syntax::{ ast::{self, HasAttrs}, }; use syntax_bridge::DocCommentDesugarMode; -use triomphe::Arc; use crate::{ AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId, @@ -37,7 +36,7 @@ impl DeclarativeMacroExpander { call_id: MacroCallId, span: Span, ) -> ExpandResult<(tt::TopSubtree, Option<u32>)> { - let loc = db.lookup_intern_macro_call(call_id); + let loc = call_id.loc(db); match self.mac.err() { Some(_) => ExpandResult::new( (tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }), None), @@ -78,12 +77,16 @@ impl DeclarativeMacroExpander { .map_err(Into::into), } } +} +#[salsa::tracked] +impl DeclarativeMacroExpander { + #[salsa::tracked(returns(ref))] pub(crate) fn expander( db: &dyn ExpandDatabase, def_crate: Crate, id: AstId<ast::Macro>, - ) -> Arc<DeclarativeMacroExpander> { + ) -> DeclarativeMacroExpander { let (root, map) = crate::db::parse_with_map(db, id.file_id); let root = root.syntax_node(); @@ -117,8 +120,7 @@ impl DeclarativeMacroExpander { def_crate.data(db).edition } else { // UNWRAP-SAFETY: Only the root context has no outer expansion - let krate = - db.lookup_intern_macro_call(ctx.outer_expn(db).unwrap().into()).def.krate; + let krate = crate::MacroCallId::from(ctx.outer_expn(db).unwrap()).loc(db).def.krate; krate.data(db).edition } }; @@ -128,7 +130,7 @@ impl DeclarativeMacroExpander { Some(arg) => { let tt = syntax_bridge::syntax_node_to_token_tree( arg.syntax(), - map.as_ref(), + map, map.span_for_range( macro_rules.macro_rules_token().unwrap().text_range(), ), @@ -152,14 +154,14 @@ impl DeclarativeMacroExpander { let args = macro_def.args().map(|args| { syntax_bridge::syntax_node_to_token_tree( args.syntax(), - map.as_ref(), + map, span, DocCommentDesugarMode::Mbe, ) }); let body = syntax_bridge::syntax_node_to_token_tree( body.syntax(), - map.as_ref(), + map, span, DocCommentDesugarMode::Mbe, ); @@ -177,6 +179,6 @@ impl DeclarativeMacroExpander { HirFileId::MacroFile(macro_file) => macro_file.lookup(db).ctxt, HirFileId::FileId(file) => SyntaxContext::root(file.edition(db)), }); - Arc::new(DeclarativeMacroExpander { mac, transparency, edition }) + DeclarativeMacroExpander { mac, transparency, edition } } } diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs index 0b6124ebf3..a19f58709b 100644 --- a/crates/hir-expand/src/eager.rs +++ b/crates/hir-expand/src/eager.rs @@ -20,9 +20,10 @@ //! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros> use base_db::Crate; use span::SyntaxContext; -use syntax::{AstPtr, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent, ted}; +use syntax::{ + AstPtr, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent, syntax_editor::SyntaxEditor, +}; use syntax_bridge::DocCommentDesugarMode; -use triomphe::Arc; use crate::{ AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, @@ -59,7 +60,7 @@ pub fn expand_eager_macro_input( kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None }, ctxt: call_site, }; - let arg_id = db.intern_macro_call(loc); + let arg_id = MacroCallId::new(db, loc); #[allow(deprecated)] // builtin eager macros are never derives let (_, _, span) = db.macro_arg(arg_id); let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } = @@ -70,7 +71,7 @@ pub fn expand_eager_macro_input( let ExpandResult { value: expanded_eager_input, err } = { eager_macro_recur( db, - &arg_exp_map, + arg_exp_map, &mut arg_map, TextSize::new(0), InFile::new(arg_id.into(), arg_exp.syntax_node()), @@ -80,7 +81,7 @@ pub fn expand_eager_macro_input( eager_callback, ) }; - let err = parse_err.or(err); + let err = parse_err.clone().or(err); if cfg!(debug_assertions) { arg_map.finish(); } @@ -92,7 +93,7 @@ pub fn expand_eager_macro_input( let mut subtree = syntax_bridge::syntax_node_to_token_tree( &expanded_eager_input, arg_map, - span, + *span, DocCommentDesugarMode::Mbe, ); @@ -104,28 +105,28 @@ pub fn expand_eager_macro_input( kind: MacroCallKind::FnLike { ast_id, expand_to, - eager: Some(Arc::new(EagerCallInfo { - arg: Arc::new(subtree), + eager: Some(Box::new(EagerCallInfo { + arg: subtree, arg_id, error: err.clone(), - span, + span: *span, })), }, ctxt: call_site, }; - ExpandResult { value: Some(db.intern_macro_call(loc)), err } + ExpandResult { value: Some(MacroCallId::new(db, loc)), err } } -fn lazy_expand( - db: &dyn ExpandDatabase, +fn lazy_expand<'db>( + db: &'db dyn ExpandDatabase, def: &MacroDefId, macro_call: &ast::MacroCall, ast_id: AstId<ast::MacroCall>, krate: Crate, call_site: SyntaxContext, eager_callback: EagerCallBackFn<'_>, -) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<ExpansionSpanMap>)> { +) -> ExpandResult<(InFile<Parse<SyntaxNode>>, &'db ExpansionSpanMap)> { let expand_to = ExpandTo::from_call_site(macro_call); let id = def.make_call( db, @@ -135,7 +136,9 @@ fn lazy_expand( ); eager_callback(ast_id.map(|ast_id| (AstPtr::new(macro_call), ast_id)), id); - db.parse_macro_expansion(id).map(|parse| (InFile::new(id.into(), parse.0), parse.1)) + db.parse_macro_expansion(id) + .as_ref() + .map(|parse| (InFile::new(id.into(), parse.0.clone()), &parse.1)) } fn eager_macro_recur( @@ -149,7 +152,8 @@ fn eager_macro_recur( macro_resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>, eager_callback: EagerCallBackFn<'_>, ) -> ExpandResult<Option<(SyntaxNode, TextSize)>> { - let original = curr.value.clone_for_update(); + let (editor, _) = SyntaxEditor::new(curr.value.clone()); + let original = curr.value.clone(); let mut replacements = Vec::new(); @@ -232,7 +236,7 @@ fn eager_macro_recur( syntax_node.clone_for_update(), offset + syntax_node.text_range().len(), )), - err: err.or(err2), + err: err.clone().or_else(|| err2.clone()), } } None => ExpandResult { value: None, err }, @@ -256,7 +260,7 @@ fn eager_macro_recur( // replace macro inside let ExpandResult { value, err: error } = eager_macro_recur( db, - &tm, + tm, expanded_map, offset, // FIXME: We discard parse errors here @@ -288,6 +292,7 @@ fn eager_macro_recur( } } - replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); + replacements.into_iter().rev().for_each(|(old, new)| editor.replace(old.syntax(), new)); + let original = editor.finish().new_root().clone(); ExpandResult { value: Some((original, offset)), err: error } } diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs index 71da560b15..a4c206156d 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs @@ -128,6 +128,16 @@ impl ErasedAstId { } } +impl<FileKind, N: AstNode> InFileWrapper<FileKind, AstPtr<N>> { + #[inline] + pub fn upcast<M: AstNode>(self) -> InFileWrapper<FileKind, AstPtr<M>> + where + N: Into<M>, + { + self.map(|it| it.upcast()) + } +} + impl<FileKind, T> InFileWrapper<FileKind, T> { pub fn new(file_id: FileKind, value: T) -> Self { Self { file_id, value } @@ -256,8 +266,10 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> { ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ { let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() { Some(parent) => Some(node.with_value(parent)), - None => db - .lookup_intern_macro_call(node.file_id.macro_file()?) + None => node + .file_id + .macro_file()? + .loc(db) .to_node_item(db) .syntax() .cloned() @@ -273,8 +285,10 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> { ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ { let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() { Some(parent) => Some(node.with_value(parent)), - None => db - .lookup_intern_macro_call(node.file_id.macro_file()?) + None => node + .file_id + .macro_file()? + .loc(db) .to_node_item(db) .syntax() .cloned() @@ -328,7 +342,7 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> { let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted( db, - &db.expansion_span_map(file_id), + db.expansion_span_map(file_id), self.value.borrow().text_range(), )?; @@ -371,7 +385,7 @@ impl InFile<SyntaxToken> { HirFileId::MacroFile(mac_file) => { let (range, ctxt) = span_for_offset( db, - &db.expansion_span_map(mac_file), + db.expansion_span_map(mac_file), self.value.text_range().start(), ); @@ -382,7 +396,7 @@ impl InFile<SyntaxToken> { } // Fall back to whole macro call. - let loc = db.lookup_intern_macro_call(mac_file); + let loc = mac_file.loc(db); loc.kind.original_call_range(db, loc.krate) } } @@ -397,7 +411,7 @@ impl InFile<SyntaxToken> { HirFileId::MacroFile(mac_file) => { let (range, ctxt) = span_for_offset( db, - &db.expansion_span_map(mac_file), + db.expansion_span_map(mac_file), self.value.text_range().start(), ); @@ -411,7 +425,7 @@ impl InFile<SyntaxToken> { impl InMacroFile<TextSize> { pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> (FileRange, SyntaxContext) { - span_for_offset(db, &db.expansion_span_map(self.file_id), self.value) + span_for_offset(db, db.expansion_span_map(self.file_id), self.value) } } @@ -425,10 +439,10 @@ impl InFile<TextRange> { (FileRange { file_id, range: self.value }, SyntaxContext::root(file_id.edition(db))) } HirFileId::MacroFile(mac_file) => { - match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) { + match map_node_range_up(db, db.expansion_span_map(mac_file), self.value) { Some(it) => it, None => { - let loc = db.lookup_intern_macro_call(mac_file); + let loc = mac_file.loc(db); ( loc.kind.original_call_range(db, loc.krate), SyntaxContext::root(loc.def.edition), @@ -443,10 +457,10 @@ impl InFile<TextRange> { match self.file_id { HirFileId::FileId(file_id) => FileRange { file_id, range: self.value }, HirFileId::MacroFile(mac_file) => { - match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) { + match map_node_range_up_rooted(db, db.expansion_span_map(mac_file), self.value) { Some(it) => it, _ => { - let loc = db.lookup_intern_macro_call(mac_file); + let loc = mac_file.loc(db); loc.kind.original_call_range(db, loc.krate) } } @@ -461,10 +475,10 @@ impl InFile<TextRange> { match self.file_id { HirFileId::FileId(file_id) => FileRange { file_id, range: self.value }, HirFileId::MacroFile(mac_file) => { - match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) { + match map_node_range_up_rooted(db, db.expansion_span_map(mac_file), self.value) { Some(it) => it, _ => { - let loc = db.lookup_intern_macro_call(mac_file); + let loc = mac_file.loc(db); loc.kind.original_call_range_with_input(db) } } @@ -482,7 +496,7 @@ impl InFile<TextRange> { SyntaxContext::root(file_id.edition(db)), )), HirFileId::MacroFile(mac_file) => { - map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) + map_node_range_up(db, db.expansion_span_map(mac_file), self.value) } } } @@ -494,7 +508,7 @@ impl InFile<TextRange> { match self.file_id { HirFileId::FileId(file_id) => Some(FileRange { file_id, range: self.value }), HirFileId::MacroFile(mac_file) => { - map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) + map_node_range_up_rooted(db, db.expansion_span_map(mac_file), self.value) } } } @@ -516,7 +530,7 @@ impl<N: AstNode> InFile<N> { let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted( db, - &db.expansion_span_map(file_id), + db.expansion_span_map(file_id), self.value.syntax().text_range(), )?; diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 424655ed65..939104b709 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -14,11 +14,11 @@ use syntax::{ match_ast, }; use syntax_bridge::DocCommentDesugarMode; -use triomphe::Arc; +use thin_vec::ThinVec; use tt::{Spacing, TransformTtAction, transform_tt}; use crate::{ - span_map::SpanMapRef, + span_map::SpanMap, tt::{self, Ident, Leaf, Punct, TopSubtree}, }; @@ -35,8 +35,7 @@ pub(crate) struct SyntaxFixups { /// This is the information needed to reverse the fixups. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SyntaxFixupUndoInfo { - // FIXME: ThinArc<[Subtree]> - original: Option<Arc<Box<[TopSubtree]>>>, + original: Option<ThinVec<TopSubtree>>, } impl SyntaxFixupUndoInfo { @@ -51,7 +50,7 @@ const FIXUP_DUMMY_RANGE: TextRange = TextRange::empty(TextSize::new(0)); const FIXUP_DUMMY_RANGE_END: TextSize = TextSize::new(!0); pub(crate) fn fixup_syntax( - span_map: SpanMapRef<'_>, + span_map: SpanMap<'_>, node: &SyntaxNode, call_site: Span, mode: DocCommentDesugarMode, @@ -59,7 +58,7 @@ pub(crate) fn fixup_syntax( let mut append = FxHashMap::<SyntaxElement, _>::default(); let mut remove = FxHashSet::<SyntaxElement>::default(); let mut preorder = node.preorder(); - let mut original = Vec::new(); + let mut original = ThinVec::new(); let dummy_range = FIXUP_DUMMY_RANGE; let fake_span = |range| { let span = span_map.span_for_range(range); @@ -317,13 +316,12 @@ pub(crate) fn fixup_syntax( } } } + original.shrink_to_fit(); let needs_fixups = !append.is_empty() || !original.is_empty(); SyntaxFixups { append, remove, - undo_info: SyntaxFixupUndoInfo { - original: needs_fixups.then(|| Arc::new(original.into_boxed_slice())), - }, + undo_info: SyntaxFixupUndoInfo { original: needs_fixups.then_some(original) }, } } @@ -340,7 +338,7 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool { } pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInfo) { - let Some(undo_info) = undo_info.original.as_deref() else { return }; + let Some(undo_info) = &undo_info.original else { return }; let undo_info = &**undo_info; let top_subtree = tt.top_subtree(); let open_span = top_subtree.delimiter.open; @@ -402,7 +400,6 @@ mod tests { use span::{Edition, EditionedFileId, FileId}; use syntax::TextRange; use syntax_bridge::DocCommentDesugarMode; - use triomphe::Arc; use crate::{ fixup::reverse_fixups, @@ -440,19 +437,19 @@ mod tests { #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) { let parsed = syntax::SourceFile::parse(ra_fixture, span::Edition::CURRENT); - let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(EditionedFileId::new( + let span_map = SpanMap::RealSpanMap(&RealSpanMap::absolute(EditionedFileId::new( FileId::from_raw(0), Edition::CURRENT, - )))); + ))); let fixups = super::fixup_syntax( - span_map.as_ref(), + span_map, &parsed.syntax_node(), span_map.span_for_range(TextRange::empty(0.into())), DocCommentDesugarMode::Mbe, ); let mut tt = syntax_bridge::syntax_node_to_token_tree_modified( &parsed.syntax_node(), - span_map.as_ref(), + span_map, fixups.append, fixups.remove, span_map.span_for_range(TextRange::empty(0.into())), @@ -494,7 +491,7 @@ mod tests { // modulo token IDs and `Punct`s' spacing. let original_as_tt = syntax_bridge::syntax_node_to_token_tree( &parsed.syntax_node(), - span_map.as_ref(), + span_map, span_map.span_for_range(TextRange::empty(0.into())), DocCommentDesugarMode::Mbe, ); diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs index ce7650d077..1cf8ce2a57 100644 --- a/crates/hir-expand/src/hygiene.rs +++ b/crates/hir-expand/src/hygiene.rs @@ -81,7 +81,7 @@ pub(super) fn apply_mark( return apply_mark_internal(db, ctxt, call_id, transparency, edition); } - let call_site_ctxt = db.lookup_intern_macro_call(call_id.into()).ctxt; + let call_site_ctxt = crate::MacroCallId::from(call_id).loc(db).ctxt; let mut call_site_ctxt = if transparency == Transparency::SemiOpaque { call_site_ctxt.normalize_to_macros_2_0(db) } else { diff --git a/crates/hir-expand/src/inert_attr_macro.rs b/crates/hir-expand/src/inert_attr_macro.rs index 53b624d9a6..4185b7b018 100644 --- a/crates/hir-expand/src/inert_attr_macro.rs +++ b/crates/hir-expand/src/inert_attr_macro.rs @@ -702,6 +702,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_dyn_incompatible_trait, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_dump_vtable, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), DuplicatesOk), gated!( diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 8d42a24e2f..0850d6156d 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -69,12 +69,12 @@ pub use tt; #[macro_export] macro_rules! impl_intern_lookup { - ($db:ident, $id:ident, $loc:ident, $intern:ident, $lookup:ident) => { + ($db:ident, $id:ident, $loc:ident) => { impl $crate::Intern for $loc { type Database = dyn $db; type ID = $id; fn intern(self, db: &Self::Database) -> Self::ID { - db.$intern(self) + $id::new(db, self) } } @@ -82,7 +82,7 @@ macro_rules! impl_intern_lookup { type Database = dyn $db; type Data = $loc; fn lookup(&self, db: &Self::Database) -> Self::Data { - db.$lookup(*self) + self.loc(db) } } }; @@ -101,13 +101,7 @@ pub trait Lookup { fn lookup(&self, db: &Self::Database) -> Self::Data; } -impl_intern_lookup!( - ExpandDatabase, - MacroCallId, - MacroCallLoc, - intern_macro_call, - lookup_intern_macro_call -); +impl_intern_lookup!(ExpandDatabase, MacroCallId, MacroCallLoc); pub type ExpandResult<T> = ValueResult<T, ExpandError>; @@ -279,7 +273,7 @@ impl MacroDefKind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EagerCallInfo { /// The expanded argument of the eager macro. - arg: Arc<tt::TopSubtree>, + arg: tt::TopSubtree, /// Call id of the eager macro's input file (this is the macro file for its fully expanded input). arg_id: MacroCallId, error: Option<ExpandError>, @@ -296,7 +290,7 @@ pub enum MacroCallKind { /// for the eager input macro file. // FIXME: This is being interned, subtrees can vary quickly differing just slightly causing // leakage problems here - eager: Option<Arc<EagerCallInfo>>, + eager: Option<Box<EagerCallInfo>>, }, Derive { ast_id: AstId<ast::Adt>, @@ -311,7 +305,7 @@ pub enum MacroCallKind { Attr { ast_id: AstId<ast::Item>, // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index`. - attr_args: Option<Arc<tt::TopSubtree>>, + attr_args: Option<Box<tt::TopSubtree>>, /// This contains the list of all *active* attributes (derives and attr macros) preceding this /// attribute, including this attribute. You can retrieve the [`AttrId`] of the current attribute /// by calling [`invoc_attr()`] on this. @@ -386,7 +380,7 @@ impl HirFileId { pub fn edition(self, db: &dyn ExpandDatabase) -> Edition { match self { HirFileId::FileId(file_id) => file_id.edition(db), - HirFileId::MacroFile(m) => db.lookup_intern_macro_call(m).def.edition, + HirFileId::MacroFile(m) => m.loc(db).def.edition, } } pub fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId { @@ -395,7 +389,7 @@ impl HirFileId { match file_id { HirFileId::FileId(id) => break id, HirFileId::MacroFile(macro_call_id) => { - file_id = db.lookup_intern_macro_call(macro_call_id).kind.file_id() + file_id = macro_call_id.loc(db).kind.file_id() } } } @@ -406,7 +400,7 @@ impl HirFileId { match self { HirFileId::FileId(id) => break id, HirFileId::MacroFile(file) => { - let loc = db.lookup_intern_macro_call(file); + let loc = file.loc(db); if loc.def.is_include() && let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind && let Ok(it) = include_input_to_file_id(db, file, &eager.arg) @@ -420,21 +414,21 @@ impl HirFileId { } pub fn original_call_node(self, db: &dyn ExpandDatabase) -> Option<InRealFile<SyntaxNode>> { - let mut call = db.lookup_intern_macro_call(self.macro_file()?).to_node(db); + let mut call = self.macro_file()?.loc(db).to_node(db); loop { match call.file_id { HirFileId::FileId(file_id) => { break Some(InRealFile { file_id, value: call.value }); } HirFileId::MacroFile(macro_call_id) => { - call = db.lookup_intern_macro_call(macro_call_id).to_node(db); + call = macro_call_id.loc(db).to_node(db); } } } } pub fn call_node(self, db: &dyn ExpandDatabase) -> Option<InFile<SyntaxNode>> { - Some(db.lookup_intern_macro_call(self.macro_file()?).to_node(db)) + Some(self.macro_file()?.loc(db).to_node(db)) } pub fn as_builtin_derive_attr_node( @@ -442,7 +436,7 @@ impl HirFileId { db: &dyn ExpandDatabase, ) -> Option<InFile<ast::Attr>> { let macro_file = self.macro_file()?; - let loc = db.lookup_intern_macro_call(macro_file); + let loc = macro_file.loc(db); let attr = match loc.def.kind { MacroDefKind::BuiltInDerive(..) => loc.to_node(db), _ => return None, @@ -471,13 +465,13 @@ pub enum MacroKind { impl MacroCallId { pub fn call_node(self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode> { - db.lookup_intern_macro_call(self).to_node(db) + self.loc(db).to_node(db) } pub fn expansion_level(self, db: &dyn ExpandDatabase) -> u32 { let mut level = 0; let mut macro_file = self; loop { - let loc = db.lookup_intern_macro_call(macro_file); + let loc = macro_file.loc(db); level += 1; macro_file = match loc.kind.file_id() { @@ -487,16 +481,16 @@ impl MacroCallId { } } pub fn parent(self, db: &dyn ExpandDatabase) -> HirFileId { - db.lookup_intern_macro_call(self).kind.file_id() + self.loc(db).kind.file_id() } /// Return expansion information if it is a macro-expansion file - pub fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo { + pub fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo<'_> { ExpansionInfo::new(db, self) } pub fn kind(self, db: &dyn ExpandDatabase) -> MacroKind { - match db.lookup_intern_macro_call(self).def.kind { + match self.loc(db).def.kind { MacroDefKind::Declarative(..) => MacroKind::Declarative, MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInEager(..) => { MacroKind::DeclarativeBuiltIn @@ -510,24 +504,24 @@ impl MacroCallId { } pub fn is_include_macro(self, db: &dyn ExpandDatabase) -> bool { - db.lookup_intern_macro_call(self).def.is_include() + self.loc(db).def.is_include() } pub fn is_include_like_macro(self, db: &dyn ExpandDatabase) -> bool { - db.lookup_intern_macro_call(self).def.is_include_like() + self.loc(db).def.is_include_like() } pub fn is_env_or_option_env(self, db: &dyn ExpandDatabase) -> bool { - db.lookup_intern_macro_call(self).def.is_env_or_option_env() + self.loc(db).def.is_env_or_option_env() } pub fn is_eager(self, db: &dyn ExpandDatabase) -> bool { - let loc = db.lookup_intern_macro_call(self); + let loc = self.loc(db); matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) } pub fn eager_arg(self, db: &dyn ExpandDatabase) -> Option<MacroCallId> { - let loc = db.lookup_intern_macro_call(self); + let loc = self.loc(db); match &loc.kind { MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id), _ => None, @@ -535,7 +529,7 @@ impl MacroCallId { } pub fn is_derive_attr_pseudo_expansion(self, db: &dyn ExpandDatabase) -> bool { - let loc = db.lookup_intern_macro_call(self); + let loc = self.loc(db); loc.def.is_attribute_derive() } } @@ -548,7 +542,7 @@ impl MacroDefId { kind: MacroCallKind, ctxt: SyntaxContext, ) -> MacroCallId { - db.intern_macro_call(MacroCallLoc { def: self, krate, kind, ctxt }) + MacroCallId::new(db, MacroCallLoc { def: self, krate, kind, ctxt }) } pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile<TextRange> { @@ -725,7 +719,7 @@ impl MacroCallKind { let file_id = loop { match kind.file_id() { HirFileId::MacroFile(file) => { - kind = db.lookup_intern_macro_call(file).kind; + kind = file.loc(db).kind; } HirFileId::FileId(file_id) => break file_id, } @@ -750,7 +744,7 @@ impl MacroCallKind { let file_id = loop { match kind.file_id() { HirFileId::MacroFile(file) => { - kind = db.lookup_intern_macro_call(file).kind; + kind = file.loc(db).kind; } HirFileId::FileId(file_id) => break file_id, } @@ -797,16 +791,16 @@ impl MacroCallKind { // FIXME: can be expensive to create, we should check the use sites and maybe replace them with // simpler function calls if the map is only used once #[derive(Clone, Debug, PartialEq, Eq)] -pub struct ExpansionInfo { +pub struct ExpansionInfo<'db> { expanded: InMacroFile<SyntaxNode>, /// The argument TokenTree or item for attributes arg: InFile<Option<SyntaxNode>>, - exp_map: Arc<ExpansionSpanMap>, - arg_map: SpanMap, + exp_map: &'db ExpansionSpanMap, + arg_map: SpanMap<'db>, loc: MacroCallLoc, } -impl ExpansionInfo { +impl<'db> ExpansionInfo<'db> { pub fn expanded(&self) -> InMacroFile<SyntaxNode> { self.expanded.clone() } @@ -872,7 +866,7 @@ impl ExpansionInfo { offset: TextSize, ) -> (FileRange, SyntaxContext) { debug_assert!(self.expanded.value.text_range().contains(offset)); - span_for_offset(db, &self.exp_map, offset) + span_for_offset(db, self.exp_map, offset) } /// Maps up the text range out of the expansion hierarchy back into the original file its from. @@ -882,7 +876,7 @@ impl ExpansionInfo { range: TextRange, ) -> Option<(FileRange, SyntaxContext)> { debug_assert!(self.expanded.value.text_range().contains_range(range)); - map_node_range_up(db, &self.exp_map, range) + map_node_range_up(db, self.exp_map, range) } /// Maps up the text range out of the expansion into its macro call. @@ -918,14 +912,14 @@ impl ExpansionInfo { } } - pub fn new(db: &dyn ExpandDatabase, macro_file: MacroCallId) -> ExpansionInfo { + pub fn new(db: &'db dyn ExpandDatabase, macro_file: MacroCallId) -> ExpansionInfo<'db> { let _p = tracing::info_span!("ExpansionInfo::new").entered(); - let loc = db.lookup_intern_macro_call(macro_file); + let loc = macro_file.loc(db); let arg_tt = loc.kind.arg(db); let arg_map = db.span_map(arg_tt.file_id); - let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; + let (parse, exp_map) = &db.parse_macro_expansion(macro_file).value; let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_map } @@ -1054,6 +1048,11 @@ impl ExpandTo { intern::impl_internable!(ModPath); +/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the +/// reason why we use salsa at all. +/// +/// We encode macro definitions into ids of macro calls, this what allows us +/// to be incremental. #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[doc(alias = "MacroFileId")] pub struct MacroCallId { diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index 78228cf82e..9142ad8b92 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -332,7 +332,7 @@ fn convert_path( { let syn_ctx = span_for_range(segment.syntax().text_range()); if let Some(macro_call_id) = syn_ctx.outer_expn(db) - && db.lookup_intern_macro_call(macro_call_id.into()).def.local_inner + && crate::MacroCallId::from(macro_call_id).loc(db).def.local_inner { mod_path.kind = match resolve_crate_root(db, syn_ctx) { Some(crate_root) => PathKind::DollarCrate(crate_root), @@ -406,7 +406,7 @@ pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContext) -> O result_mark = Some(mark); } - result_mark.map(|call| db.lookup_intern_macro_call(call.into()).def.krate) + result_mark.map(|call| crate::MacroCallId::from(call).loc(db).def.krate) } pub use crate::name as __name; @@ -427,6 +427,7 @@ macro_rules! __known_path { (core::range::RangeFrom) => {}; (core::range::RangeInclusive) => {}; (core::range::RangeToInclusive) => {}; + (core::async_iter::AsyncIterator) => {}; (core::future::Future) => {}; (core::future::IntoFuture) => {}; (core::fmt::Debug) => {}; diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 0408a6943d..3ddc305f95 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -33,12 +33,14 @@ impl fmt::Debug for Name { } impl Ord for Name { + #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.symbol.as_str().cmp(other.symbol.as_str()) } } impl PartialOrd for Name { + #[inline] fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) } @@ -46,74 +48,62 @@ impl PartialOrd for Name { // No need to strip `r#`, all comparisons are done against well-known symbols. impl PartialEq<Symbol> for Name { + #[inline] fn eq(&self, sym: &Symbol) -> bool { self.symbol == *sym } } impl PartialEq<&Symbol> for Name { + #[inline] fn eq(&self, &sym: &&Symbol) -> bool { self.symbol == *sym } } impl PartialEq<Name> for Symbol { + #[inline] fn eq(&self, name: &Name) -> bool { *self == name.symbol } } impl PartialEq<Name> for &Symbol { + #[inline] fn eq(&self, name: &Name) -> bool { **self == name.symbol } } impl Name { + #[inline] fn new_text(text: &str) -> Name { Name { symbol: Symbol::intern(text), ctx: () } } + #[inline] pub fn new(text: &str, mut ctx: SyntaxContext) -> Name { // For comparisons etc. we remove the edition, because sometimes we search for some `Name` // and we don't know which edition it came from. // Can't do that for all `SyntaxContextId`s because it breaks Salsa. ctx.remove_root_edition(); _ = ctx; - match text.strip_prefix("r#") { - Some(text) => Self::new_text(text), - None => Self::new_text(text), - } + let text = text.strip_prefix("r#").unwrap_or(text); + Self::new_text(text) } + #[inline] pub fn new_root(text: &str) -> Name { // The edition doesn't matter for hygiene. Self::new(text, SyntaxContext::root(Edition::Edition2015)) } + #[inline] pub fn new_tuple_field(idx: usize) -> Name { - let symbol = match idx { - 0 => sym::INTEGER_0, - 1 => sym::INTEGER_1, - 2 => sym::INTEGER_2, - 3 => sym::INTEGER_3, - 4 => sym::INTEGER_4, - 5 => sym::INTEGER_5, - 6 => sym::INTEGER_6, - 7 => sym::INTEGER_7, - 8 => sym::INTEGER_8, - 9 => sym::INTEGER_9, - 10 => sym::INTEGER_10, - 11 => sym::INTEGER_11, - 12 => sym::INTEGER_12, - 13 => sym::INTEGER_13, - 14 => sym::INTEGER_14, - 15 => sym::INTEGER_15, - _ => Symbol::intern(&idx.to_string()), - }; - Name { symbol, ctx: () } + Name::new_symbol_root(sym::Integer::get(idx)) } + #[inline] pub fn new_lifetime(lt: &str) -> Name { match lt.strip_prefix("'r#") { Some(lt) => Self::new_text(&format_smolstr!("'{lt}")), @@ -121,6 +111,7 @@ impl Name { } } + #[inline] pub fn new_symbol(symbol: Symbol, ctx: SyntaxContext) -> Self { debug_assert!(!symbol.as_str().starts_with("r#")); _ = ctx; @@ -128,6 +119,7 @@ impl Name { } // FIXME: This needs to go once we have hygiene + #[inline] pub fn new_symbol_root(sym: Symbol) -> Self { Self::new_symbol(sym, SyntaxContext::root(Edition::Edition2015)) } @@ -141,6 +133,7 @@ impl Name { /// Ideally, we want a `gensym` semantics for missing names -- each missing /// name is equal only to itself. It's not clear how to implement this in /// salsa though, so we punt on that bit for a moment. + #[inline] pub const fn missing() -> Name { Name { symbol: sym::MISSING_NAME, ctx: () } } @@ -150,23 +143,27 @@ impl Name { /// /// Use this method instead of comparing with `Self::missing()` as missing names /// (ideally should) have a `gensym` semantics. + #[inline] pub fn is_missing(&self) -> bool { - self == &Name::missing() + self.symbol == sym::MISSING_NAME } /// Generates a new name that attempts to be unique. Should only be used when body lowering and /// creating desugared locals and labels. The caller is responsible for picking an index /// that is stable across re-executions + #[inline] pub fn generate_new_name(idx: usize) -> Name { - Name::new_text(&format!("<ra@gennew>{idx}")) + Name::new_symbol_root(sym::RaGeneratedName::get(idx)) } /// Returns the tuple index this name represents if it is a tuple field. + #[inline] pub fn as_tuple_index(&self) -> Option<usize> { - self.symbol.as_str().parse().ok() + sym::Integer::as_uint(&self.symbol) } /// Whether this name needs to be escaped in the given edition via `r#`. + #[inline] pub fn needs_escape(&self, edition: Edition) -> bool { is_raw_identifier(self.symbol.as_str(), edition) } @@ -175,10 +172,12 @@ impl Name { /// /// Do not use this for user-facing text, use `display` instead to handle editions properly. // FIXME: This should take a database argument to hide the interning + #[inline] pub fn as_str(&self) -> &str { self.symbol.as_str() } + #[inline] pub fn display<'a>( &'a self, db: &dyn crate::db::ExpandDatabase, @@ -190,14 +189,17 @@ impl Name { // FIXME: Remove this in favor of `display`, see fixme on `as_str` #[doc(hidden)] + #[inline] pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ { Display { name: self, edition } } + #[inline] pub fn symbol(&self) -> &Symbol { &self.symbol } + #[inline] pub fn is_generated(&self) -> bool { self.as_str().starts_with("<ra@gennew>") } diff --git a/crates/hir-expand/src/prettify_macro_expansion_.rs b/crates/hir-expand/src/prettify_macro_expansion_.rs index 6431d46d39..79e6f0f5b7 100644 --- a/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -29,7 +29,7 @@ pub fn prettify_macro_expansion( let macro_call_id = ctx .outer_expn(db) .expect("`$crate` cannot come from `SyntaxContextId::ROOT`"); - let macro_call = db.lookup_intern_macro_call(macro_call_id.into()); + let macro_call = crate::MacroCallId::from(macro_call_id).loc(db); let macro_def_crate = macro_call.def.krate; // First, if this is the same crate as the macro, nothing will work but `crate`. // If not, if the target trait has the macro's crate as a dependency, using the dependency name diff --git a/crates/hir-expand/src/span_map.rs b/crates/hir-expand/src/span_map.rs index aa8603341b..6a94df8b5a 100644 --- a/crates/hir-expand/src/span_map.rs +++ b/crates/hir-expand/src/span_map.rs @@ -2,7 +2,6 @@ use span::Span; use syntax::{AstNode, TextRange, ast}; -use triomphe::Arc; pub use span::RealSpanMap; @@ -11,35 +10,21 @@ use crate::{HirFileId, MacroCallId, db::ExpandDatabase}; pub type ExpansionSpanMap = span::SpanMap; /// Spanmap for a macro file or a real file -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum SpanMap { +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SpanMap<'db> { /// Spanmap for a macro file - ExpansionSpanMap(Arc<ExpansionSpanMap>), + ExpansionSpanMap(&'db ExpansionSpanMap), /// Spanmap for a real file - RealSpanMap(Arc<RealSpanMap>), + RealSpanMap(&'db RealSpanMap), } -#[derive(Copy, Clone)] -pub enum SpanMapRef<'a> { - /// Spanmap for a macro file - ExpansionSpanMap(&'a ExpansionSpanMap), - /// Spanmap for a real file - RealSpanMap(&'a RealSpanMap), -} - -impl syntax_bridge::SpanMapper for SpanMap { - fn span_for(&self, range: TextRange) -> Span { - self.span_for_range(range) - } -} - -impl syntax_bridge::SpanMapper for SpanMapRef<'_> { +impl syntax_bridge::SpanMapper for SpanMap<'_> { fn span_for(&self, range: TextRange) -> Span { self.span_for_range(range) } } -impl SpanMap { +impl<'db> SpanMap<'db> { pub fn span_for_range(&self, range: TextRange) -> Span { match self { // FIXME: Is it correct for us to only take the span at the start? This feels somewhat @@ -51,37 +36,22 @@ impl SpanMap { } } - pub fn as_ref(&self) -> SpanMapRef<'_> { - match self { - Self::ExpansionSpanMap(span_map) => SpanMapRef::ExpansionSpanMap(span_map), - Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map), - } - } - #[inline] - pub(crate) fn new(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap { + pub(crate) fn new(db: &'db dyn ExpandDatabase, file_id: HirFileId) -> SpanMap<'db> { match file_id { HirFileId::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)), HirFileId::MacroFile(m) => { - SpanMap::ExpansionSpanMap(db.parse_macro_expansion(m).value.1) + SpanMap::ExpansionSpanMap(&db.parse_macro_expansion(m).value.1) } } } } -impl SpanMapRef<'_> { - pub fn span_for_range(self, range: TextRange) -> Span { - match self { - Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()), - Self::RealSpanMap(span_map) => span_map.span_for_range(range), - } - } -} - +#[salsa_macros::tracked(returns(ref))] pub(crate) fn real_span_map( db: &dyn ExpandDatabase, editioned_file_id: base_db::EditionedFileId, -) -> Arc<RealSpanMap> { +) -> RealSpanMap { use syntax::ast::HasModuleItem; let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)]; let ast_id_map = db.ast_id_map(editioned_file_id.into()); @@ -134,16 +104,16 @@ pub(crate) fn real_span_map( _ => (), }); - Arc::new(RealSpanMap::from_file( + RealSpanMap::from_file( editioned_file_id.span_file_id(db), pairs.into_boxed_slice(), tree.syntax().text_range().end(), - )) + ) } pub(crate) fn expansion_span_map( db: &dyn ExpandDatabase, file_id: MacroCallId, -) -> Arc<ExpansionSpanMap> { - db.parse_macro_expansion(file_id).value.1 +) -> &ExpansionSpanMap { + &db.parse_macro_expansion(file_id).value.1 } diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index 18426f3095..e8eda74e40 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -33,6 +33,7 @@ query-group.workspace = true salsa.workspace = true salsa-macros.workspace = true petgraph.workspace = true +bitflags.workspace = true ra-ap-rustc_abi.workspace = true ra-ap-rustc_index.workspace = true diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs index abab3bfb25..a8ed4126ab 100644 --- a/crates/hir-ty/src/autoderef.rs +++ b/crates/hir-ty/src/autoderef.rs @@ -10,7 +10,7 @@ use rustc_type_ir::inherent::{IntoKind, Ty as _}; use tracing::debug; use crate::{ - ParamEnvAndCrate, + ParamEnvAndCrate, Span, db::HirDatabase, infer::InferenceContext, next_solver::{ @@ -39,8 +39,8 @@ pub fn autoderef<'db>( ) -> impl Iterator<Item = Ty<'db>> + use<'db> { let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let (ty, _) = infcx.instantiate_canonical(&ty); - let autoderef = Autoderef::new(&infcx, env.param_env, ty); + let (ty, _) = infcx.instantiate_canonical(Span::Dummy, &ty); + let autoderef = Autoderef::new(&infcx, env.param_env, ty, Span::Dummy); let mut v = Vec::new(); for (ty, _steps) in autoderef { // `ty` may contain unresolved inference variables. Since there's no chance they would be @@ -155,6 +155,7 @@ pub(crate) struct GeneralAutoderef<'db, Ctx, Steps = Vec<(Ty<'db>, AutoderefKind // Configurations: include_raw_pointers: bool, use_receiver_trait: bool, + span: Span, } pub(crate) type Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> = @@ -200,7 +201,7 @@ where // autoderef expect this type to have been structurally normalized. if let TyKind::Alias(..) = ty.kind() { let (normalized_ty, obligations) = - structurally_normalize_ty(self.infcx(), self.param_env(), ty)?; + structurally_normalize_ty(self.infcx(), self.param_env(), ty, self.span)?; self.state.obligations.extend(obligations); (AutoderefKind::Builtin, normalized_ty) } else { @@ -232,8 +233,9 @@ impl<'a, 'db> Autoderef<'a, 'db> { infcx: &'a InferCtxt<'db>, param_env: ParamEnv<'db>, base_ty: Ty<'db>, + span: Span, ) -> Self { - Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty) + Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty, span) } } @@ -242,8 +244,9 @@ impl<'a, 'b, 'db> InferenceContextAutoderef<'a, 'b, 'db> { pub(crate) fn new_from_inference_context( ctx: &'a mut InferenceContext<'b, 'db>, base_ty: Ty<'db>, + span: Span, ) -> Self { - Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty) + Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty, span) } #[inline] @@ -258,8 +261,9 @@ impl<'a, 'db> Autoderef<'a, 'db, usize> { infcx: &'a InferCtxt<'db>, param_env: ParamEnv<'db>, base_ty: Ty<'db>, + span: Span, ) -> Self { - Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty) + Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty, span) } } @@ -269,7 +273,7 @@ where Steps: TrackAutoderefSteps<'db>, { #[inline] - fn new_impl(ctx: Ctx, base_ty: Ty<'db>) -> Self { + fn new_impl(ctx: Ctx, base_ty: Ty<'db>, span: Span) -> Self { GeneralAutoderef { state: AutoderefSnapshot { steps: Steps::default(), @@ -282,6 +286,7 @@ where traits: None, include_raw_pointers: false, use_receiver_trait: false, + span, } } @@ -338,7 +343,7 @@ where let trait_ref = TraitRef::new(interner, trait_.into(), [ty]); let obligation = - Obligation::new(interner, ObligationCause::new(), self.param_env(), trait_ref); + Obligation::new(interner, ObligationCause::new(self.span), self.param_env(), trait_ref); // We detect whether the self type implements `Deref` before trying to // structurally normalize. We use `predicate_may_hold_opaque_types_jank` // to support not-yet-defined opaque types. It will succeed for `impl Deref` @@ -352,6 +357,7 @@ where self.infcx(), self.param_env(), Ty::new_projection(interner, trait_target.into(), [ty]), + self.span, )?; debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); self.state.obligations.extend(obligations); @@ -403,9 +409,11 @@ fn structurally_normalize_ty<'db>( infcx: &InferCtxt<'db>, param_env: ParamEnv<'db>, ty: Ty<'db>, + span: Span, ) -> Option<(Ty<'db>, PredicateObligations<'db>)> { let mut ocx = ObligationCtxt::new(infcx); - let Ok(normalized_ty) = ocx.structurally_normalize_ty(&ObligationCause::misc(), param_env, ty) + let Ok(normalized_ty) = + ocx.structurally_normalize_ty(&ObligationCause::new(span), param_env, ty) else { // We shouldn't have errors here in the old solver, except for // evaluate/fulfill mismatches, but that's not a reason for an ICE. diff --git a/crates/hir-ty/src/builtin_derive.rs b/crates/hir-ty/src/builtin_derive.rs index 6a9b1671e7..fe60fbc510 100644 --- a/crates/hir-ty/src/builtin_derive.rs +++ b/crates/hir-ty/src/builtin_derive.rs @@ -12,7 +12,7 @@ use hir_def::{ use itertools::Itertools; use la_arena::ArenaMap; use rustc_type_ir::{ - AliasTyKind, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, + AliasTyKind, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, inherent::{GenericArgs as _, IntoKind}, }; @@ -21,7 +21,8 @@ use crate::{ db::HirDatabase, next_solver::{ AliasTy, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, - StoredEarlyBinder, StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics, + StoredEarlyBinder, StoredTy, TraitRef, Ty, TyKind, Unnormalized, fold::fold_tys, + generics::Generics, }, }; @@ -53,7 +54,10 @@ fn trait_args(trait_: BuiltinDeriveImplTrait, self_ty: Ty<'_>) -> GenericArgs<'_ } } -pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> Generics { +pub(crate) fn generics_of<'db>( + interner: DbInterner<'db>, + id: BuiltinDeriveImplId, +) -> Generics<'db> { let db = interner.db; let loc = id.loc(db); match loc.trait_ { @@ -65,15 +69,14 @@ pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplI | BuiltinDeriveImplTrait::Ord | BuiltinDeriveImplTrait::PartialOrd | BuiltinDeriveImplTrait::Eq - | BuiltinDeriveImplTrait::PartialEq => interner.generics_of(loc.adt.into()), + | BuiltinDeriveImplTrait::PartialEq => Generics::from_generic_def(db, loc.adt.into()), BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => { - let mut generics = interner.generics_of(loc.adt.into()); let trait_id = loc .trait_ .get_id(interner.lang_items()) .expect("we don't pass the impl to the solver if we can't resolve the trait"); - generics.push_param(coerce_pointee_new_type_param(trait_id).into()); - generics + let additional_param = coerce_pointee_new_type_param(trait_id).into(); + Generics::from_generic_def_plus_one(db, loc.adt.into(), additional_param) } } } @@ -150,7 +153,7 @@ pub fn impl_trait<'db>( } #[salsa::tracked(returns(ref))] -pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates { +pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates { let loc = impl_.loc(db); let generic_params = GenericParams::of(db, loc.adt.into()); let interner = DbInterner::new_with(db, loc.module(db).krate(db)); @@ -195,6 +198,7 @@ pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> }; let duplicated_bounds = adt_predicates.explicit_predicates().iter_identity().filter_map(|pred| { + let pred = pred.skip_norm_wip(); let mentions_pointee = pred.visit_with(&mut MentionsPointee { pointee_param_idx }).is_break(); if !mentions_pointee { @@ -216,6 +220,7 @@ pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> adt_predicates .explicit_predicates() .iter_identity() + .map(Unnormalized::skip_norm_wip) .chain(duplicated_bounds) .chain(unsize_bound), ) @@ -317,6 +322,7 @@ fn simple_trait_predicates<'db>( adt_predicates .explicit_predicates() .iter_identity() + .map(Unnormalized::skip_norm_wip) .chain(extra_predicates) .chain(assoc_type_bounds), ) @@ -359,7 +365,7 @@ fn extend_assoc_type_bounds<'db>( let mut visitor = ProjectionFinder { interner, assoc_type_bounds, trait_id, trait_ }; for (_, field) in fields.iter() { - field.get().instantiate_identity().visit_with(&mut visitor); + field.get().instantiate_identity().skip_norm_wip().visit_with(&mut visitor); } } diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 80e7e05d76..e1d6cec594 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -5,41 +5,34 @@ mod tests; use base_db::Crate; use hir_def::{ - ConstId, EnumVariantId, ExpressionStoreOwnerId, GeneralConstId, GenericDefId, HasModule, - StaticId, + ConstId, EnumVariantId, ExpressionStoreOwnerId, GenericDefId, HasModule, StaticId, attrs::AttrFlags, - expr_store::{Body, ExpressionStore}, + expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{Expr, ExprId, Literal}, + resolver::{Resolver, ValueNs}, }; use hir_expand::Lookup; use rustc_abi::Size; use rustc_apfloat::Float; -use rustc_type_ir::inherent::IntoKind; +use rustc_ast_ir::Mutability; +use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _}; use stdx::never; -use triomphe::Arc; use crate::{ - LifetimeElisionKind, ParamEnvAndCrate, TyLoweringContext, - db::HirDatabase, + ParamEnvAndCrate, Span, + db::{AnonConstId, AnonConstLoc, GeneralConstId, HirDatabase}, display::DisplayTarget, - infer::InferenceContext, + generics::Generics, mir::{MirEvalError, MirLowerError, pad16}, next_solver::{ - Allocation, Const, ConstKind, Consts, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, - ScalarInt, StoredAllocation, StoredGenericArgs, Ty, TyKind, ValTreeKind, default_types, + Allocation, Const, ConstKind, Consts, DbInterner, DefaultAny, GenericArgs, ParamConst, + ScalarInt, StoredAllocation, StoredEarlyBinder, StoredGenericArgs, Ty, TyKind, + UnevaluatedConst, ValTreeKind, default_types, }, traits::StoredParamEnvAndCrate, }; -use super::mir::{interpret_mir, lower_body_to_mir}; - -pub fn unknown_const<'db>(_ty: Ty<'db>) -> Const<'db> { - Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) -} - -pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> { - unknown_const(ty).into() -} +use super::mir::interpret_mir; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ConstEvalError { @@ -82,15 +75,13 @@ impl From<MirEvalError> for ConstEvalError { } /// Interns a constant scalar with the given type -pub fn intern_const_ref<'a>( - db: &'a dyn HirDatabase, +fn intern_const_ref<'db>( + interner: DbInterner<'db>, value: &Literal, - ty: Ty<'a>, - krate: Crate, -) -> Const<'a> { - let interner = DbInterner::new_no_crate(db); - let Ok(data_layout) = db.target_data_layout(krate) else { - return Const::error(interner); + ty: Ty<'db>, +) -> Result<Const<'db>, CreateConstError<'db>> { + let Ok(data_layout) = interner.db.target_data_layout(interner.expect_crate()) else { + return Ok(Const::error(interner)); }; let valtree = match (ty.kind(), value) { (TyKind::Uint(uint), Literal::Uint(value, _)) => { @@ -138,14 +129,80 @@ pub fn intern_const_ref<'a>( } (_, Literal::CString(_)) => { // FIXME: - return Const::error(interner); + return Ok(Const::error(interner)); } _ => { never!("mismatching type for literal"); - return Const::error(interner); + let actual = literal_ty( + interner, + value, + |types| types.types.i32, + |types| types.types.u32, + |types| types.types.f64, + ); + return Err(CreateConstError::TypeMismatch { actual }); } }; - Const::new_valtree(interner, ty, valtree) + Ok(Const::new_valtree(interner, ty, valtree)) +} + +pub(crate) fn literal_ty<'db>( + interner: DbInterner<'db>, + value: &Literal, + default_int: impl FnOnce(&DefaultAny<'db>) -> Ty<'db>, + default_uint: impl FnOnce(&DefaultAny<'db>) -> Ty<'db>, + default_float: impl FnOnce(&DefaultAny<'db>) -> Ty<'db>, +) -> Ty<'db> { + let types = interner.default_types(); + match value { + Literal::Bool(..) => types.types.bool, + Literal::String(..) => types.types.static_str_ref, + Literal::ByteString(bs) => { + let byte_type = types.types.u8; + let array_type = Ty::new_array(interner, byte_type, bs.len() as u128); + Ty::new_ref(interner, types.regions.statik, array_type, Mutability::Not) + } + Literal::CString(..) => Ty::new_ref( + interner, + types.regions.statik, + interner.lang_items().CStr.map_or(types.types.error, |strukt| { + Ty::new_adt(interner, strukt.into(), types.empty.generic_args) + }), + Mutability::Not, + ), + Literal::Char(..) => types.types.char, + Literal::Int(_v, ty) => match ty { + Some(int_ty) => match int_ty { + hir_def::builtin_type::BuiltinInt::Isize => types.types.isize, + hir_def::builtin_type::BuiltinInt::I8 => types.types.i8, + hir_def::builtin_type::BuiltinInt::I16 => types.types.i16, + hir_def::builtin_type::BuiltinInt::I32 => types.types.i32, + hir_def::builtin_type::BuiltinInt::I64 => types.types.i64, + hir_def::builtin_type::BuiltinInt::I128 => types.types.i128, + }, + None => default_int(types), + }, + Literal::Uint(_v, ty) => match ty { + Some(int_ty) => match int_ty { + hir_def::builtin_type::BuiltinUint::Usize => types.types.usize, + hir_def::builtin_type::BuiltinUint::U8 => types.types.u8, + hir_def::builtin_type::BuiltinUint::U16 => types.types.u16, + hir_def::builtin_type::BuiltinUint::U32 => types.types.u32, + hir_def::builtin_type::BuiltinUint::U64 => types.types.u64, + hir_def::builtin_type::BuiltinUint::U128 => types.types.u128, + }, + None => default_uint(types), + }, + Literal::Float(_v, ty) => match ty { + Some(float_ty) => match float_ty { + hir_def::builtin_type::BuiltinFloat::F16 => types.types.f16, + hir_def::builtin_type::BuiltinFloat::F32 => types.types.f32, + hir_def::builtin_type::BuiltinFloat::F64 => types.types.f64, + hir_def::builtin_type::BuiltinFloat::F128 => types.types.f128, + }, + None => default_float(types), + }, + } } /// Interns a possibly-unknown target usize @@ -185,7 +242,11 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u let ec = db.const_eval_static(id).ok()?; Some(allocation_as_usize(ec)) } - GeneralConstId::AnonConstId(_) => None, + GeneralConstId::AnonConstId(id) => { + let subst = unevaluated_const.args; + let ec = db.anon_const_eval(id, subst, None).ok()?; + Some(allocation_as_usize(ec)) + } }, ConstKind::Value(val) => { if val.ty == default_types(db).types.usize { @@ -203,8 +264,8 @@ pub fn allocation_as_isize(ec: Allocation<'_>) -> i128 { i128::from_le_bytes(pad16(&ec.memory, true)) } -pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<i128> { - match (*c).kind() { +pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<i128> { + match c.kind() { ConstKind::Param(_) => None, ConstKind::Infer(_) => None, ConstKind::Bound(_, _) => None, @@ -219,7 +280,11 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< let ec = db.const_eval_static(id).ok()?; Some(allocation_as_isize(ec)) } - GeneralConstId::AnonConstId(_) => None, + GeneralConstId::AnonConstId(id) => { + let subst = unevaluated_const.args; + let ec = db.anon_const_eval(id, subst, None).ok()?; + Some(allocation_as_isize(ec)) + } }, ConstKind::Value(val) => { if val.ty == default_types(db).types.isize { @@ -233,6 +298,99 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< } } +#[derive(Debug)] +pub(crate) enum CreateConstError<'db> { + UsedForbiddenParam, + ResolveToNonConst, + DoesNotResolve, + UnderscoreExpr, + TypeMismatch { + #[expect(unused, reason = "will need this for diagnostics")] + actual: Ty<'db>, + }, +} + +pub(crate) fn path_to_const<'a, 'db>( + db: &'db dyn HirDatabase, + resolver: &Resolver<'db>, + generics: &dyn Fn() -> &'a Generics<'db>, + forbid_params_after: Option<u32>, + path: &Path, +) -> Result<Const<'db>, CreateConstError<'db>> { + let interner = DbInterner::new_no_crate(db); + let resolution = resolver + .resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) + .ok_or(CreateConstError::DoesNotResolve)?; + let konst = match resolution { + ValueNs::ConstId(id) => GeneralConstId::ConstId(id), + ValueNs::StaticId(id) => GeneralConstId::StaticId(id), + ValueNs::GenericParam(param) => { + let index = generics().type_or_const_param_idx(param.into()); + if forbid_params_after.is_some_and(|forbid_after| index >= forbid_after) { + return Err(CreateConstError::UsedForbiddenParam); + } + return Ok(Const::new_param(interner, ParamConst { id: param, index })); + } + // These are not valid as consts. + // FIXME: Report an error? + ValueNs::ImplSelf(_) + | ValueNs::LocalBinding(_) + | ValueNs::FunctionId(_) + | ValueNs::StructId(_) + | ValueNs::EnumVariantId(_) => return Err(CreateConstError::ResolveToNonConst), + }; + let args = GenericArgs::empty(interner); + Ok(Const::new_unevaluated(interner, UnevaluatedConst { def: konst.into(), args })) +} + +pub(crate) fn create_anon_const<'a, 'db>( + interner: DbInterner<'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + expr: ExprId, + resolver: &Resolver<'db>, + expected_ty: Ty<'db>, + generics: &dyn Fn() -> &'a Generics<'db>, + create_var: Option<&mut dyn FnMut(Span) -> Const<'db>>, + forbid_params_after: Option<u32>, +) -> Result<Const<'db>, CreateConstError<'db>> { + match &store[expr] { + Expr::Literal(literal) => intern_const_ref(interner, literal, expected_ty), + Expr::Underscore => match create_var { + Some(create_var) => Ok(create_var(expr.into())), + None => Err(CreateConstError::UnderscoreExpr), + }, + Expr::Path(path) + if let konst = + path_to_const(interner.db, resolver, generics, forbid_params_after, path) + && !matches!(konst, Err(CreateConstError::DoesNotResolve)) => + { + konst + } + _ => { + let allow_using_generic_params = forbid_params_after.is_none(); + let konst = AnonConstId::new( + interner.db, + AnonConstLoc { + owner, + expr, + ty: StoredEarlyBinder::bind(expected_ty.store()), + allow_using_generic_params, + }, + ); + let args = if allow_using_generic_params { + GenericArgs::identity_for_item(interner, owner.generic_def(interner.db).into()) + } else { + GenericArgs::empty(interner) + }; + Ok(Const::new_unevaluated( + interner, + UnevaluatedConst { def: GeneralConstId::AnonConstId(konst).into(), args }, + )) + } + } +} + pub(crate) fn const_eval_discriminant_variant( db: &dyn HirDatabase, variant_id: EnumVariantId, @@ -258,7 +416,7 @@ pub(crate) fn const_eval_discriminant_variant( let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); let mir_body = db.monomorphized_mir_body( - def, + def.into(), GenericArgs::empty(interner).store(), ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } .store(), @@ -268,49 +426,6 @@ pub(crate) fn const_eval_discriminant_variant( Ok(c) } -// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should -// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here -// and make this function private. See the fixme comment on `InferenceContext::resolve_all`. -pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'db>) -> Const<'db> { - let infer = ctx.fixme_resolve_all_clone(); - fn has_closure(store: &ExpressionStore, expr: ExprId) -> bool { - if matches!(store[expr], Expr::Closure { .. }) { - return true; - } - let mut r = false; - store.walk_child_exprs(expr, |idx| r |= has_closure(store, idx)); - r - } - if has_closure(ctx.store, expr) { - // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. - return Const::error(ctx.interner()); - } - if let Expr::Path(p) = &ctx.store[expr] { - let mut ctx = TyLoweringContext::new( - ctx.db, - &ctx.resolver, - ctx.store, - ctx.generic_def, - LifetimeElisionKind::Infer, - ); - if let Some(c) = ctx.path_to_const(p) { - return c; - } - } - if let Some(body_owner) = ctx.owner.as_def_with_body() - && let Ok(mir_body) = - lower_body_to_mir(ctx.db, body_owner, Body::of(ctx.db, body_owner), &infer, expr) - && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None) - { - return Const::new_from_allocation( - ctx.interner(), - &result, - ParamEnvAndCrate { param_env: ctx.table.param_env, krate: ctx.resolver.krate() }, - ); - } - Const::error(ctx.interner()) -} - pub(crate) fn const_eval_discriminant_cycle_result( _: &dyn HirDatabase, _: salsa::Id, @@ -331,8 +446,8 @@ pub(crate) fn const_eval<'db>( }; #[salsa::tracked(returns(ref), cycle_result = const_eval_cycle_result)] - pub(crate) fn const_eval_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn const_eval_query( + db: &dyn HirDatabase, def: ConstId, subst: StoredGenericArgs, trait_env: Option<StoredParamEnvAndCrate>, @@ -362,6 +477,48 @@ pub(crate) fn const_eval<'db>( } } +pub(crate) fn anon_const_eval<'db>( + db: &'db dyn HirDatabase, + def: AnonConstId, + subst: GenericArgs<'db>, + trait_env: Option<ParamEnvAndCrate<'db>>, +) -> Result<Allocation<'db>, ConstEvalError> { + return match anon_const_eval_query(db, def, subst.store(), trait_env.map(|env| env.store())) { + Ok(konst) => Ok(konst.as_ref()), + Err(err) => Err(err.clone()), + }; + + #[salsa::tracked(returns(ref), cycle_result = anon_const_eval_cycle_result)] + pub(crate) fn anon_const_eval_query( + db: &dyn HirDatabase, + def: AnonConstId, + subst: StoredGenericArgs, + trait_env: Option<StoredParamEnvAndCrate>, + ) -> Result<StoredAllocation, ConstEvalError> { + let body = db.monomorphized_mir_body( + def.into(), + subst, + ParamEnvAndCrate { + param_env: db.trait_environment(def.loc(db).owner), + krate: def.krate(db), + } + .store(), + )?; + let c = interpret_mir(db, body, false, trait_env.as_ref().map(|env| env.as_ref()))?.0?; + Ok(c.store()) + } + + pub(crate) fn anon_const_eval_cycle_result( + _: &dyn HirDatabase, + _: salsa::Id, + _: AnonConstId, + _: StoredGenericArgs, + _: Option<StoredParamEnvAndCrate>, + ) -> Result<StoredAllocation, ConstEvalError> { + Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) + } +} + pub(crate) fn const_eval_static<'db>( db: &'db dyn HirDatabase, def: StaticId, @@ -372,8 +529,8 @@ pub(crate) fn const_eval_static<'db>( }; #[salsa::tracked(returns(ref), cycle_result = const_eval_static_cycle_result)] - pub(crate) fn const_eval_static_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn const_eval_static_query( + db: &dyn HirDatabase, def: StaticId, ) -> Result<StoredAllocation, ConstEvalError> { let interner = DbInterner::new_no_crate(db); diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 723fa0fc68..f158661069 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -2474,8 +2474,6 @@ fn extern_weak_statics() { } #[test] -// FIXME -#[should_panic] fn from_ne_bytes() { check_number( r#" @@ -2567,9 +2565,8 @@ fn const_transfer_memory() { fn anonymous_const_block() { check_number( r#" - extern "rust-intrinsic" { - pub fn size_of<T>() -> usize; - } + #[rustc_intrinsic] + pub fn size_of<T>() -> usize; const fn f<T>() -> usize { let r = const { size_of::<T>() }; diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index 10282d2116..85e917fe1a 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -204,12 +204,11 @@ fn const_eval_select() { check_number( r#" //- minicore: fn - extern "rust-intrinsic" { - pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET - where - G: FnOnce<ARG, Output = RET>, - F: FnOnce<ARG, Output = RET>; - } + #[rustc_intrinsic] + pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET + where + G: FnOnce<ARG, Output = RET>, + F: FnOnce<ARG, Output = RET>; const fn in_const(x: i32, y: i32) -> i32 { x + y @@ -229,9 +228,8 @@ fn const_eval_select() { fn wrapping_add() { check_number( r#" - extern "rust-intrinsic" { - pub fn wrapping_add<T>(a: T, b: T) -> T; - } + #[rustc_intrinsic] + pub fn wrapping_add<T>(a: T, b: T) -> T; const GOAL: u8 = wrapping_add(10, 250); "#, @@ -244,10 +242,10 @@ fn ptr_offset_from() { check_number( r#" //- minicore: index, slice, coerce_unsized - extern "rust-intrinsic" { - pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize; - pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize; - } + #[rustc_intrinsic] + pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize; + #[rustc_intrinsic] + pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize; const GOAL: isize = { let x = [1, 2, 3, 4, 5i32]; @@ -265,9 +263,8 @@ fn ptr_offset_from() { fn saturating() { check_number( r#" - extern "rust-intrinsic" { - pub fn saturating_add<T>(a: T, b: T) -> T; - } + #[rustc_intrinsic] + pub fn saturating_add<T>(a: T, b: T) -> T; const GOAL: u8 = saturating_add(10, 250); "#, @@ -275,9 +272,8 @@ fn saturating() { ); check_number( r#" - extern "rust-intrinsic" { - pub fn saturating_sub<T>(a: T, b: T) -> T; - } + #[rustc_intrinsic] + pub fn saturating_sub<T>(a: T, b: T) -> T; const GOAL: bool = saturating_sub(5u8, 7) == 0 && saturating_sub(8u8, 4) == 4; "#, @@ -285,9 +281,8 @@ fn saturating() { ); check_number( r#" - extern "rust-intrinsic" { - pub fn saturating_add<T>(a: T, b: T) -> T; - } + #[rustc_intrinsic] + pub fn saturating_add<T>(a: T, b: T) -> T; const GOAL: i8 = saturating_add(5, 8); "#, @@ -330,9 +325,8 @@ fn allocator() { fn overflowing_add() { check_number( r#" - extern "rust-intrinsic" { - pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool); - } + #[rustc_intrinsic] + pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool); const GOAL: u8 = add_with_overflow(1, 2).0; "#, @@ -340,9 +334,8 @@ fn overflowing_add() { ); check_number( r#" - extern "rust-intrinsic" { - pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool); - } + #[rustc_intrinsic] + pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool); const GOAL: u8 = add_with_overflow(1, 2).1 as u8; "#, @@ -357,9 +350,8 @@ fn needs_drop() { //- minicore: drop, manually_drop, copy, sized, phantom_data use core::mem::ManuallyDrop; use core::marker::PhantomData; - extern "rust-intrinsic" { - pub fn needs_drop<T: ?Sized>() -> bool; - } + #[rustc_intrinsic] + pub fn needs_drop<T: ?Sized>() -> bool; struct X; struct NeedsDrop; impl Drop for NeedsDrop { @@ -405,9 +397,8 @@ fn discriminant_value() { r#" //- minicore: discriminant, option use core::marker::DiscriminantKind; - extern "rust-intrinsic" { - pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant; - } + #[rustc_intrinsic] + pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant; const GOAL: bool = { discriminant_value(&Some(2i32)) == discriminant_value(&Some(5i32)) && discriminant_value(&Some(2i32)) != discriminant_value(&None::<i32>) @@ -442,11 +433,12 @@ fn floating_point() { // FIXME(#17451): Add `f16` and `f128` tests once intrinsics are added. check_number( r#" - extern "rust-intrinsic" { - pub fn sqrtf32(x: f32) -> f32; - pub fn powf32(a: f32, x: f32) -> f32; - pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; - } + #[rustc_intrinsic] + pub fn sqrtf32(x: f32) -> f32; + #[rustc_intrinsic] + pub fn powf32(a: f32, x: f32) -> f32; + #[rustc_intrinsic] + pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; const GOAL: f32 = sqrtf32(1.2) + powf32(3.4, 5.6) + fmaf32(-7.8, 1.3, 2.4); "#, @@ -458,11 +450,12 @@ fn floating_point() { #[allow(unknown_lints, clippy::unnecessary_min_or_max)] check_number( r#" - extern "rust-intrinsic" { - pub fn powif64(a: f64, x: i32) -> f64; - pub fn sinf64(x: f64) -> f64; - pub fn minnumf64(x: f64, y: f64) -> f64; - } + #[rustc_intrinsic] + pub fn powif64(a: f64, x: i32) -> f64; + #[rustc_intrinsic] + pub fn sinf64(x: f64) -> f64; + #[rustc_intrinsic] + pub fn minnumf64(x: f64, y: f64) -> f64; const GOAL: f64 = powif64(1.2, 5) + sinf64(3.4) + minnumf64(-7.8, 1.3); "#, @@ -478,21 +471,32 @@ fn atomic() { check_number( r#" //- minicore: copy - extern "rust-intrinsic" { - pub fn atomic_load_seqcst<T: Copy>(src: *const T) -> T; - pub fn atomic_xchg_acquire<T: Copy>(dst: *mut T, src: T) -> T; - pub fn atomic_cxchg_release_seqcst<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool); - pub fn atomic_cxchgweak_acquire_acquire<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool); - pub fn atomic_store_release<T: Copy>(dst: *mut T, val: T); - pub fn atomic_xadd_acqrel<T: Copy>(dst: *mut T, src: T) -> T; - pub fn atomic_xsub_seqcst<T: Copy>(dst: *mut T, src: T) -> T; - pub fn atomic_and_acquire<T: Copy>(dst: *mut T, src: T) -> T; - pub fn atomic_nand_seqcst<T: Copy>(dst: *mut T, src: T) -> T; - pub fn atomic_or_release<T: Copy>(dst: *mut T, src: T) -> T; - pub fn atomic_xor_seqcst<T: Copy>(dst: *mut T, src: T) -> T; - pub fn atomic_fence_seqcst(); - pub fn atomic_singlethreadfence_acqrel(); - } + #[rustc_intrinsic] + pub fn atomic_load_seqcst<T: Copy>(src: *const T) -> T; + #[rustc_intrinsic] + pub fn atomic_xchg_acquire<T: Copy>(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_cxchg_release_seqcst<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool); + #[rustc_intrinsic] + pub fn atomic_cxchgweak_acquire_acquire<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool); + #[rustc_intrinsic] + pub fn atomic_store_release<T: Copy>(dst: *mut T, val: T); + #[rustc_intrinsic] + pub fn atomic_xadd_acqrel<T: Copy>(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_xsub_seqcst<T: Copy>(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_and_acquire<T: Copy>(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_nand_seqcst<T: Copy>(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_or_release<T: Copy>(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_xor_seqcst<T: Copy>(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_fence_seqcst(); + #[rustc_intrinsic] + pub fn atomic_singlethreadfence_acqrel(); fn should_not_reach() { _ // fails the test if executed @@ -528,10 +532,10 @@ fn offset() { check_number( r#" //- minicore: coerce_unsized, index, slice - extern "rust-intrinsic" { - pub fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr; - pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; - } + #[rustc_intrinsic] + pub fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr; + #[rustc_intrinsic] + pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; const GOAL: i32 = unsafe { let ar: &[(i32, i32, i32)] = &[ @@ -557,9 +561,8 @@ fn arith_offset() { check_number( r#" //- minicore: coerce_unsized, index, slice - extern "rust-intrinsic" { - pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; - } + #[rustc_intrinsic] + pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; const GOAL: u8 = unsafe { let ar: &[(u8, u8, u8)] = &[ @@ -583,9 +586,8 @@ fn arith_offset() { fn copy_nonoverlapping() { check_number( r#" - extern "rust-intrinsic" { - pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize); - } + #[rustc_intrinsic] + pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize); const GOAL: u8 = unsafe { let mut x = 2; @@ -602,9 +604,8 @@ fn copy_nonoverlapping() { fn write_bytes() { check_number( r#" - extern "rust-intrinsic" { - fn write_bytes<T>(dst: *mut T, val: u8, count: usize); - } + #[rustc_intrinsic] + fn write_bytes<T>(dst: *mut T, val: u8, count: usize); const GOAL: i32 = unsafe { let mut x = 2; @@ -620,9 +621,8 @@ fn write_bytes() { fn write_via_move() { check_number( r#" - extern "rust-intrinsic" { - fn write_via_move<T>(ptr: *mut T, value: T); - } + #[rustc_intrinsic] + fn write_via_move<T>(ptr: *mut T, value: T); const GOAL: i32 = unsafe { let mut x = 2; @@ -639,9 +639,8 @@ fn copy() { check_number( r#" //- minicore: coerce_unsized, index, slice - extern "rust-intrinsic" { - pub fn copy<T>(src: *const T, dst: *mut T, count: usize); - } + #[rustc_intrinsic] + pub fn copy<T>(src: *const T, dst: *mut T, count: usize); const GOAL: i32 = unsafe { let mut x = [1i32, 2, 3, 4, 5]; @@ -659,9 +658,8 @@ fn copy() { fn ctpop() { check_number( r#" - extern "rust-intrinsic" { - pub fn ctpop<T: Copy>(x: T) -> T; - } + #[rustc_intrinsic] + pub fn ctpop<T: Copy>(x: T) -> T; const GOAL: i64 = ctpop(-29); "#, @@ -673,9 +671,8 @@ fn ctpop() { fn ctlz() { check_number( r#" - extern "rust-intrinsic" { - pub fn ctlz<T: Copy>(x: T) -> T; - } + #[rustc_intrinsic] + pub fn ctlz<T: Copy>(x: T) -> T; const GOAL: u8 = ctlz(0b0001_1100_u8); "#, @@ -687,9 +684,8 @@ fn ctlz() { fn cttz() { check_number( r#" - extern "rust-intrinsic" { - pub fn cttz<T: Copy>(x: T) -> T; - } + #[rustc_intrinsic] + pub fn cttz<T: Copy>(x: T) -> T; const GOAL: i64 = cttz(-24); "#, @@ -701,9 +697,8 @@ fn cttz() { fn rotate() { check_number( r#" - extern "rust-intrinsic" { - pub fn rotate_left<T: Copy>(x: T, y: T) -> T; - } + #[rustc_intrinsic] + pub fn rotate_left<T: Copy>(x: T, y: T) -> T; const GOAL: i64 = rotate_left(0xaa00000000006e1i64, 12); "#, @@ -711,9 +706,8 @@ fn rotate() { ); check_number( r#" - extern "rust-intrinsic" { - pub fn rotate_right<T: Copy>(x: T, y: T) -> T; - } + #[rustc_intrinsic] + pub fn rotate_right<T: Copy>(x: T, y: T) -> T; const GOAL: i64 = rotate_right(0x6e10aa, 12); "#, @@ -721,9 +715,8 @@ fn rotate() { ); check_number( r#" - extern "rust-intrinsic" { - pub fn rotate_left<T: Copy>(x: T, y: T) -> T; - } + #[rustc_intrinsic] + pub fn rotate_left<T: Copy>(x: T, y: T) -> T; const GOAL: i8 = rotate_left(129, 2); "#, diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 3bf2d9a6a6..c24a5b943d 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -1,28 +1,38 @@ //! The home of `HirDatabase`, which is the Salsa database containing all the //! type inference-related queries. +use arrayvec::ArrayVec; use base_db::{Crate, target::TargetLoadError}; use either::Either; use hir_def::{ - AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, - ExpressionStoreOwnerId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, - StaticId, TraitId, TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod, - db::DefDatabase, expr_store::ExpressionStore, hir::ExprId, layout::TargetDataLayout, + AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, EnumVariantId, + ExpressionStoreOwnerId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, + LocalFieldId, ModuleId, StaticId, TraitId, TypeAliasId, VariantId, + builtin_derive::BuiltinDeriveImplMethod, + db::DefDatabase, + expr_store::ExpressionStore, + hir::{ClosureKind, ExprId, generics::LocalTypeOrConstParamId}, + layout::TargetDataLayout, + resolver::{HasResolver, Resolver}, + signatures::{ConstSignature, StaticSignature}, }; use la_arena::ArenaMap; -use salsa::plumbing::AsId; +use span::Edition; +use stdx::impl_from; use triomphe::Arc; use crate::{ - ImplTraitId, TyDefId, ValueTyDefId, + GenericDefaultsRef, GenericPredicates, ImplTraitId, InferBodyId, TyDefId, TyLoweringResult, + ValueTyDefId, consteval::ConstEvalError, dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, - lower::{Diagnostics, GenericDefaults}, + lower::{GenericDefaults, TypeAliasBounds}, mir::{BorrowckResult, MirBody, MirLowerError}, next_solver::{ - Allocation, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredEarlyBinder, - StoredGenericArgs, StoredTy, TraitRef, Ty, VariancesOf, + Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredClauses, + StoredEarlyBinder, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitRef, + Ty, VariancesOf, }, traits::{ParamEnvAndCrate, StoredParamEnvAndCrate}, }; @@ -33,33 +43,44 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // FXME: Collapse `mir_body_for_closure` into `mir_body` // and `monomorphized_mir_body_for_closure` into `monomorphized_mir_body` - #[salsa::invoke(crate::mir::mir_body_query)] - #[salsa::cycle(cycle_result = crate::mir::mir_body_cycle_result)] - fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>; + #[salsa::transparent] + fn mir_body(&self, def: InferBodyId) -> Result<&MirBody, MirLowerError> { + crate::mir::mir_body_query(self, def).as_ref().map_err(|err| err.clone()) + } - #[salsa::invoke(crate::mir::mir_body_for_closure_query)] - fn mir_body_for_closure(&self, def: InternedClosureId) -> Result<Arc<MirBody>, MirLowerError>; + #[salsa::transparent] + fn mir_body_for_closure(&self, def: InternedClosureId) -> Result<&MirBody, MirLowerError> { + crate::mir::mir_body_for_closure_query(self, def).as_ref().map_err(|err| err.clone()) + } - #[salsa::invoke(crate::mir::monomorphized_mir_body_query)] - #[salsa::cycle(cycle_result = crate::mir::monomorphized_mir_body_cycle_result)] + #[salsa::transparent] fn monomorphized_mir_body( &self, - def: DefWithBodyId, + def: InferBodyId, subst: StoredGenericArgs, env: StoredParamEnvAndCrate, - ) -> Result<Arc<MirBody>, MirLowerError>; + ) -> Result<&MirBody, MirLowerError> { + crate::mir::monomorphized_mir_body_query(self, def, subst, env) + .as_ref() + .map_err(|err| err.clone()) + } - #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] + #[salsa::transparent] fn monomorphized_mir_body_for_closure( &self, def: InternedClosureId, subst: StoredGenericArgs, env: StoredParamEnvAndCrate, - ) -> Result<Arc<MirBody>, MirLowerError>; + ) -> Result<&MirBody, MirLowerError> { + crate::mir::monomorphized_mir_body_for_closure_query(self, def, subst, env) + .as_ref() + .map_err(|err| err.clone()) + } - #[salsa::invoke(crate::mir::borrowck_query)] - #[salsa::lru(2024)] - fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>; + #[salsa::transparent] + fn borrowck(&self, def: InferBodyId) -> Result<&[BorrowckResult], MirLowerError> { + crate::mir::borrowck_query(self, def).as_ref().map(|it| &**it).map_err(|err| err.clone()) + } #[salsa::invoke(crate::consteval::const_eval)] #[salsa::transparent] @@ -70,6 +91,15 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { trait_env: Option<ParamEnvAndCrate<'db>>, ) -> Result<Allocation<'db>, ConstEvalError>; + #[salsa::invoke(crate::consteval::anon_const_eval)] + #[salsa::transparent] + fn anon_const_eval<'db>( + &'db self, + def: AnonConstId, + subst: GenericArgs<'db>, + trait_env: Option<ParamEnvAndCrate<'db>>, + ) -> Result<Allocation<'db>, ConstEvalError>; + #[salsa::invoke(crate::consteval::const_eval_static)] #[salsa::transparent] fn const_eval_static<'db>(&'db self, def: StaticId) -> Result<Allocation<'db>, ConstEvalError>; @@ -106,8 +136,10 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { env: StoredParamEnvAndCrate, ) -> Result<Arc<Layout>, LayoutError>; - #[salsa::invoke(crate::layout::target_data_layout_query)] - fn target_data_layout(&self, krate: Crate) -> Result<Arc<TargetDataLayout>, TargetLoadError>; + #[salsa::transparent] + fn target_data_layout(&self, krate: Crate) -> Result<&TargetDataLayout, TargetLoadError> { + crate::layout::target_data_layout_query(self, krate).as_ref().map_err(|err| err.clone()) + } #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)] fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option<DynCompatibilityViolation>; @@ -118,10 +150,10 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::type_for_type_alias_with_diagnostics)] #[salsa::transparent] - fn type_for_type_alias_with_diagnostics<'db>( - &'db self, + fn type_for_type_alias_with_diagnostics( + &self, def: TypeAliasId, - ) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics); + ) -> &TyLoweringResult<StoredEarlyBinder<StoredTy>>; /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is /// a `StructId` or `EnumVariantId` with a record constructor. @@ -129,43 +161,71 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::transparent] fn value_ty<'db>(&'db self, def: ValueTyDefId) -> Option<EarlyBinder<'db, Ty<'db>>>; + #[salsa::invoke(crate::lower::type_for_const)] + #[salsa::transparent] + fn type_for_const<'db>(&'db self, def: ConstId) -> EarlyBinder<'db, Ty<'db>>; + + #[salsa::invoke(crate::lower::type_for_const_with_diagnostics)] + #[salsa::transparent] + fn type_for_const_with_diagnostics( + &self, + def: ConstId, + ) -> &TyLoweringResult<StoredEarlyBinder<StoredTy>>; + + #[salsa::invoke(crate::lower::type_for_static)] + #[salsa::transparent] + fn type_for_static<'db>(&'db self, def: StaticId) -> EarlyBinder<'db, Ty<'db>>; + + #[salsa::invoke(crate::lower::type_for_static_with_diagnostics)] + #[salsa::transparent] + fn type_for_static_with_diagnostics( + &self, + def: StaticId, + ) -> &TyLoweringResult<StoredEarlyBinder<StoredTy>>; + #[salsa::invoke(crate::lower::impl_self_ty_with_diagnostics)] #[salsa::transparent] - fn impl_self_ty_with_diagnostics<'db>( - &'db self, + fn impl_self_ty_with_diagnostics( + &self, def: ImplId, - ) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics); + ) -> &TyLoweringResult<StoredEarlyBinder<StoredTy>>; #[salsa::invoke(crate::lower::impl_self_ty_query)] #[salsa::transparent] fn impl_self_ty<'db>(&'db self, def: ImplId) -> EarlyBinder<'db, Ty<'db>>; - #[salsa::invoke(crate::lower::const_param_ty_with_diagnostics)] + #[salsa::invoke(crate::lower::const_param_types_with_diagnostics)] + #[salsa::transparent] + fn const_param_types_with_diagnostics( + &self, + def: GenericDefId, + ) -> &TyLoweringResult<ArenaMap<LocalTypeOrConstParamId, StoredTy>>; + + #[salsa::invoke(crate::lower::const_param_types)] #[salsa::transparent] - fn const_param_ty_with_diagnostics<'db>(&'db self, def: ConstParamId) - -> (Ty<'db>, Diagnostics); + fn const_param_types(&self, def: GenericDefId) -> &ArenaMap<LocalTypeOrConstParamId, StoredTy>; - #[salsa::invoke(crate::lower::const_param_ty_query)] + #[salsa::invoke(crate::lower::const_param_ty)] #[salsa::transparent] - fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> Ty<'db>; + fn const_param_ty<'db>(&'db self, def: ConstParamId) -> Ty<'db>; #[salsa::invoke(crate::lower::impl_trait_with_diagnostics)] #[salsa::transparent] - fn impl_trait_with_diagnostics<'db>( - &'db self, + fn impl_trait_with_diagnostics( + &self, def: ImplId, - ) -> Option<(EarlyBinder<'db, TraitRef<'db>>, Diagnostics)>; + ) -> &Option<TyLoweringResult<StoredEarlyBinder<StoredTraitRef>>>; #[salsa::invoke(crate::lower::impl_trait_query)] #[salsa::transparent] fn impl_trait<'db>(&'db self, def: ImplId) -> Option<EarlyBinder<'db, TraitRef<'db>>>; - #[salsa::invoke(crate::lower::field_types_with_diagnostics_query)] + #[salsa::invoke(crate::lower::field_types_with_diagnostics)] #[salsa::transparent] fn field_types_with_diagnostics( &self, var: VariantId, - ) -> &(ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>, Diagnostics); + ) -> &TyLoweringResult<ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>>; #[salsa::invoke(crate::lower::field_types_query)] #[salsa::transparent] @@ -178,27 +238,51 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { def: CallableDefId, ) -> EarlyBinder<'db, PolyFnSig<'db>>; + #[salsa::invoke(crate::lower::callable_item_signature_with_diagnostics)] + #[salsa::transparent] + fn callable_item_signature_with_diagnostics( + &self, + def: CallableDefId, + ) -> &TyLoweringResult<StoredEarlyBinder<StoredPolyFnSig>>; + #[salsa::invoke(crate::lower::trait_environment)] #[salsa::transparent] fn trait_environment<'db>(&'db self, def: ExpressionStoreOwnerId) -> ParamEnv<'db>; - #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics_query)] - #[salsa::cycle(cycle_result = crate::lower::generic_defaults_with_diagnostics_cycle_result)] + #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics)] + #[salsa::transparent] fn generic_defaults_with_diagnostics( &self, def: GenericDefId, - ) -> (GenericDefaults, Diagnostics); + ) -> &TyLoweringResult<GenericDefaults>; /// This returns an empty list if no parameter has default. /// /// The binders of the returned defaults are only up to (not including) this parameter. - #[salsa::invoke(crate::lower::generic_defaults_query)] + #[salsa::invoke(crate::lower::generic_defaults)] + #[salsa::transparent] + fn generic_defaults(&self, def: GenericDefId) -> GenericDefaultsRef<'_>; + + #[salsa::invoke(crate::lower::type_alias_bounds_with_diagnostics)] #[salsa::transparent] - fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults; + fn type_alias_bounds_with_diagnostics( + &self, + type_alias: TypeAliasId, + ) -> &TyLoweringResult<TypeAliasBounds<StoredEarlyBinder<StoredClauses>>>; + + #[salsa::invoke(crate::lower::type_alias_bounds)] + #[salsa::transparent] + fn type_alias_bounds<'db>( + &'db self, + type_alias: TypeAliasId, + ) -> EarlyBinder<'db, &'db [Clause<'db>]>; - // Interned IDs for solver integration - #[salsa::interned] - fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId; + #[salsa::invoke(crate::lower::type_alias_self_bounds)] + #[salsa::transparent] + fn type_alias_self_bounds<'db>( + &'db self, + type_alias: TypeAliasId, + ) -> EarlyBinder<'db, &'db [Clause<'db>]>; #[salsa::invoke(crate::variance::variances_of)] #[salsa::transparent] @@ -229,8 +313,12 @@ pub struct InternedOpaqueTyId { pub loc: ImplTraitId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct InternedClosure(pub ExpressionStoreOwnerId, pub ExprId); +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InternedClosure { + pub owner: InferBodyId, + pub expr: ExprId, + pub kind: ClosureKind, +} #[salsa_macros::interned(constructor = new_impl, no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] @@ -242,8 +330,8 @@ impl InternedClosureId { #[inline] pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self { if cfg!(debug_assertions) { - let store = ExpressionStore::of(db, loc.0); - let expr = &store[loc.1]; + let store = ExpressionStore::of(db, loc.owner.expression_store_owner(db)); + let expr = &store[loc.expr]; assert!( matches!( expr, @@ -270,14 +358,14 @@ impl InternedCoroutineId { #[inline] pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self { if cfg!(debug_assertions) { - let store = ExpressionStore::of(db, loc.0); - let expr = &store[loc.1]; + let store = ExpressionStore::of(db, loc.owner.expression_store_owner(db)); + let expr = &store[loc.expr]; assert!( matches!( expr, hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine(_) - | hir_def::hir::ClosureKind::AsyncBlock { .. }, + closure_kind: hir_def::hir::ClosureKind::OldCoroutine(_) + | hir_def::hir::ClosureKind::Coroutine { .. }, .. } ), @@ -299,13 +387,13 @@ impl InternedCoroutineClosureId { #[inline] pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self { if cfg!(debug_assertions) { - let store = ExpressionStore::of(db, loc.0); - let expr = &store[loc.1]; + let store = ExpressionStore::of(db, loc.owner.expression_store_owner(db)); + let expr = &store[loc.expr]; assert!( matches!( expr, hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::AsyncClosure, + closure_kind: hir_def::hir::ClosureKind::CoroutineClosure(_), .. } ), @@ -316,3 +404,112 @@ impl InternedCoroutineClosureId { Self::new_impl(db, loc) } } + +/// An anonymous const expression that appears in a type position (e.g., array lengths, +/// const generic arguments like `{ N + 1 }`, or const param defaults). Unlike named constants, +/// these don't have their own `Body` — their expressions live in the parent's signature `ExpressionStore`. +#[derive(Debug, Hash, PartialEq, Eq, Clone)] +pub struct AnonConstLoc { + /// The owner store containing this expression. + pub owner: ExpressionStoreOwnerId, + /// The ExprId within the owner's ExpressionStore that is the root + /// of this anonymous const expression. + pub expr: ExprId, + pub ty: StoredEarlyBinder<StoredTy>, + /// Whether to allow using generic params from the owner. + /// true for array repeats, false for everything else. + pub(crate) allow_using_generic_params: bool, +} + +#[salsa_macros::interned(debug, no_lifetime, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct AnonConstId { + #[returns(ref)] + pub loc: AnonConstLoc, +} + +impl HasModule for AnonConstId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { + self.loc(db).owner.module(db) + } +} + +impl HasResolver for AnonConstId { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { + self.loc(db).owner.resolver(db) + } +} + +impl AnonConstId { + pub fn all_from_signature( + db: &dyn HirDatabase, + def: GenericDefId, + ) -> ArrayVec<&[AnonConstId], 5> { + let mut result = ArrayVec::new(); + + // Queries common to all generic defs: + result.push(db.generic_defaults_with_diagnostics(def).defined_anon_consts()); + result.push(GenericPredicates::query_with_diagnostics(db, def).defined_anon_consts()); + result.push(db.const_param_types_with_diagnostics(def).defined_anon_consts()); + + match def { + GenericDefId::ImplId(id) => { + result.push(db.impl_self_ty_with_diagnostics(id).defined_anon_consts()); + if let Some(trait_ref) = db.impl_trait_with_diagnostics(id) { + result.push(trait_ref.defined_anon_consts()); + } + } + GenericDefId::TypeAliasId(id) => { + result.push(db.type_for_type_alias_with_diagnostics(id).defined_anon_consts()); + result.push(db.type_alias_bounds_with_diagnostics(id).defined_anon_consts()); + } + GenericDefId::FunctionId(id) => result + .push(db.callable_item_signature_with_diagnostics(id.into()).defined_anon_consts()), + GenericDefId::ConstId(def) => { + result.push(db.type_for_const_with_diagnostics(def).defined_anon_consts()) + } + GenericDefId::StaticId(def) => { + result.push(db.type_for_static_with_diagnostics(def).defined_anon_consts()) + } + GenericDefId::TraitId(_) | GenericDefId::AdtId(_) => {} + } + + result + } +} + +/// A constant, which might appears as a const item, an anonymous const block in expressions +/// or patterns, or as a constant in types with const generics. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)] +pub enum GeneralConstId { + ConstId(ConstId), + StaticId(StaticId), + AnonConstId(AnonConstId), +} + +impl_from!(ConstId, StaticId, AnonConstId for GeneralConstId); + +impl GeneralConstId { + pub fn generic_def(self, db: &dyn HirDatabase) -> Option<GenericDefId> { + match self { + GeneralConstId::ConstId(it) => Some(it.into()), + GeneralConstId::StaticId(it) => Some(it.into()), + GeneralConstId::AnonConstId(it) => Some(it.loc(db).owner.generic_def(db)), + } + } + + pub fn name(self, db: &dyn DefDatabase) -> String { + match self { + GeneralConstId::StaticId(it) => { + StaticSignature::of(db, it).name.display(db, Edition::CURRENT).to_string() + } + GeneralConstId::ConstId(const_id) => { + ConstSignature::of(db, const_id).name.as_ref().map_or_else( + || "_".to_owned(), + |name| name.display(db, Edition::CURRENT).to_string(), + ) + } + GeneralConstId::AnonConstId(_) => "{const}".to_owned(), + } + } +} diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index 89d8c0e91d..76fbb66b06 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs @@ -33,7 +33,7 @@ use hir_expand::{ HirFileId, name::{AsName, Name}, }; -use intern::sym; +use rustc_abi::ExternAbi; use stdx::{always, never}; use syntax::{ AstNode, AstPtr, ToSmolStr, @@ -211,7 +211,7 @@ impl<'a> DeclValidator<'a> { // Don't run the lint on extern "[not Rust]" fn items with the // #[no_mangle] attribute. let no_mangle = AttrFlags::query(self.db, func.into()).contains(AttrFlags::NO_MANGLE); - if no_mangle && data.abi.as_ref().is_some_and(|abi| *abi != sym::Rust) { + if no_mangle && data.abi != ExternAbi::Rust { cov_mark::hit!(extern_func_no_mangle_ignored); } else { self.create_incorrect_case_diagnostic_for_item_name( diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 068118c705..760ebd27e0 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -7,7 +7,8 @@ use std::fmt; use base_db::Crate; use either::Either; use hir_def::{ - AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup, + AdtId, AssocItemId, CallableDefId, DefWithBodyId, HasModule, ItemContainerId, Lookup, + attrs::AttrFlags, lang_item::LangItems, resolver::{HasResolver, ValueNs}, }; @@ -15,7 +16,7 @@ use intern::sym; use itertools::Itertools; use rustc_hash::FxHashSet; use rustc_pattern_analysis::constructor::Constructor; -use rustc_type_ir::inherent::{AdtDef, IntoKind}; +use rustc_type_ir::inherent::IntoKind; use syntax::{ AstNode, ast::{self, UnaryOp}, @@ -33,7 +34,7 @@ use crate::{ }, display::{DisplayTarget, HirDisplay}, next_solver::{ - DbInterner, ParamEnv, Ty, TyKind, TypingMode, + CallableIdWrapper, DbInterner, ParamEnv, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, }; @@ -44,7 +45,7 @@ pub(crate) use hir_def::{ hir::{Expr, ExprId, MatchArm, Pat, PatId, RecordSpread, Statement}, }; -pub enum BodyValidationDiagnostic { +pub enum BodyValidationDiagnostic<'db> { RecordMissingFields { record: Either<ExprId, PatId>, variant: VariantId, @@ -67,14 +68,18 @@ pub enum BodyValidationDiagnostic { RemoveUnnecessaryElse { if_expr: ExprId, }, + UnusedMustUse { + expr: ExprId, + message: Option<&'db str>, + }, } -impl BodyValidationDiagnostic { +impl<'db> BodyValidationDiagnostic<'db> { pub fn collect( - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, owner: DefWithBodyId, validate_lints: bool, - ) -> Vec<BodyValidationDiagnostic> { + ) -> Vec<BodyValidationDiagnostic<'db>> { let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered(); let infer = InferenceResult::of(db, owner); let body = Body::of(db, owner); @@ -101,7 +106,7 @@ struct ExprValidator<'db> { body: &'db Body, infer: &'db InferenceResult, env: ParamEnv<'db>, - diagnostics: Vec<BodyValidationDiagnostic>, + diagnostics: Vec<BodyValidationDiagnostic<'db>>, validate_lints: bool, infcx: InferCtxt<'db>, } @@ -177,7 +182,7 @@ impl<'db> ExprValidator<'db> { } // Check that the number of arguments matches the number of parameters. - if self.infer.expr_type_mismatches().next().is_some() { + if self.infer.exprs_have_type_mismatches() { // FIXME: Due to shortcomings in the current type system implementation, only emit // this diagnostic if there are no type mismatches in the containing function. } else if let Expr::MethodCall { receiver, .. } = expr { @@ -218,9 +223,7 @@ impl<'db> ExprValidator<'db> { // 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 { - let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else { - return; - }; + let pat_ty = self.infer.type_of_pat_with_adjust(arm.pat); if pat_ty.references_non_lt_error() { return; } @@ -313,7 +316,7 @@ impl<'db> ExprValidator<'db> { value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_))) } Expr::Field { expr, .. } => match self.infer.expr_ty(*expr).kind() { - TyKind::Adt(adt, ..) if matches!(adt.def_id().0, AdtId::UnionId(_)) => false, + TyKind::Adt(adt, ..) if matches!(adt.def_id(), AdtId::UnionId(_)) => false, _ => self.is_known_valid_scrutinee(*expr), }, Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base), @@ -330,52 +333,71 @@ impl<'db> ExprValidator<'db> { let pattern_arena = Arena::new(); let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env); for stmt in &**statements { - let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else { - continue; + let diag = match *stmt { + Statement::Expr { expr: stmt_expr, has_semi: true } if self.validate_lints => { + self.check_unused_must_use(stmt_expr) + } + Statement::Let { pat, initializer, else_branch: None, .. } => { + self.check_non_exhaustive_let(&cx, &pattern_arena, pat, initializer) + } + _ => None, }; - if self.infer.type_mismatch_for_pat(pat).is_some() { - continue; - } - let Some(initializer) = initializer else { continue }; - let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue }; - if ty.references_non_lt_error() { - continue; + if let Some(diag) = diag { + self.diagnostics.push(diag); } + } + } - let mut have_errors = false; - let deconstructed_pat = self.lower_pattern(&cx, pat, &mut have_errors); + fn check_non_exhaustive_let<'a>( + &self, + cx: &MatchCheckCtx<'a, 'db>, + pattern_arena: &'a Arena<DeconstructedPat<'a, 'db>>, + pat: PatId, + initializer: Option<ExprId>, + ) -> Option<BodyValidationDiagnostic<'db>> { + if self.infer.pat_has_type_mismatch(pat) { + return None; + } + let initializer = initializer?; + let ty = self.infer.type_of_expr_with_adjust(initializer)?; + if ty.references_non_lt_error() { + return None; + } - // optimization, wildcard trivially hold - if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) { - continue; - } + let mut have_errors = false; + let deconstructed_pat = self.lower_pattern(cx, pat, &mut have_errors); - let match_arm = rustc_pattern_analysis::MatchArm { - pat: pattern_arena.alloc(deconstructed_pat), - has_guard: false, - arm_data: (), - }; - let report = match cx.compute_match_usefulness(&[match_arm], ty, None) { - Ok(v) => v, - Err(e) => { - debug!(?e, "match usefulness error"); - continue; - } - }; - let witnesses = report.non_exhaustiveness_witnesses; - if !witnesses.is_empty() { - self.diagnostics.push(BodyValidationDiagnostic::NonExhaustiveLet { - pat, - uncovered_patterns: missing_match_arms( - &cx, - ty, - witnesses, - false, - self.owner.krate(self.db()), - ), - }); + // optimization, wildcard trivially hold + if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) { + return None; + } + + let match_arm = rustc_pattern_analysis::MatchArm { + pat: pattern_arena.alloc(deconstructed_pat), + has_guard: false, + arm_data: (), + }; + let report = match cx.compute_match_usefulness(&[match_arm], ty, None) { + Ok(v) => v, + Err(e) => { + debug!(?e, "match usefulness error"); + return None; } + }; + let witnesses = report.non_exhaustiveness_witnesses; + if witnesses.is_empty() { + return None; } + Some(BodyValidationDiagnostic::NonExhaustiveLet { + pat, + uncovered_patterns: missing_match_arms( + cx, + ty, + witnesses, + false, + self.owner.krate(self.db()), + ), + }) } fn lower_pattern<'a>( @@ -393,6 +415,34 @@ impl<'db> ExprValidator<'db> { pattern } + fn check_unused_must_use(&self, expr: ExprId) -> Option<BodyValidationDiagnostic<'db>> { + let fn_def = match &self.body[expr] { + Expr::Call { callee, .. } => { + let callee_ty = self.infer.expr_ty(*callee); + if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(func)), _) = + callee_ty.kind() + { + Some(func.into()) + } else { + None + } + } + Expr::MethodCall { .. } => { + self.infer.method_resolution(expr).map(|(func, _)| func.into()) + } + _ => return None, + }; + let ty_def = self.infer.type_of_expr_with_adjust(expr).and_then(|ty| match ty.kind() { + TyKind::Adt(adt, _) => Some(adt.def_id().into()), + _ => None, + }); + let must_use_diag = |owner| { + AttrFlags::must_use_message(self.db(), owner?) + .map(|message| BodyValidationDiagnostic::UnusedMustUse { expr, message }) + }; + must_use_diag(fn_def).or_else(|| must_use_diag(ty_def)) + } + fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) { if !self.validate_lints { return; @@ -630,13 +680,13 @@ pub fn record_pattern_missing_fields( fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult) -> bool { fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) { - match infer.type_mismatch_for_pat(pat) { - Some(_) => *has_type_mismatches = true, - None if *has_type_mismatches => (), - None => { + match infer.pat_has_type_mismatch(pat) { + true => *has_type_mismatches = true, + false if *has_type_mismatches => (), + false => { let pat = &body[pat]; if let Pat::ConstBlock(expr) | Pat::Lit(expr) = *pat { - *has_type_mismatches |= infer.type_mismatch_for_expr(expr).is_some(); + *has_type_mismatches |= infer.expr_has_type_mismatch(expr); if *has_type_mismatches { return; } diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs index f559c26bf5..8356329d96 100644 --- a/crates/hir-ty/src/diagnostics/match_check.rs +++ b/crates/hir-ty/src/diagnostics/match_check.rs @@ -22,7 +22,7 @@ use span::Edition; use stdx::{always, never, variance::PhantomCovariantLifetime}; use crate::{ - InferenceResult, + ByRef, InferenceResult, db::HirDatabase, display::{HirDisplay, HirDisplayError, HirFormatter}, infer::BindingMode, @@ -121,7 +121,7 @@ impl<'a, 'db> PatCtxt<'a, 'db> { self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold( unadjusted_pat, |subpattern, ref_ty| Pat { - ty: ref_ty.as_ref(), + ty: ref_ty.source.as_ref(), kind: Box::new(PatKind::Deref { subpattern }), }, ) @@ -158,8 +158,8 @@ impl<'a, 'db> PatCtxt<'a, 'db> { ty = self.infer.binding_ty(id); let name = &self.body[id].name; match (bm, ty.kind()) { - (BindingMode::Ref(_), TyKind::Ref(_, rty, _)) => ty = rty, - (BindingMode::Ref(_), _) => { + (BindingMode(ByRef::Yes(_), _), TyKind::Ref(_, rty, _)) => ty = rty, + (BindingMode(ByRef::Yes(_), _), _) => { never!( "`ref {}` has wrong type {:?}", name.display(self.db, Edition::LATEST), diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index bc3d9bbec6..46959aaa5a 100644 --- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -4,15 +4,14 @@ use std::{cell::LazyCell, fmt}; use hir_def::{ EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId, attrs::AttrFlags, - signatures::VariantFields, + signatures::VariantFields, unstable_features::UnstableFeatures, }; -use intern::sym; use rustc_pattern_analysis::{ IndexVec, PatCx, PrivateUninhabitedField, constructor::{Constructor, ConstructorSet, VariantVisibility}, usefulness::{PlaceValidity, UsefulnessReport, compute_match_usefulness}, }; -use rustc_type_ir::inherent::{AdtDef, IntoKind}; +use rustc_type_ir::inherent::IntoKind; use smallvec::{SmallVec, smallvec}; use stdx::never; @@ -82,8 +81,7 @@ pub(crate) struct MatchCheckCtx<'a, 'db> { impl<'a, 'db> MatchCheckCtx<'a, 'db> { pub(crate) fn new(module: ModuleId, infcx: &'a InferCtxt<'db>, env: ParamEnv<'db>) -> Self { let db = infcx.interner.db; - let def_map = module.crate_def_map(db); - let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns); + let exhaustive_patterns = UnstableFeatures::query(db, module.krate(db)).exhaustive_patterns; Self { module, db, exhaustive_patterns, env, infcx } } @@ -151,7 +149,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> { let fields_len = variant.fields(self.db).fields().len() as u32; (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).map(move |fid| { - let ty = field_tys[fid].get().instantiate(self.infcx.interner, substs); + let ty = field_tys[fid].get().instantiate(self.infcx.interner, substs).skip_norm_wip(); let ty = self .infcx .at(&ObligationCause::dummy(), self.env) @@ -199,7 +197,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> { arity = substs.len(); } TyKind::Adt(adt_def, _) => { - let adt = adt_def.def_id().0; + let adt = adt_def.def_id(); ctor = match pat.kind.as_ref() { PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => { UnionField @@ -267,7 +265,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> { }, TyKind::Adt(adt, substs) => { let variant = - Self::variant_id_for_adt(self.db, pat.ctor(), adt.def_id().0).unwrap(); + Self::variant_id_for_adt(self.db, pat.ctor(), adt.def_id()).unwrap(); let subpatterns = self .list_variant_fields(*pat.ty(), variant) .zip(subpatterns) @@ -327,7 +325,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { TyKind::Tuple(tys) => tys.len(), TyKind::Adt(adt_def, ..) => { let variant = - Self::variant_id_for_adt(self.db, ctor, adt_def.def_id().0).unwrap(); + Self::variant_id_for_adt(self.db, ctor, adt_def.def_id()).unwrap(); variant.fields(self.db).fields().len() } _ => { @@ -361,7 +359,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { } TyKind::Ref(_, rty, _) => single(rty), TyKind::Adt(adt_def, ..) => { - let adt = adt_def.def_id().0; + let adt = adt_def.def_id(); let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); let visibilities = @@ -430,7 +428,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { TyKind::Int(..) | TyKind::Uint(..) => unhandled(), TyKind::Array(..) | TyKind::Slice(..) => unhandled(), TyKind::Adt(adt_def, subst) => { - let adt = adt_def.def_id().0; + let adt = adt_def.def_id(); match adt { hir_def::AdtId::EnumId(enum_id) => { let enum_data = enum_id.enum_variants(cx.db); diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index ee33f7d158..c37a194d47 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -253,12 +253,13 @@ impl<'db> UnsafeVisitor<'db> { | Pat::TupleStruct { .. } | Pat::Ref { .. } | Pat::Box { .. } + | Pat::Deref { .. } | Pat::Expr(..) | Pat::ConstBlock(..) => { self.on_unsafe_op(current.into(), UnsafetyReason::UnionField) } // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read. - Pat::Missing | Pat::Wild | Pat::Or(_) => {} + Pat::Missing | Pat::Rest | Pat::Wild | Pat::Or(_) => {} } } @@ -297,7 +298,7 @@ impl<'db> UnsafeVisitor<'db> { self.check_call(current, func); } if let TyKind::FnPtr(_, hdr) = callee.kind() - && hdr.safety == Safety::Unsafe + && hdr.safety() == Safety::Unsafe { self.on_unsafe_op(current.into(), UnsafetyReason::UnsafeFnCall); } @@ -424,7 +425,6 @@ impl<'db> UnsafeVisitor<'db> { Expr::Closure { args, .. } => { self.walk_pats_top(args.iter().copied(), current); } - Expr::Const(e) => self.walk_expr(*e), _ => {} } diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index e4a8def442..bc726b652f 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -10,17 +10,20 @@ use std::{ use base_db::{Crate, FxIndexMap}; use either::Either; use hir_def::{ - ExpressionStoreOwnerId, FindPathConfig, GenericDefId, GenericParamId, HasModule, LocalFieldId, - Lookup, ModuleDefId, ModuleId, TraitId, + ExpressionStoreOwnerId, FindPathConfig, GenericDefId, GenericParamId, HasModule, + ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, TypeAliasId, expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, - hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, + hir::{ + ClosureKind as HirClosureKind, CoroutineKind, + generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, + }, item_scope::ItemInNs, item_tree::FieldsShape, lang_item::LangItems, signatures::{ - EnumSignature, FunctionSignature, StructSignature, TraitSignature, TypeAliasSignature, - UnionSignature, VariantFields, + ConstSignature, EnumSignature, FunctionSignature, StaticSignature, StructSignature, + TraitSignature, TypeAliasSignature, UnionSignature, VariantFields, }, type_ref::{ ConstRef, LifetimeRef, LifetimeRefId, TraitBoundModifier, TypeBound, TypeRef, TypeRefId, @@ -32,6 +35,7 @@ use hir_expand::{mod_path::PathKind, name::Name}; use intern::{Internable, Interned, sym}; use itertools::Itertools; use la_arena::ArenaMap; +use rustc_abi::ExternAbi; use rustc_apfloat::{ Float, ieee::{Half as f16, Quad as f128}, @@ -40,24 +44,24 @@ use rustc_ast_ir::FloatTy; use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, RegionKind, Upcast, - inherent::{AdtDef, GenericArgs as _, IntoKind, Term as _, Ty as _, Tys as _}, + inherent::{GenericArgs as _, IntoKind, Term as _, Ty as _, Tys as _}, }; use smallvec::SmallVec; use span::Edition; use stdx::never; use crate::{ - CallableDefId, FnAbi, ImplTraitId, MemoryMap, ParamEnvAndCrate, consteval, - db::{HirDatabase, InternedClosure}, - generics::generics, + CallableDefId, ImplTraitId, MemoryMap, ParamEnvAndCrate, consteval, + db::{GeneralConstId, HirDatabase}, + generics::{ProvenanceSplit, generics}, layout::Layout, lower::GenericPredicates, mir::pad16, next_solver::{ AliasTy, Allocation, Clause, ClauseKind, Const, ConstKind, DbInterner, ExistentialPredicate, FnSig, GenericArg, GenericArgKind, GenericArgs, ParamEnv, PolyFnSig, - Region, SolverDefId, StoredEarlyBinder, StoredTy, Term, TermKind, TraitRef, Ty, TyKind, - TypingMode, ValTree, + Region, StoredEarlyBinder, StoredTy, Term, TermId, TermKind, TraitPredicate, TraitRef, Ty, + TyKind, TypingMode, Unnormalized, ValTree, abi::Safety, infer::{DbInternerInferExt, traits::ObligationCause}, }, @@ -65,6 +69,36 @@ use crate::{ utils::{detect_variant_from_bytes, fn_traits}, }; +fn async_gen_item_ty_from_yield_ty<'db>( + lang_items: &LangItems, + yield_ty: Ty<'db>, +) -> Option<Ty<'db>> { + let poll_id = lang_items.Poll.map(hir_def::AdtId::EnumId)?; + let option_id = lang_items.Option.map(hir_def::AdtId::EnumId)?; + + let TyKind::Adt(poll_def, poll_args) = yield_ty.kind() else { + return None; + }; + if poll_def.def_id() != poll_id { + return None; + } + let [poll_inner] = poll_args.as_slice() else { + return None; + }; + let poll_inner = poll_inner.ty()?; + + let TyKind::Adt(option_def, option_args) = poll_inner.kind() else { + return None; + }; + if option_def.def_id() != option_id { + return None; + } + let [item] = option_args.as_slice() else { + return None; + }; + item.ty() +} + pub type Result<T = (), E = HirDisplayError> = std::result::Result<T, E>; pub trait HirWrite: fmt::Write { @@ -101,7 +135,15 @@ pub struct HirFormatter<'a, 'db> { display_lifetimes: DisplayLifetime, display_kind: DisplayKind, display_target: DisplayTarget, - bounds_formatting_ctx: BoundsFormattingCtx<'db>, + /// We can have recursive bounds like the following case: + /// ```ignore + /// where + /// T: Foo, + /// T::FooAssoc: Baz<<T::FooAssoc as Bar>::BarAssoc> + Bar + /// ``` + /// So, record the projection types met while formatting bounds and + /// prevent recursing into their bounds to avoid infinite loops. + currently_formatting_bounds: FxHashSet<AliasTy<'db>>, /// Whether formatting `impl Trait1 + Trait2` or `dyn Trait1 + Trait2` needs parentheses around it, /// for example when formatting `&(impl Trait1 + Trait2)`. trait_bounds_need_parens: bool, @@ -121,34 +163,6 @@ pub enum DisplayLifetime { Never, } -#[derive(Default)] -enum BoundsFormattingCtx<'db> { - Entered { - /// We can have recursive bounds like the following case: - /// ```ignore - /// where - /// T: Foo, - /// T::FooAssoc: Baz<<T::FooAssoc as Bar>::BarAssoc> + Bar - /// ``` - /// So, record the projection types met while formatting bounds and - //. prevent recursing into their bounds to avoid infinite loops. - projection_tys_met: FxHashSet<AliasTy<'db>>, - }, - #[default] - Exited, -} - -impl<'db> BoundsFormattingCtx<'db> { - fn contains(&self, proj: &AliasTy<'db>) -> bool { - match self { - BoundsFormattingCtx::Entered { projection_tys_met } => { - projection_tys_met.contains(proj) - } - BoundsFormattingCtx::Exited => false, - } - } -} - impl<'db> HirFormatter<'_, 'db> { pub fn start_location_link(&mut self, location: ModuleDefId) { self.fmt.start_location_link(location); @@ -162,26 +176,42 @@ impl<'db> HirFormatter<'_, 'db> { self.fmt.end_location_link(); } - fn format_bounds_with<T, F: FnOnce(&mut Self) -> T>( + fn format_bounds_with<F: FnOnce(&mut Self) -> Result>( &mut self, target: AliasTy<'db>, format_bounds: F, - ) -> T { - match self.bounds_formatting_ctx { - BoundsFormattingCtx::Entered { ref mut projection_tys_met } => { - projection_tys_met.insert(target); - format_bounds(self) - } - BoundsFormattingCtx::Exited => { - let mut projection_tys_met = FxHashSet::default(); - projection_tys_met.insert(target); - self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met }; - let res = format_bounds(self); - // Since we want to prevent only the infinite recursions in bounds formatting - // and do not want to skip formatting of other separate bounds, clear context - // when exiting the formatting of outermost bounds - self.bounds_formatting_ctx = BoundsFormattingCtx::Exited; - res + ) -> Result { + if self.currently_formatting_bounds.insert(target) { + let result = format_bounds(self); + self.currently_formatting_bounds.remove(&target); + result + } else { + if self.display_kind.is_source_code() { + Err(HirDisplayError::DisplaySourceCodeError(DisplaySourceCodeError::Cycle)) + } else { + match target.kind { + AliasTyKind::Projection { def_id } => { + let def_id = def_id.0; + let ItemContainerId::TraitId(trait_) = def_id.loc(self.db).container else { + panic!("expected an assoc type"); + }; + let trait_name = &TraitSignature::of(self.db, trait_).name; + let assoc_type_name = &TypeAliasSignature::of(self.db, def_id).name; + write!( + self, + "<… as {}>::{}", + trait_name.display(self.db, self.edition()), + assoc_type_name.display(self.db, self.edition()), + )?; + if target.args.len() > 1 { + self.write_str("<…>")?; + } + Ok(()) + } + AliasTyKind::Inherent { .. } + | AliasTyKind::Opaque { .. } + | AliasTyKind::Free { .. } => self.write_str("…"), + } } } } @@ -335,7 +365,7 @@ pub trait HirDisplay<'db> { display_kind: DisplayKind::SourceCode { target_module_id: module_id, allow_opaque }, show_container_bounds: false, display_lifetimes: DisplayLifetime::OnlyNamedOrStatic, - bounds_formatting_ctx: Default::default(), + currently_formatting_bounds: Default::default(), trait_bounds_need_parens: false, }) { Ok(()) => {} @@ -511,6 +541,7 @@ pub enum DisplaySourceCodeError { PathNotFound, Coroutine, OpaqueType, + Cycle, } pub enum HirDisplayError { @@ -571,7 +602,7 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> { closure_style: self.closure_style, show_container_bounds: self.show_container_bounds, display_lifetimes: self.display_lifetimes, - bounds_formatting_ctx: Default::default(), + currently_formatting_bounds: Default::default(), trait_bounds_need_parens: false, }) } @@ -623,63 +654,58 @@ fn write_projection<'db>( f: &mut HirFormatter<'_, 'db>, alias: &AliasTy<'db>, needs_parens_if_multi: bool, + def_id: TypeAliasId, ) -> Result { - if f.should_truncate() { - return write!(f, "{TYPE_HINT_TRUNCATION}"); - } - let trait_ref = alias.trait_ref(f.interner); - let self_ty = trait_ref.self_ty(); - - // if we are projection on a type parameter, check if the projection target has bounds - // itself, if so, we render them directly as `impl Bound` instead of the less useful - // `<Param as Trait>::Assoc` - if !f.display_kind.is_source_code() - && let TyKind::Param(param) = self_ty.kind() - && !f.bounds_formatting_ctx.contains(alias) - { - // FIXME: We shouldn't use `param.id`, it should be removed. We should know the - // `GenericDefId` from the formatted type (store it inside the `HirFormatter`). - let bounds = GenericPredicates::query_all(f.db, param.id.parent()) - .iter_identity() - .filter(|wc| { - let ty = match wc.kind().skip_binder() { - ClauseKind::Trait(tr) => tr.self_ty(), - ClauseKind::TypeOutlives(t) => t.0, - _ => return false, - }; - let TyKind::Alias(a) = ty.kind() else { - return false; - }; - a == *alias - }) - .collect::<Vec<_>>(); - if !bounds.is_empty() { - return f.format_bounds_with(*alias, |f| { - write_bounds_like_dyn_trait_with_prefix( + f.format_bounds_with(*alias, |f| { + if f.should_truncate() { + return write!(f, "{TYPE_HINT_TRUNCATION}"); + } + let trait_ref = alias.trait_ref(f.interner); + let self_ty = trait_ref.self_ty(); + + // if we are projection on a type parameter, check if the projection target has bounds + // itself, if so, we render them directly as `impl Bound` instead of the less useful + // `<Param as Trait>::Assoc` + if !f.display_kind.is_source_code() + && let TyKind::Param(param) = self_ty.kind() + { + // FIXME: We shouldn't use `param.id`, it should be removed. We should know the + // `GenericDefId` from the formatted type (store it inside the `HirFormatter`). + let bounds = GenericPredicates::query_all(f.db, param.id.parent()) + .iter_identity() + .map(Unnormalized::skip_norm_wip) + .filter(|wc| { + let ty = match wc.kind().skip_binder() { + ClauseKind::Trait(tr) => tr.self_ty(), + ClauseKind::TypeOutlives(t) => t.0, + _ => return false, + }; + let TyKind::Alias(a) = ty.kind() else { + return false; + }; + a == *alias + }) + .collect::<Vec<_>>(); + if !bounds.is_empty() { + return write_bounds_like_dyn_trait_with_prefix( f, "impl", Either::Left(Ty::new_alias(f.interner, *alias)), &bounds, SizedByDefault::NotSized, needs_parens_if_multi, - ) - }); + ); + } } - } - write!(f, "<")?; - self_ty.hir_fmt(f)?; - write!(f, " as ")?; - trait_ref.hir_fmt(f)?; - write!( - f, - ">::{}", - TypeAliasSignature::of(f.db, alias.kind.def_id().expect_type_alias()) - .name - .display(f.db, f.edition()) - )?; - let proj_params = &alias.args.as_slice()[trait_ref.args.len()..]; - hir_fmt_generics(f, proj_params, None, None) + write!(f, "<")?; + self_ty.hir_fmt(f)?; + write!(f, " as ")?; + trait_ref.hir_fmt(f)?; + write!(f, ">::{}", TypeAliasSignature::of(f.db, def_id).name.display(f.db, f.edition()))?; + let proj_params = &alias.args.as_slice()[trait_ref.args.len()..]; + hir_fmt_generics(f, proj_params, None, None) + }) } impl<'db> HirDisplay<'db> for GenericArg<'db> { @@ -710,7 +736,7 @@ impl<'db> HirDisplay<'db> for Const<'db> { } ConstKind::Infer(..) => write!(f, "#c#"), ConstKind::Param(param) => { - let generics = generics(f.db, param.id.parent()); + let generics = GenericParams::of(f.db, param.id.parent()); let param_data = &generics[param.id.local_id()]; f.start_location_link_generic(param.id.into()); write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?; @@ -720,7 +746,25 @@ impl<'db> HirDisplay<'db> for Const<'db> { ConstKind::Value(value) => render_const_scalar_from_valtree(f, value.ty, value.value), ConstKind::Unevaluated(unev) => { let c = unev.def.0; - write!(f, "{}", c.name(f.db))?; + match c { + GeneralConstId::ConstId(id) => match &ConstSignature::of(f.db, id).name { + Some(name) => { + f.start_location_link(id.into()); + write!(f, "{}", name.display(f.db, f.edition()))?; + f.end_location_link(); + } + None => f.write_str("_")?, + }, + GeneralConstId::StaticId(id) => { + let name = &StaticSignature::of(f.db, id).name; + f.start_location_link(id.into()); + write!(f, "{}", name.display(f.db, f.edition()))?; + f.end_location_link(); + } + GeneralConstId::AnonConstId(_) => { + f.write_str(if f.display_kind.is_source_code() { "_" } else { "{const}" })? + } + }; hir_fmt_generics(f, unev.args.as_slice(), c.generic_def(f.db), None)?; Ok(()) } @@ -738,7 +782,7 @@ fn render_const_scalar<'db>( ) -> Result { let param_env = ParamEnv::empty(); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); - let ty = infcx.at(&ObligationCause::new(), param_env).deeply_normalize(ty).unwrap_or(ty); + let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_inner(f, b, memory_map, ty, param_env) } @@ -857,7 +901,7 @@ fn render_const_scalar_inner<'db>( f.write_str("&")?; render_const_scalar(f, bytes, memory_map, t) } - TyKind::Adt(adt, _) if b.len() == 2 * size_of::<usize>() => match adt.def_id().0 { + TyKind::Adt(adt, _) if b.len() == 2 * size_of::<usize>() => match adt.def_id() { hir_def::AdtId::StructId(s) => { let data = StructSignature::of(f.db, s); write!(f, "&{}", data.name.display(f.db, f.edition()))?; @@ -911,7 +955,7 @@ fn render_const_scalar_inner<'db>( f.write_str(")") } TyKind::Adt(def, args) => { - let def = def.def_id().0; + let def = def.def_id(); let Ok(layout) = f.db.layout_of_adt(def, args.store(), param_env.store()) else { return f.write_str("<layout-error>"); }; @@ -941,7 +985,7 @@ fn render_const_scalar_inner<'db>( return f.write_str("<target-layout-not-available>"); }; let Some((var_id, var_layout)) = - detect_variant_from_bytes(&layout, f.db, &target_data_layout, b, e) + detect_variant_from_bytes(&layout, f.db, target_data_layout, b, e) else { return f.write_str("<failed-to-detect-variant>"); }; @@ -1023,7 +1067,7 @@ fn render_const_scalar_from_valtree<'db>( ) -> Result { let param_env = ParamEnv::empty(); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); - let ty = infcx.at(&ObligationCause::new(), param_env).deeply_normalize(ty).unwrap_or(ty); + let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_from_valtree_inner(f, ty, valtree, param_env) } @@ -1176,7 +1220,7 @@ fn render_variant_after_name<'db>( FieldsShape::Record | FieldsShape::Tuple => { let render_field = |f: &mut HirFormatter<'_, 'db>, id: LocalFieldId| { let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize(); - let ty = field_types[id].get().instantiate(f.interner, args); + let ty = field_types[id].get().instantiate(f.interner, args).skip_norm_wip(); let Ok(layout) = f.db.layout_of_ty(ty.store(), param_env.store()) else { return f.write_str("<layout-error>"); }; @@ -1287,7 +1331,8 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } TyKind::FnDef(def, args) => { let def = def.0; - let sig = db.callable_item_signature(def).instantiate(interner, args); + let sig = + db.callable_item_signature(def).instantiate(interner, args).skip_norm_wip(); if f.display_kind.is_source_code() { // `FnDef` is anonymous and there's no surface syntax for it. Show it as a @@ -1297,7 +1342,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { if let Safety::Unsafe = sig.safety() { write!(f, "unsafe ")?; } - if !matches!(sig.abi(), FnAbi::Rust | FnAbi::RustCall) { + if !sig.abi().is_rustic_abi() { f.write_str("extern \"")?; f.write_str(sig.abi().as_str())?; f.write_str("\" ")?; @@ -1331,8 +1376,14 @@ impl<'db> HirDisplay<'db> for Ty<'db> { if !args.is_empty() { let generic_def_id = GenericDefId::from_callable(db, def); let generics = generics(db, generic_def_id); - let (parent_len, self_param, type_, const_, impl_, lifetime) = - generics.provenance_split(); + let ProvenanceSplit { + parent_total: parent_len, + has_self_param: self_param, + non_impl_trait_type_params: type_, + const_params: const_, + impl_trait_type_params: impl_, + lifetimes: lifetime, + } = generics.provenance_split(); let parameters = args.as_slice(); debug_assert_eq!( parameters.len(), @@ -1387,7 +1438,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } } TyKind::Adt(def, parameters) => { - let def_id = def.def_id().0; + let def_id = def.def_id(); f.start_location_link(def_id.into()); match f.display_kind { DisplayKind::Diagnostics | DisplayKind::Test => { @@ -1425,10 +1476,10 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } f.end_location_link(); - hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().0.into()), None)?; + hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().into()), None)?; } - TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => { - write_projection(f, &alias_ty, trait_bounds_need_parens)? + TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Projection { def_id }, .. }) => { + write_projection(f, &alias_ty, trait_bounds_need_parens, def_id.0)? } TyKind::Foreign(alias) => { let type_alias = TypeAliasSignature::of(db, alias.0); @@ -1437,19 +1488,17 @@ impl<'db> HirDisplay<'db> for Ty<'db> { f.end_location_link(); } TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Opaque { def_id }, .. }) => { - let opaque_ty_id = match def_id { - SolverDefId::InternedOpaqueTyId(id) => id, - _ => unreachable!(), - }; + let opaque_ty_id = def_id.0; if !f.display_kind.allows_opaque() { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::OpaqueType, )); } - let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id); + let impl_trait_id = opaque_ty_id.loc(db); let data = impl_trait_id.predicates(db); let bounds = data .iter_instantiated_copied(interner, alias_ty.args.as_slice()) + .map(Unnormalized::skip_norm_wip) .collect::<Vec<_>>(); let krate = match impl_trait_id { ImplTraitId::ReturnTypeImplTrait(func, _) => { @@ -1519,6 +1568,15 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } TyKind::CoroutineClosure(id, args) => { let id = id.0; + let closure_kind = match id.loc(db).kind { + HirClosureKind::CoroutineClosure(kind) => kind, + kind => panic!("invalid kind for coroutine closure: {kind:?}"), + }; + let closure_label = match closure_kind { + CoroutineKind::Async => "async closure", + CoroutineKind::Gen => "gen closure", + CoroutineKind::AsyncGen => "async gen closure", + }; if f.display_kind.is_source_code() { if !f.display_kind.allows_opaque() { return Err(HirDisplayError::DisplaySourceCodeError( @@ -1533,25 +1591,28 @@ impl<'db> HirDisplay<'db> for Ty<'db> { ClosureStyle::ClosureWithId => { return write!( f, - "{{async closure#{:?}}}", + "{{{closure_label}#{:?}}}", salsa::plumbing::AsId::as_id(&id).index() ); } ClosureStyle::ClosureWithSubst => { write!( f, - "{{async closure#{:?}}}", + "{{{closure_label}#{:?}}}", salsa::plumbing::AsId::as_id(&id).index() )?; return hir_fmt_generics(f, args.as_slice(), None, None); } _ => (), } - let kind = args.as_coroutine_closure().kind(); - let kind = match kind { - rustc_type_ir::ClosureKind::Fn => "AsyncFn", - rustc_type_ir::ClosureKind::FnMut => "AsyncFnMut", - rustc_type_ir::ClosureKind::FnOnce => "AsyncFnOnce", + let callable_kind = args.as_coroutine_closure().kind(); + let kind = match (closure_kind, callable_kind) { + (CoroutineKind::Async, rustc_type_ir::ClosureKind::Fn) => "AsyncFn", + (CoroutineKind::Async, rustc_type_ir::ClosureKind::FnMut) => "AsyncFnMut", + (CoroutineKind::Async, rustc_type_ir::ClosureKind::FnOnce) => "AsyncFnOnce", + (_, rustc_type_ir::ClosureKind::Fn) => "Fn", + (_, rustc_type_ir::ClosureKind::FnMut) => "FnMut", + (_, rustc_type_ir::ClosureKind::FnOnce) => "FnOnce", }; let coroutine_sig = args.as_coroutine_closure().coroutine_closure_sig(); let coroutine_sig = coroutine_sig.skip_binder(); @@ -1559,7 +1620,11 @@ impl<'db> HirDisplay<'db> for Ty<'db> { let coroutine_output = coroutine_sig.return_ty; match f.closure_style { ClosureStyle::ImplFn => write!(f, "impl {kind}(")?, - ClosureStyle::RANotation => write!(f, "async |")?, + ClosureStyle::RANotation => match closure_kind { + CoroutineKind::Async => write!(f, "async |")?, + CoroutineKind::Gen => write!(f, "gen |")?, + CoroutineKind::AsyncGen => write!(f, "async gen |")?, + }, _ => unreachable!(), } if coroutine_inputs.is_empty() { @@ -1582,7 +1647,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { TyKind::Param(param) => { // FIXME: We should not access `param.id`, it should be removed, and we should know the // parent from the formatted type. - let generics = generics(db, param.id.parent()); + let generics = GenericParams::of(db, param.id.parent()); let param_data = &generics[param.id.local_id()]; match param_data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { @@ -1601,6 +1666,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { TypeParamProvenance::ArgumentImplTrait => { let bounds = GenericPredicates::query_all(f.db, param.id.parent()) .iter_identity() + .map(Unnormalized::skip_norm_wip) .filter(|wc| match wc.kind().skip_binder() { ClauseKind::Trait(tr) => tr.self_ty() == *self, ClauseKind::Projection(proj) => proj.self_ty() == *self, @@ -1670,21 +1736,14 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } TyKind::Infer(..) => write!(f, "_")?, TyKind::Coroutine(coroutine_id, subst) => { - let InternedClosure(owner, expr_id) = coroutine_id.0.loc(db); + let kind = coroutine_id.0.loc(db).kind; let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } = subst.split_coroutine_args(); - let body = ExpressionStore::of(db, owner); - let expr = &body[expr_id]; - match expr { - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. }, - .. - } => { - let future_trait = f.lang_items().Future; - let output = future_trait.and_then(|t| { - t.trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::Output)) - }); + match kind { + HirClosureKind::Coroutine { kind: CoroutineKind::Async, .. } => { + let lang_items = f.lang_items(); + let future_trait = lang_items.Future; + let output = lang_items.FutureOutput; write!(f, "impl ")?; if let Some(t) = future_trait { f.start_location_link(t.into()); @@ -1705,10 +1764,57 @@ impl<'db> HirDisplay<'db> for Ty<'db> { return_ty.hir_fmt(f)?; write!(f, ">")?; } - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine(..), - .. - } => { + HirClosureKind::Coroutine { kind: CoroutineKind::Gen, .. } => { + let lang_items = f.lang_items(); + let iterator_trait = lang_items.Iterator; + let item = lang_items.IteratorItem; + write!(f, "impl ")?; + if let Some(t) = iterator_trait { + f.start_location_link(t.into()); + } + write!(f, "Iterator")?; + if iterator_trait.is_some() { + f.end_location_link(); + } + write!(f, "<")?; + if let Some(t) = item { + f.start_location_link(t.into()); + } + write!(f, "Item")?; + if item.is_some() { + f.end_location_link(); + } + write!(f, " = ")?; + yield_ty.hir_fmt(f)?; + write!(f, ">")?; + } + HirClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. } => { + let lang_items = f.lang_items(); + let async_iterator_trait = lang_items.AsyncIterator; + let item = lang_items.AsyncIteratorItem; + write!(f, "impl ")?; + if let Some(t) = async_iterator_trait { + f.start_location_link(t.into()); + } + write!(f, "AsyncIterator")?; + if async_iterator_trait.is_some() { + f.end_location_link(); + } + write!(f, "<")?; + if let Some(t) = item { + f.start_location_link(t.into()); + } + write!(f, "Item")?; + if item.is_some() { + f.end_location_link(); + } + write!(f, " = ")?; + let item_ty = async_gen_item_ty_from_yield_ty(f.lang_items(), yield_ty) + .unwrap_or(yield_ty); + item_ty.hir_fmt(f)?; + write!(f, ">")?; + } + HirClosureKind::OldCoroutine(..) => { if f.display_kind.is_source_code() { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::Coroutine, @@ -1724,7 +1830,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { write!(f, " -> ")?; return_ty.hir_fmt(f)?; } - _ => panic!("invalid expr for coroutine: {expr:?}"), + _ => panic!("invalid kind for coroutine: {kind:?}"), } } TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?, @@ -1769,7 +1875,9 @@ fn generic_args_sans_defaults<'ga, 'db>( let should_show = |arg: GenericArg<'db>, i: usize| match default_parameters.get(i) { None => true, Some(default_parameter) => { - arg != default_parameter.instantiate(f.interner, ¶meters[..i]) + arg != default_parameter + .instantiate(f.interner, ¶meters[..i]) + .skip_norm_wip() } }; let mut default_from = 0; @@ -1852,8 +1960,8 @@ fn hir_fmt_tys<'db>( impl<'db> HirDisplay<'db> for PolyFnSig<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - let FnSig { inputs_and_output, c_variadic, safety, abi: _ } = self.skip_binder(); - if let Safety::Unsafe = safety { + let FnSig { inputs_and_output, fn_sig_kind } = self.skip_binder(); + if let Safety::Unsafe = fn_sig_kind.safety() { write!(f, "unsafe ")?; } // FIXME: Enable this when the FIXME on FnAbi regarding PartialEq is fixed. @@ -1864,7 +1972,7 @@ impl<'db> HirDisplay<'db> for PolyFnSig<'db> { // } write!(f, "fn(")?; f.write_joined(inputs_and_output.inputs(), ", ")?; - if c_variadic { + if fn_sig_kind.c_variadic() { if inputs_and_output.inputs().is_empty() { write!(f, "...")?; } else { @@ -2067,6 +2175,9 @@ fn write_bounds_like_dyn_trait<'db>( } } ClauseKind::Projection(projection) => { + let TermId::TypeAliasId(assoc_ty_id) = projection.def_id().0 else { + continue; + }; // in types in actual Rust, these will always come // after the corresponding Implemented predicate if angle_open { @@ -2075,7 +2186,6 @@ fn write_bounds_like_dyn_trait<'db>( write!(f, "<")?; angle_open = true; } - let assoc_ty_id = projection.def_id().expect_type_alias(); let type_alias = TypeAliasSignature::of(f.db, assoc_ty_id); f.start_location_link(assoc_ty_id.into()); write!(f, "{}", type_alias.name.display(f.db, f.edition()))?; @@ -2174,11 +2284,28 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> { } } +impl<'db> HirDisplay<'db> for TraitPredicate<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { + self.self_ty().hir_fmt(f)?; + f.write_str(": ")?; + match self.polarity { + rustc_type_ir::PredicatePolarity::Positive => {} + rustc_type_ir::PredicatePolarity::Negative => f.write_char('!')?, + } + let trait_ = self.def_id().0; + f.start_location_link(trait_.into()); + write!(f, "{}", TraitSignature::of(f.db, trait_).name.display(f.db, f.edition()))?; + f.end_location_link(); + let substs = &self.trait_ref.args[1..]; + hir_fmt_generic_args(f, substs, None, None) + } +} + impl<'db> HirDisplay<'db> for Region<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { match self.kind() { RegionKind::ReEarlyParam(param) => { - let generics = generics(f.db, param.id.parent); + let generics = GenericParams::of(f.db, param.id.parent); let param_data = &generics[param.id.local_id]; f.start_location_link_generic(param.id.into()); write!(f, "{}", param_data.name.display(f.db, f.edition()))?; @@ -2364,9 +2491,9 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { if fn_.is_unsafe { write!(f, "unsafe ")?; } - if let Some(abi) = &fn_.abi { + if fn_.abi != ExternAbi::Rust { f.write_str("extern \"")?; - f.write_str(abi.as_str())?; + f.write_str(fn_.abi.as_str())?; f.write_str("\" ")?; } write!(f, "fn(")?; diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs index 0d25d7dbd1..61e6720a29 100644 --- a/crates/hir-ty/src/drop.rs +++ b/crates/hir-ty/src/drop.rs @@ -22,9 +22,9 @@ use crate::{ #[salsa::tracked] pub fn destructor(db: &dyn HirDatabase, adt: AdtId) -> Option<ImplId> { let module = match adt { - AdtId::EnumId(id) => db.lookup_intern_enum(id).container, - AdtId::StructId(id) => db.lookup_intern_struct(id).container, - AdtId::UnionId(id) => db.lookup_intern_union(id).container, + AdtId::EnumId(id) => id.loc(db).container, + AdtId::StructId(id) => id.loc(db).container, + AdtId::UnionId(id) => id.loc(db).container, }; let interner = DbInterner::new_with(db, module.krate(db)); let drop_trait = interner.lang_items().Drop?; @@ -70,7 +70,7 @@ fn has_drop_glue_impl<'db>( let db = infcx.interner.db; match ty.kind() { TyKind::Adt(adt_def, subst) => { - let adt_id = adt_def.def_id().0; + let adt_id = adt_def.def_id(); if adt_def.destructor(infcx.interner).is_some() { return DropGlue::HasDropGlue; } @@ -87,7 +87,7 @@ fn has_drop_glue_impl<'db>( .map(|(_, field_ty)| { has_drop_glue_impl( infcx, - field_ty.get().instantiate(infcx.interner, subst), + field_ty.get().instantiate(infcx.interner, subst).skip_norm_wip(), env, visited, ) @@ -107,7 +107,10 @@ fn has_drop_glue_impl<'db>( .map(|(_, field_ty)| { has_drop_glue_impl( infcx, - field_ty.get().instantiate(infcx.interner, subst), + field_ty + .get() + .instantiate(infcx.interner, subst) + .skip_norm_wip(), env, visited, ) diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs index ba63343d49..34858212cb 100644 --- a/crates/hir-ty/src/dyn_compatibility.rs +++ b/crates/hir-ty/src/dyn_compatibility.rs @@ -6,8 +6,8 @@ use hir_def::{ AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, hir::generics::{GenericParams, LocalTypeOrConstParamId}, - nameres::crate_def_map, signatures::{FunctionSignature, TraitFlags, TraitSignature}, + unstable_features::UnstableFeatures, }; use rustc_hash::FxHashSet; use rustc_type_ir::{ @@ -21,11 +21,14 @@ use crate::{ db::{HirDatabase, InternedOpaqueTyId}, lower::{GenericPredicates, associated_ty_item_bounds}, next_solver::{ - AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, Goal, ParamEnv, - ParamTy, SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, infer::DbInternerInferExt, + AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, ParamTy, + SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, Unnormalized, + infer::{ + DbInternerInferExt, + traits::{Obligation, ObligationCause}, + }, mk_param, }, - traits::next_trait_solve_in_ctxt, }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -111,8 +114,9 @@ where // rustc checks for non-lifetime binders here, but we don't support HRTB yet let trait_data = trait_.trait_items(db); + let mut features = None; for (_, assoc_item) in &trait_data.items { - dyn_compatibility_violation_for_assoc_item(db, trait_, *assoc_item, cb)?; + dyn_compatibility_violation_for_assoc_item(db, &mut features, trait_, *assoc_item, cb)?; } ControlFlow::Continue(()) @@ -142,8 +146,8 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b // FIXME: We should use `explicit_predicates_of` here, which hasn't been implemented to // rust-analyzer yet // https://github.com/rust-lang/rust/blob/ddaf12390d3ffb7d5ba74491a48f3cd528e5d777/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L490 - elaborate::elaborate(interner, predicates.iter_identity()).any(|pred| { - match pred.kind().skip_binder() { + elaborate::elaborate(interner, predicates.iter_identity().map(Unnormalized::skip_norm_wip)).any( + |pred| match pred.kind().skip_binder() { ClauseKind::Trait(trait_pred) => { if sized == trait_pred.def_id().0 && let rustc_type_ir::TyKind::Param(param_ty) = @@ -156,17 +160,17 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b } } _ => false, - } - }) + }, + ) } // rustc gathers all the spans that references `Self` for error rendering, // but we don't have good way to render such locations. // So, just return single boolean value for existence of such `Self` reference fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { - GenericPredicates::query_explicit(db, trait_.into()) - .iter_identity() - .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) + GenericPredicates::query_explicit(db, trait_.into()).iter_identity().any(|pred| { + predicate_references_self(db, trait_, pred.skip_norm_wip(), AllowSelfProjection::No) + }) } // Same as the above, `predicates_reference_self` @@ -244,11 +248,7 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable<DbI proj @ AliasTy { kind: AliasTyKind::Projection { .. }, .. }, ) => match self.allow_self_projection { AllowSelfProjection::Yes => { - let trait_ = proj.trait_def_id(interner); - let trait_ = match trait_ { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_ = proj.trait_def_id(interner).0; if self.super_traits.is_none() { self.super_traits = Some( elaborate::supertrait_def_ids(interner, self.trait_.into()) @@ -274,8 +274,9 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable<DbI t.visit_with(&mut visitor).is_break() } -fn dyn_compatibility_violation_for_assoc_item<F>( - db: &dyn HirDatabase, +fn dyn_compatibility_violation_for_assoc_item<'db, F>( + db: &'db dyn HirDatabase, + features: &mut Option<&'db UnstableFeatures>, trait_: TraitId, item: AssocItemId, cb: &mut F, @@ -297,8 +298,10 @@ where }) } AssocItemId::TypeAliasId(it) => { - let def_map = crate_def_map(db, trait_.krate(db)); - if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) { + if features + .get_or_insert_with(|| UnstableFeatures::query(db, trait_.krate(db))) + .generic_associated_type_extended + { ControlFlow::Continue(()) } else { let generic_params = GenericParams::of(db, item.into()); @@ -397,7 +400,7 @@ fn receiver_is_dispatchable<'db>( func: FunctionId, sig: &EarlyBinder<'db, Binder<'db, rustc_type_ir::FnSig<DbInterner<'db>>>>, ) -> bool { - let sig = sig.instantiate_identity(); + let sig = sig.instantiate_identity().skip_norm_wip(); let module = trait_.module(db); let interner = DbInterner::new_with(db, module.krate(db)); @@ -461,6 +464,7 @@ fn receiver_is_dispatchable<'db>( interner, generic_predicates .iter_identity() + .map(Unnormalized::skip_norm_wip) .chain([unsize_predicate.upcast(interner), trait_predicate.upcast(interner)]) .chain(meta_sized_predicate), ), @@ -470,12 +474,11 @@ fn receiver_is_dispatchable<'db>( // Receiver: DispatchFromDyn<Receiver[Self => U]> let predicate = TraitRef::new(interner, dispatch_from_dyn_did.into(), [receiver_ty, unsized_receiver_ty]); - let goal = Goal::new(interner, param_env, predicate); + let obligation = Obligation::new(interner, ObligationCause::dummy(), param_env, predicate); let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); // the receiver is dispatchable iff the obligation holds - let res = next_trait_solve_in_ctxt(&infcx, goal); - res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) + infcx.predicate_must_hold_modulo_regions(&obligation) } fn receiver_for_self_ty<'db>( @@ -488,7 +491,7 @@ fn receiver_for_self_ty<'db>( if index == 0 { self_ty.into() } else { mk_param(interner, index, kind) } }); - EarlyBinder::bind(receiver_ty).instantiate(interner, args) + EarlyBinder::bind(receiver_ty).instantiate(interner, args).skip_norm_wip() } fn contains_illegal_impl_trait_in_trait<'db>( @@ -509,11 +512,7 @@ fn contains_illegal_impl_trait_in_trait<'db>( .. }) = ty.kind() { - let id = match def_id { - SolverDefId::InternedOpaqueTyId(id) => id, - _ => unreachable!(), - }; - self.0.insert(id); + self.0.insert(def_id.0); } ty.super_visit_with(self) } @@ -526,7 +525,7 @@ fn contains_illegal_impl_trait_in_trait<'db>( // Since we haven't implemented RPITIT in proper way like rustc yet, // just check whether `ret` contains RPIT for now for opaque_ty in visitor.0 { - let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty); + let impl_trait_id = opaque_ty.loc(db); if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) { return Some(MethodViolationCode::ReferencesImplTraitInTrait); } diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs index 822942eec3..c4321e8a61 100644 --- a/crates/hir-ty/src/generics.rs +++ b/crates/hir-ty/src/generics.rs @@ -7,51 +7,56 @@ //! - Type or Const parameters //! //! where parent follows the same scheme. -use std::ops; +use arrayvec::ArrayVec; use hir_def::{ ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId, Lookup, TypeOrConstParamId, TypeParamId, db::DefDatabase, expr_store::ExpressionStore, hir::generics::{ - GenericParamDataRef, GenericParams, LifetimeParamData, LocalLifetimeParamId, - LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamProvenance, WherePredicate, + GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData, + TypeParamProvenance, WherePredicate, }, }; -use itertools::chain; -pub fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics<'_> { - let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); +pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics<'_> { + let mut chain = ArrayVec::new(); + let mut parent_params_len = 0; + if let Some(parent_def) = parent_generic_def(db, def) { + let (parent_params, parent_store) = GenericParams::with_store(db, parent_def); + chain.push(SingleGenerics { + def: parent_def, + params: parent_params, + store: parent_store, + preceding_params_len: 0, + }); + parent_params_len = parent_params.len() as u32; + } let (params, store) = GenericParams::with_store(db, def); - let has_trait_self_param = params.trait_self_param().is_some(); - Generics { def, params, parent_generics, has_trait_self_param, store } + chain.push(SingleGenerics { def, params, store, preceding_params_len: parent_params_len }); + Generics { chain } } -#[derive(Clone, Debug)] + +#[derive(Debug)] pub struct Generics<'db> { + chain: ArrayVec<SingleGenerics<'db>, 2>, +} + +#[derive(Debug)] +pub(crate) struct SingleGenerics<'db> { def: GenericDefId, + preceding_params_len: u32, params: &'db GenericParams, store: &'db ExpressionStore, - parent_generics: Option<Box<Generics<'db>>>, - has_trait_self_param: bool, } -impl<T> ops::Index<T> for Generics<'_> -where - GenericParams: ops::Index<T>, -{ - type Output = <GenericParams as ops::Index<T>>::Output; - fn index(&self, index: T) -> &Self::Output { - &self.params[index] - } -} - -impl<'db> Generics<'db> { +impl<'db> SingleGenerics<'db> { pub(crate) fn def(&self) -> GenericDefId { self.def } - pub(crate) fn store(&self) -> &ExpressionStore { + pub(crate) fn store(&self) -> &'db ExpressionStore { self.store } @@ -59,171 +64,249 @@ impl<'db> Generics<'db> { self.params.where_predicates().iter() } - pub(crate) fn is_empty(&self) -> bool { - self.params.is_empty() && self.parent_generics.as_ref().is_none_or(|g| g.params.is_empty()) + pub(crate) fn has_no_params(&self) -> bool { + self.params.is_empty() + } + + pub(crate) fn len_lifetimes(&self) -> usize { + self.params.len_lifetimes() } - pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ { - self.iter_parent_id().chain(self.iter_self_id()) + pub(crate) fn len(&self) -> usize { + self.params.len() } - pub(crate) fn iter_self_id(&self) -> impl Iterator<Item = GenericParamId> + '_ { - self.iter_self().map(|(id, _)| id) + fn iter_lifetimes(&self) -> impl Iterator<Item = (LifetimeParamId, &'db LifetimeParamData)> { + let parent = self.def; + self.params + .iter_lt() + .map(move |(local_id, data)| (LifetimeParamId { parent, local_id }, data)) } - pub(crate) fn iter_parent_id(&self) -> impl Iterator<Item = GenericParamId> + '_ { - self.iter_parent().map(|(id, _)| id) + pub(crate) fn iter_type_or_consts( + &self, + ) -> impl Iterator<Item = (TypeOrConstParamId, &'db TypeOrConstParamData)> { + let parent = self.def; + self.params + .iter_type_or_consts() + .map(move |(local_id, data)| (TypeOrConstParamId { parent, local_id }, data)) } - pub(crate) fn iter_self_type_or_consts( + fn iter_type_or_consts_as_generic( &self, - ) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> + '_ - { - let mut toc = self.params.iter_type_or_consts(); - let trait_self_param = self.has_trait_self_param.then(|| toc.next()).flatten(); - chain!(trait_self_param, toc) + ) -> impl Iterator<Item = (GenericParamId, GenericParamDataRef<'db>)> { + self.iter_type_or_consts().map(|(id, data)| match data { + TypeOrConstParamData::TypeParamData(data) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(data), + ), + TypeOrConstParamData::ConstParamData(data) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(data), + ), + }) } - /// Iterate over the parent params followed by self params. - pub(crate) fn iter( + fn trait_self_and_others( &self, - ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ { - self.iter_parent().chain(self.iter_self()) + ) -> ( + Option<(GenericParamId, GenericParamDataRef<'db>)>, + impl Iterator<Item = (GenericParamId, GenericParamDataRef<'db>)>, + ) { + let mut iter = self.iter_type_or_consts_as_generic(); + let trait_self = if let GenericDefId::TraitId(_) = self.def { iter.next() } else { None }; + (trait_self, iter) } - pub(crate) fn iter_parents_with_store( + pub(crate) fn iter(&self) -> impl Iterator<Item = (GenericParamId, GenericParamDataRef<'db>)> { + let lifetimes = self.iter_lifetimes().map(|(id, data)| { + (GenericParamId::LifetimeParamId(id), GenericParamDataRef::LifetimeParamData(data)) + }); + let (trait_self, type_and_consts) = self.trait_self_and_others(); + trait_self.into_iter().chain(lifetimes).chain(type_and_consts) + } + + pub(crate) fn iter_with_idx( &self, - ) -> impl Iterator<Item = ((GenericParamId, GenericParamDataRef<'_>), &ExpressionStore)> + '_ - { - self.iter_parent() - .zip(self.parent_generics().into_iter().flat_map(|it| std::iter::repeat(it.store))) + ) -> impl Iterator<Item = (u32, GenericParamId, GenericParamDataRef<'db>)> { + std::iter::zip(self.preceding_params_len.., self.iter()) + .map(|(index, (id, data))| (index, id, data)) + } + + pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> { + self.iter().map(|(id, _)| id) + } +} + +impl<'db> Generics<'db> { + pub(crate) fn iter_owners(&self) -> impl DoubleEndedIterator<Item = &SingleGenerics<'db>> { + self.chain.iter() + } + + fn owner(&self) -> &SingleGenerics<'db> { + self.chain.last().expect("must have an owner params") + } + + pub(crate) fn parent(&self) -> Option<&SingleGenerics<'db>> { + match &*self.chain { + [parent, _owner] => Some(parent), + _ => None, + } + } + + pub(crate) fn has_no_params(&self) -> bool { + self.iter_owners().all(|owner| owner.has_no_params()) + } + + pub(crate) fn def(&self) -> GenericDefId { + self.owner().def + } + + pub(crate) fn store(&self) -> &'db ExpressionStore { + self.owner().store } - /// Iterate over the params without parent params. pub(crate) fn iter_self( &self, - ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ { - let mut toc = self.params.iter_type_or_consts().map(from_toc_id(self)); - let trait_self_param = self.has_trait_self_param.then(|| toc.next()).flatten(); - chain!(trait_self_param, self.params.iter_lt().map(from_lt_id(self)), toc) + ) -> impl Iterator<Item = (GenericParamId, GenericParamDataRef<'db>)> { + self.owner().iter() } - /// Iterator over types and const params of parent. - pub(crate) fn iter_parent( + pub(crate) fn iter_self_with_idx( &self, - ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ { - self.parent_generics().into_iter().flat_map(|it| { - let mut toc = it.params.iter_type_or_consts().map(from_toc_id(it)); - let trait_self_param = it.has_trait_self_param.then(|| toc.next()).flatten(); - chain!(trait_self_param, it.params.iter_lt().map(from_lt_id(it)), toc) - }) + ) -> impl Iterator<Item = (u32, GenericParamId, GenericParamDataRef<'db>)> { + self.owner().iter_with_idx() + } + + pub(crate) fn iter_parent_id(&self) -> impl Iterator<Item = GenericParamId> { + self.parent().into_iter().flat_map(|parent| parent.iter_id()) + } + + pub(crate) fn iter_self_type_or_consts( + &self, + ) -> impl Iterator<Item = (TypeOrConstParamId, &'db TypeOrConstParamData)> { + self.owner().iter_type_or_consts() + } + + /// Iterate over the parent params followed by self params. + #[cfg(test)] + pub(crate) fn iter(&self) -> impl Iterator<Item = (GenericParamId, GenericParamDataRef<'_>)> { + self.iter_owners().flat_map(|owner| owner.iter()) + } + + pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> { + self.iter_owners().flat_map(|owner| owner.iter_id()) } /// Returns total number of generic parameters in scope, including those from parent. pub(crate) fn len(&self) -> usize { - let parent = self.len_parent(); - let child = self.params.len(); - parent + child + match &*self.chain { + [parent, owner] => parent.len() + owner.len(), + [owner] => owner.len(), + _ => unreachable!(), + } } #[inline] pub(crate) fn len_parent(&self) -> usize { - self.parent_generics().map_or(0, Generics::len) - } - - /// Returns numbers of generic parameters excluding those from parent. - pub(crate) fn len_self(&self) -> usize { - self.params.len() + self.parent().map_or(0, SingleGenerics::len) } pub(crate) fn len_lifetimes_self(&self) -> usize { - self.params.len_lifetimes() + self.owner().len_lifetimes() } - /// (parent total, self param, type params, const params, impl trait list, lifetimes) - pub(crate) fn provenance_split(&self) -> (usize, bool, usize, usize, usize, usize) { - let mut self_param = false; - let mut type_params = 0; - let mut impl_trait_params = 0; + pub(crate) fn provenance_split(&self) -> ProvenanceSplit { + let parent_total = self.len_parent(); + + let owner = self.owner(); + let lifetimes = owner.params.len_lifetimes(); + + let mut has_self_param = false; + let mut non_impl_trait_type_params = 0; + let mut impl_trait_type_params = 0; let mut const_params = 0; - self.params.iter_type_or_consts().for_each(|(_, data)| match data { + owner.params.iter_type_or_consts().for_each(|(_, data)| match data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { - TypeParamProvenance::TypeParamList => type_params += 1, - TypeParamProvenance::TraitSelf => self_param |= true, - TypeParamProvenance::ArgumentImplTrait => impl_trait_params += 1, + TypeParamProvenance::TypeParamList => non_impl_trait_type_params += 1, + TypeParamProvenance::TraitSelf => has_self_param |= true, + TypeParamProvenance::ArgumentImplTrait => impl_trait_type_params += 1, }, TypeOrConstParamData::ConstParamData(_) => const_params += 1, }); - let lifetime_params = self.params.len_lifetimes(); - - let parent_len = self.parent_generics().map_or(0, Generics::len); - (parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params) - } - - pub(crate) fn type_or_const_param( - &self, - param: TypeOrConstParamId, - ) -> Option<(usize, TypeOrConstParamData)> { - let idx = self.find_type_or_const_param(param)?; - self.iter().nth(idx).and_then(|p| { - let data = match p.1 { - GenericParamDataRef::TypeParamData(p) => p.clone().into(), - GenericParamDataRef::ConstParamData(p) => p.clone().into(), - _ => return None, - }; - Some((idx, data)) - }) + ProvenanceSplit { + parent_total, + has_self_param, + non_impl_trait_type_params, + const_params, + impl_trait_type_params, + lifetimes, + } } - pub fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> { - self.find_type_or_const_param(param) + fn find_owner(&self, def: GenericDefId) -> &SingleGenerics<'db> { + match &*self.chain { + [parent, owner] => { + if parent.def == def { + parent + } else { + debug_assert_eq!(def, owner.def); + owner + } + } + [owner] => { + debug_assert_eq!(def, owner.def); + owner + } + _ => unreachable!(), + } } - fn find_type_or_const_param(&self, param: TypeOrConstParamId) -> Option<usize> { - if param.parent == self.def { - let idx = param.local_id.into_raw().into_u32() as usize; - debug_assert!( - idx < self.params.len_type_or_consts(), - "idx: {} len: {}", - idx, - self.params.len_type_or_consts() - ); - if self.params.trait_self_param() == Some(param.local_id) { - return Some(idx); - } - Some(self.parent_generics().map_or(0, |g| g.len()) + self.params.len_lifetimes() + idx) + pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> u32 { + let owner = self.find_owner(param.parent); + let has_trait_self = matches!(owner.def, GenericDefId::TraitId(_)); + if has_trait_self && param.local_id == GenericParams::SELF_PARAM_ID_IN_SELF { + owner.preceding_params_len } else { - debug_assert_eq!(self.parent_generics().map(|it| it.def), Some(param.parent)); - self.parent_generics().and_then(|g| g.find_type_or_const_param(param)) + owner.preceding_params_len + + owner.len_lifetimes() as u32 + + param.local_id.into_raw().into_u32() } } - pub fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option<usize> { - self.find_lifetime(lifetime) + pub(crate) fn lifetime_param_idx(&self, param: LifetimeParamId) -> u32 { + let owner = self.find_owner(param.parent); + let has_trait_self = matches!(owner.def, GenericDefId::TraitId(_)); + owner.preceding_params_len + + u32::from(has_trait_self) + + param.local_id.into_raw().into_u32() } - fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<usize> { - if lifetime.parent == self.def { - let idx = lifetime.local_id.into_raw().into_u32() as usize; - debug_assert!(idx <= self.params.len_lifetimes()); - Some( - self.parent_generics().map_or(0, |g| g.len()) - + self.params.trait_self_param().is_some() as usize - + idx, - ) - } else { - debug_assert_eq!(self.parent_generics().map(|it| it.def), Some(lifetime.parent)); - self.parent_generics().and_then(|g| g.find_lifetime(lifetime)) - } + #[deprecated = "don't use this; it's easy to expose an erroneous `Generics` with this"] + pub(crate) fn empty(def: GenericDefId) -> Self { + let mut chain = ArrayVec::new(); + chain.push(SingleGenerics { + def, + preceding_params_len: 0, + params: GenericParams::empty(), + store: ExpressionStore::empty(), + }); + Generics { chain } } +} - pub(crate) fn parent_generics(&self) -> Option<&Generics<'db>> { - self.parent_generics.as_deref() - } +pub(crate) struct ProvenanceSplit { + pub(crate) parent_total: usize, + // The rest are about self. + pub(crate) has_self_param: bool, + pub(crate) non_impl_trait_type_params: usize, + pub(crate) const_params: usize, + pub(crate) impl_trait_type_params: usize, + pub(crate) lifetimes: usize, } -pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> { +fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, GenericDefId::TypeAliasId(it) => it.lookup(db).container, @@ -240,35 +323,3 @@ pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Opt ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, } } - -fn from_toc_id<'a>( - it: &'a Generics<'a>, -) -> impl Fn( - (LocalTypeOrConstParamId, &'a TypeOrConstParamData), -) -> (GenericParamId, GenericParamDataRef<'a>) { - move |(local_id, p): (_, _)| { - let id = TypeOrConstParamId { parent: it.def, local_id }; - match p { - TypeOrConstParamData::TypeParamData(p) => ( - GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), - GenericParamDataRef::TypeParamData(p), - ), - TypeOrConstParamData::ConstParamData(p) => ( - GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), - GenericParamDataRef::ConstParamData(p), - ), - } - } -} - -fn from_lt_id<'a>( - it: &'a Generics<'a>, -) -> impl Fn((LocalLifetimeParamId, &'a LifetimeParamData)) -> (GenericParamId, GenericParamDataRef<'a>) -{ - move |(local_id, p): (_, _)| { - ( - GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), - GenericParamDataRef::LifetimeParamData(p), - ) - } -} diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 339ce7933a..39ffb91a8c 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -29,31 +29,39 @@ mod path; mod place_op; pub(crate) mod unify; -use std::{cell::OnceCell, convert::identity, fmt, iter, ops::Deref}; +use std::{ + cell::{OnceCell, RefCell}, + convert::identity, + fmt, + hash::Hash, + ops::Deref, +}; use base_db::{Crate, FxIndexMap}; use either::Either; use hir_def::{ - AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwnerId, FieldId, - FunctionId, GenericDefId, GenericParamId, ItemContainerId, LocalFieldId, Lookup, TraitId, - TupleFieldId, TupleId, TypeAliasId, TypeOrConstParamId, VariantId, - expr_store::{Body, ExpressionStore, HygieneId, RootExprOrigin, path::Path}, - hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, + AdtId, AssocItemId, AttrDefId, ConstId, DefWithBodyId, ExpressionStoreOwnerId, FieldId, + FunctionId, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, StaticId, TraitId, + TupleFieldId, TupleId, VariantId, + attrs::AttrFlags, + expr_store::{Body, ExpressionStore, HygieneId, path::Path}, + hir::{BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::LangItems, layout::Integer, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature}, - type_ref::{ConstRef, LifetimeRefId, TypeRef, TypeRefId}, + type_ref::{LifetimeRefId, TypeRefId}, + unstable_features::UnstableFeatures, }; use hir_expand::{mod_path::ModPath, name::Name}; use indexmap::IndexSet; -use intern::sym; use la_arena::ArenaMap; +use macros::{TypeFoldable, TypeVisitable}; use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - AliasTyKind, TypeFoldable, - inherent::{AdtDef, IntoKind, Ty as _}, + AliasTyKind, TypeFoldable, TypeVisitableExt, + inherent::{GenericArgs as _, IntoKind, Ty as _}, }; use smallvec::SmallVec; use span::Edition; @@ -61,10 +69,12 @@ use stdx::never; use thin_vec::ThinVec; use crate::{ - ImplTraitId, IncorrectGenericsLenKind, PathLoweringDiagnostic, TargetFeatures, + ImplTraitId, IncorrectGenericsLenKind, InferBodyId, PathLoweringDiagnostic, Span, + TargetFeatures, closure_analysis::PlaceBase, - collect_type_inference_vars, - db::{HirDatabase, InternedOpaqueTyId}, + consteval::{create_anon_const, path_to_const}, + db::{AnonConstId, GeneralConstId, HirDatabase, InternedOpaqueTyId}, + generics::Generics, infer::{ callee::DeferredCallResolution, closure::analysis::{ @@ -72,19 +82,25 @@ use crate::{ expr_use_visitor::{FakeReadCause, Place}, }, coerce::{CoerceMany, DynamicCoerceMany}, - diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext}, + diagnostics::{ + Diagnostics, InferenceTyLoweringContext as TyLoweringContext, + InferenceTyLoweringVarsCtx, + }, expr::ExprIsRead, + pat::PatOrigin, + unify::resolve_completely::WriteBackCtxt, }, lower::{ ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic, }, - method_resolution::{CandidateId, MethodResolutionUnstableFeatures}, + method_resolution::CandidateId, next_solver::{ - AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, - StoredGenericArgs, StoredTy, StoredTys, Ty, TyKind, Tys, + AliasTy, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region, + StoredGenericArg, StoredGenericArgs, StoredTy, StoredTys, Term, Ty, TyKind, Tys, abi::Safety, infer::{InferCtxt, ObligationInspector, traits::ObligationCause}, }, + solver_errors::SolverDiagnostic, utils::TargetFeatureIsSafeInTarget, }; @@ -111,17 +127,24 @@ pub fn infer_query_with_inspect<'db>( let _p = tracing::info_span!("infer_query").entered(); let resolver = def.resolver(db); let body = Body::of(db, def); - let mut ctx = - InferenceContext::new(db, ExpressionStoreOwnerId::Body(def), &body.store, resolver); + let mut ctx = InferenceContext::new( + db, + InferBodyId::DefWithBodyId(def), + ExpressionStoreOwnerId::Body(def), + def.generic_def(db), + &body.store, + resolver, + true, + ); if let Some(inspect) = inspect { ctx.table.infer_ctxt.attach_obligation_inspector(inspect); } match def { - DefWithBodyId::FunctionId(f) => ctx.collect_fn(f, body.self_param, &body.params), + DefWithBodyId::FunctionId(f) => ctx.collect_fn(f, body.self_param(), &body.params), DefWithBodyId::ConstId(c) => ctx.collect_const(c, ConstSignature::of(db, c)), - DefWithBodyId::StaticId(s) => ctx.collect_static(StaticSignature::of(db, s)), + DefWithBodyId::StaticId(s) => ctx.collect_static(s, StaticSignature::of(db, s)), DefWithBodyId::VariantId(v) => { ctx.return_ty = match EnumSignature::variant_body_type(db, v.lookup(db).parent) { hir_def::layout::IntegerType::Pointer(signed) => match signed { @@ -162,91 +185,38 @@ fn infer_cycle_result(db: &dyn HirDatabase, _: salsa::Id, _: DefWithBodyId) -> I } } -/// Infer types for all const expressions in an item's signature. -/// -/// This handles const expressions that appear in type positions within a generic -/// item's signature, such as array lengths (`[T; N]`) and const generic arguments -/// (`Foo<{ expr }>`). Each root expression is inferred independently within -/// a shared `InferenceContext`, accumulating results into a single `InferenceResult`. -fn infer_signature_query(db: &dyn HirDatabase, def: GenericDefId) -> InferenceResult { - let _p = tracing::info_span!("infer_signature_query").entered(); - let store = ExpressionStore::of(db, def.into()); - let mut roots = store.expr_roots_with_origins().peekable(); - let Some(_) = roots.peek() else { - return InferenceResult::new(crate::next_solver::default_types(db).types.error); - }; - - let resolver = def.resolver(db); - let owner = ExpressionStoreOwnerId::Signature(def); - - let mut ctx = InferenceContext::new(db, owner, store, resolver); - - for (root_expr, origin) in roots { - let expected = match origin { - // Array lengths are always `usize`. - RootExprOrigin::ArrayLength => Expectation::has_type(ctx.types.types.usize), - // Const parameter default: look up the param's declared type. - RootExprOrigin::ConstParam(local_id) => Expectation::has_type(db.const_param_ty_ns( - ConstParamId::from_unchecked(TypeOrConstParamId { parent: def, local_id }), - )), - // Path const generic args: determining the expected type requires - // path resolution. - // FIXME - RootExprOrigin::GenericArgsPath => Expectation::None, - RootExprOrigin::BodyRoot => Expectation::None, - }; - ctx.infer_expr(root_expr, &expected, ExprIsRead::Yes); - } - - infer_finalize(ctx) -} - -fn infer_variant_fields_query(db: &dyn HirDatabase, def: VariantId) -> InferenceResult { - let _p = tracing::info_span!("infer_variant_fields_query").entered(); - let store = ExpressionStore::of(db, def.into()); - let mut roots = store.expr_roots_with_origins().peekable(); - let Some(_) = roots.peek() else { - return InferenceResult::new(crate::next_solver::default_types(db).types.error); - }; - - let resolver = def.resolver(db); - let owner = ExpressionStoreOwnerId::VariantFields(def); - - let mut ctx = InferenceContext::new(db, owner, store, resolver); - - for (root_expr, origin) in roots { - let expected = match origin { - // Array lengths are always `usize`. - RootExprOrigin::ArrayLength => Expectation::has_type(ctx.types.types.usize), - // unreachable - RootExprOrigin::ConstParam(_) => Expectation::None, - // Path const generic args: determining the expected type requires - // path resolution. - // FIXME - RootExprOrigin::GenericArgsPath => Expectation::None, - RootExprOrigin::BodyRoot => Expectation::None, - }; - ctx.infer_expr(root_expr, &expected, ExprIsRead::Yes); - } +/// Infer types for an anonymous const expression. +fn infer_anon_const_query(db: &dyn HirDatabase, def: AnonConstId) -> InferenceResult { + let _p = tracing::info_span!("infer_anon_const_query").entered(); + let loc = def.loc(db); + let store_owner = loc.owner; + let store = ExpressionStore::of(db, store_owner); + + let resolver = store_owner.resolver(db); + + let mut ctx = InferenceContext::new( + db, + InferBodyId::AnonConstId(def), + store_owner, + loc.owner.generic_def(db), + store, + resolver, + loc.allow_using_generic_params, + ); + + ctx.infer_expr( + loc.expr, + &Expectation::has_type(loc.ty.get().instantiate_identity().skip_norm_wip()), + ExprIsRead::Yes, + ); infer_finalize(ctx) } -fn infer_signature_cycle_result( +fn infer_anon_const_cycle_result( db: &dyn HirDatabase, _: salsa::Id, - _: GenericDefId, -) -> InferenceResult { - InferenceResult { - has_errors: true, - ..InferenceResult::new(Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed)) - } -} - -fn infer_variant_fields_cycle_result( - db: &dyn HirDatabase, - _: salsa::Id, - _: VariantId, + _: AnonConstId, ) -> InferenceResult { InferenceResult { has_errors: true, @@ -280,27 +250,25 @@ fn infer_finalize(mut ctx: InferenceContext<'_, '_>) -> InferenceResult { ctx.handle_opaque_type_uses(); + ctx.merge_anon_consts(); + ctx.resolve_all() } -/// Binding modes inferred for patterns. -/// <https://doc.rust-lang.org/reference/patterns.html#binding-modes> -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] -pub enum BindingMode { - #[default] - Move, - Ref(Mutability), -} -impl BindingMode { - fn convert(annotation: BindingAnnotation) -> BindingMode { - match annotation { - BindingAnnotation::Unannotated | BindingAnnotation::Mutable => BindingMode::Move, - BindingAnnotation::Ref => BindingMode::Ref(Mutability::Not), - BindingAnnotation::RefMut => BindingMode::Ref(Mutability::Mut), - } - } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ByRef { + Yes(Mutability), + No, } +/// The mode of a binding (`mut`, `ref mut`, etc). +/// Used for both the explicit binding annotations given in the HIR for a binding +/// and the final binding mode that we infer after type inference/match ergonomics. +/// `.0` is the by-reference mode (`ref`, `ref mut`, or by value), +/// `.1` is the mutability of the binding. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct BindingMode(pub ByRef, pub Mutability); + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum InferenceTyDiagnosticSource { /// Diagnostics that come from types in the body. @@ -309,104 +277,188 @@ pub enum InferenceTyDiagnosticSource { Signature, } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, TypeVisitable, TypeFoldable)] pub enum InferenceDiagnostic { NoSuchField { + #[type_visitable(ignore)] field: ExprOrPatId, + #[type_visitable(ignore)] private: Option<LocalFieldId>, + #[type_visitable(ignore)] + variant: VariantId, + }, + MismatchedArrayPatLen { + #[type_visitable(ignore)] + pat: PatId, + #[type_visitable(ignore)] + expected: u128, + #[type_visitable(ignore)] + found: u128, + #[type_visitable(ignore)] + has_rest: bool, + }, + ExpectedArrayOrSlicePat { + #[type_visitable(ignore)] + pat: PatId, + found: StoredTy, + }, + DuplicateField { + #[type_visitable(ignore)] + field: ExprOrPatId, + #[type_visitable(ignore)] variant: VariantId, }, PrivateField { + #[type_visitable(ignore)] expr: ExprId, + #[type_visitable(ignore)] field: FieldId, }, PrivateAssocItem { + #[type_visitable(ignore)] id: ExprOrPatId, + #[type_visitable(ignore)] item: AssocItemId, }, UnresolvedField { + #[type_visitable(ignore)] expr: ExprId, receiver: StoredTy, + #[type_visitable(ignore)] name: Name, + #[type_visitable(ignore)] method_with_same_name_exists: bool, }, UnresolvedMethodCall { + #[type_visitable(ignore)] expr: ExprId, receiver: StoredTy, + #[type_visitable(ignore)] name: Name, /// Contains the type the field resolves to field_with_same_name: Option<StoredTy>, + #[type_visitable(ignore)] assoc_func_with_same_name: Option<FunctionId>, }, UnresolvedAssocItem { + #[type_visitable(ignore)] id: ExprOrPatId, }, UnresolvedIdent { + #[type_visitable(ignore)] id: ExprOrPatId, }, // FIXME: This should be emitted in body lowering BreakOutsideOfLoop { + #[type_visitable(ignore)] expr: ExprId, + #[type_visitable(ignore)] is_break: bool, + #[type_visitable(ignore)] bad_value_break: bool, }, + NonExhaustiveRecordExpr { + #[type_visitable(ignore)] + expr: ExprId, + }, + FunctionalRecordUpdateOnNonStruct { + #[type_visitable(ignore)] + base_expr: ExprId, + }, MismatchedArgCount { + #[type_visitable(ignore)] call_expr: ExprId, + #[type_visitable(ignore)] expected: usize, + #[type_visitable(ignore)] found: usize, }, MismatchedTupleStructPatArgCount { - pat: ExprOrPatId, + #[type_visitable(ignore)] + pat: PatId, + #[type_visitable(ignore)] expected: usize, + #[type_visitable(ignore)] found: usize, }, ExpectedFunction { + #[type_visitable(ignore)] call_expr: ExprId, found: StoredTy, }, TypedHole { + #[type_visitable(ignore)] expr: ExprId, expected: StoredTy, }, CastToUnsized { + #[type_visitable(ignore)] expr: ExprId, cast_ty: StoredTy, }, InvalidCast { + #[type_visitable(ignore)] expr: ExprId, + #[type_visitable(ignore)] error: CastError, expr_ty: StoredTy, cast_ty: StoredTy, }, TyDiagnostic { + #[type_visitable(ignore)] source: InferenceTyDiagnosticSource, + #[type_visitable(ignore)] diag: TyLoweringDiagnostic, }, PathDiagnostic { + #[type_visitable(ignore)] node: ExprOrPatId, + #[type_visitable(ignore)] diag: PathLoweringDiagnostic, }, MethodCallIncorrectGenericsLen { + #[type_visitable(ignore)] expr: ExprId, + #[type_visitable(ignore)] provided_count: u32, + #[type_visitable(ignore)] expected_count: u32, + #[type_visitable(ignore)] kind: IncorrectGenericsLenKind, + #[type_visitable(ignore)] def: GenericDefId, }, MethodCallIncorrectGenericsOrder { + #[type_visitable(ignore)] expr: ExprId, + #[type_visitable(ignore)] param_id: GenericParamId, + #[type_visitable(ignore)] arg_idx: u32, /// Whether the `GenericArgs` contains a `Self` arg. + #[type_visitable(ignore)] has_self_arg: bool, }, -} - -/// A mismatch between an expected and an inferred type. -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct TypeMismatch { - pub expected: StoredTy, - pub actual: StoredTy, + InvalidLhsOfAssignment { + #[type_visitable(ignore)] + lhs: ExprId, + }, + TypeMustBeKnown { + #[type_visitable(ignore)] + at_point: Span, + top_term: Option<StoredGenericArg>, + }, + UnionExprMustHaveExactlyOneField { + #[type_visitable(ignore)] + expr: ExprId, + }, + TypeMismatch { + #[type_visitable(ignore)] + node: ExprOrPatId, + expected: StoredTy, + found: StoredTy, + }, + SolverDiagnostic(SolverDiagnostic), } /// Represents coercing a value to a different type of value. @@ -575,6 +627,27 @@ pub enum PointerCast { Unsize, } +/// Represents an implicit coercion applied to the scrutinee of a match before testing a pattern +/// against it. Currently, this is used only for implicit dereferences. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PatAdjustment { + pub kind: PatAdjust, + /// The type of the scrutinee before the adjustment is applied, or the "adjusted type" of the + /// pattern. + pub source: StoredTy, +} + +/// Represents implicit coercions of patterns' types, rather than values' types. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum PatAdjust { + /// An implicit dereference before matching, such as when matching the pattern `0` against a + /// scrutinee of type `&u8` or `&mut u8`. + BuiltinDeref, + /// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the + /// pattern `[..]` against a scrutinee of type `Vec<T>`. + OverloadedDeref, +} + /// The result of type inference: A mapping from expressions and patterns to types. /// /// When you add a field that stores types (including `Substitution` and the like), don't forget @@ -605,7 +678,6 @@ pub struct InferenceResult { pub(crate) type_of_type_placeholder: FxHashMap<TypeRefId, StoredTy>, pub(crate) type_of_opaque: FxHashMap<InternedOpaqueTyId, StoredTy>, - pub(crate) type_mismatches: Option<Box<FxHashMap<ExprOrPatId, TypeMismatch>>>, /// Whether there are any type-mismatching errors in the result. // FIXME: This isn't as useful as initially thought due to us falling back placeholders to // `TyKind::Error`. @@ -613,6 +685,8 @@ pub struct InferenceResult { pub(crate) has_errors: bool, /// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead. diagnostics: ThinVec<InferenceDiagnostic>, + // FIXME: Remove this, change it to be in `InferenceContext`: + nodes_with_type_mismatches: Option<Box<FxHashSet<ExprOrPatId>>>, /// Interned `Error` type to return references to. // FIXME: Remove this. @@ -620,7 +694,7 @@ pub struct InferenceResult { pub(crate) expr_adjustments: FxHashMap<ExprId, Box<[Adjustment]>>, /// Stores the types which were implicitly dereferenced in pattern binding modes. - pub(crate) pat_adjustments: FxHashMap<PatId, Vec<StoredTy>>, + pub(crate) pat_adjustments: FxHashMap<PatId, Vec<PatAdjustment>>, /// Stores the binding mode (`ref` in `let ref x = 2`) of bindings. /// /// This one is tied to the `PatId` instead of `BindingId`, because in some rare cases, a binding in an @@ -636,9 +710,15 @@ pub struct InferenceResult { /// the first `rest` has implicit `ref` binding mode, but the second `rest` binding mode is `move`. pub(crate) binding_modes: ArenaMap<PatId, BindingMode>, + /// Set of reference patterns that match against a match-ergonomics inserted reference + /// (as opposed to against a reference in the scrutinee type). + skipped_ref_pats: FxHashSet<PatId>, + pub(crate) coercion_casts: FxHashSet<ExprId>, pub closures_data: FxHashMap<ExprId, ClosureData>, + + defined_anon_consts: ThinVec<AnonConstId>, } #[derive(Clone, PartialEq, Eq, Debug, Default)] @@ -870,30 +950,21 @@ impl InferenceResult { /// Returns an `InferenceResult` containing type information for array lengths, /// const generic arguments, and other const expressions appearing in type /// positions within the item's signature. - #[salsa::tracked(returns(ref), cycle_result = infer_signature_cycle_result)] - fn for_signature(db: &dyn HirDatabase, def: GenericDefId) -> InferenceResult { - infer_signature_query(db, def) - } - - #[salsa::tracked(returns(ref), cycle_result = infer_variant_fields_cycle_result)] - fn for_variant_fields(db: &dyn HirDatabase, def: VariantId) -> InferenceResult { - infer_variant_fields_query(db, def) + #[salsa::tracked(returns(ref), cycle_result = infer_anon_const_cycle_result)] + fn for_anon_const(db: &dyn HirDatabase, def: AnonConstId) -> InferenceResult { + infer_anon_const_query(db, def) } -} -impl InferenceResult { - pub fn of(db: &dyn HirDatabase, def: impl Into<ExpressionStoreOwnerId>) -> &InferenceResult { + #[inline] + pub fn of(db: &dyn HirDatabase, def: impl Into<InferBodyId>) -> &InferenceResult { match def.into() { - ExpressionStoreOwnerId::Signature(generic_def_id) => { - Self::for_signature(db, generic_def_id) - } - ExpressionStoreOwnerId::Body(def_with_body_id) => Self::for_body(db, def_with_body_id), - ExpressionStoreOwnerId::VariantFields(variant_id) => { - Self::for_variant_fields(db, variant_id) - } + InferBodyId::DefWithBodyId(it) => InferenceResult::for_body(db, it), + InferBodyId::AnonConstId(it) => InferenceResult::for_anon_const(db, it), } } +} +impl InferenceResult { fn new(error_ty: Ty<'_>) -> Self { Self { method_resolutions: Default::default(), @@ -902,12 +973,13 @@ impl InferenceResult { assoc_resolutions: Default::default(), tuple_field_access_types: Default::default(), diagnostics: Default::default(), + nodes_with_type_mismatches: Default::default(), type_of_expr: Default::default(), type_of_pat: Default::default(), type_of_binding: Default::default(), type_of_type_placeholder: Default::default(), type_of_opaque: Default::default(), - type_mismatches: Default::default(), + skipped_ref_pats: Default::default(), has_errors: Default::default(), error_ty: error_ty.store(), pat_adjustments: Default::default(), @@ -915,6 +987,7 @@ impl InferenceResult { expr_adjustments: Default::default(), coercion_casts: Default::default(), closures_data: Default::default(), + defined_anon_consts: Default::default(), } } @@ -957,26 +1030,22 @@ impl InferenceResult { ExprOrPatId::PatId(id) => self.assoc_resolutions_for_pat(id), } } - pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> { - self.type_mismatches.as_deref()?.get(&expr.into()) + pub fn expr_or_pat_has_type_mismatch(&self, node: ExprOrPatId) -> bool { + self.nodes_with_type_mismatches.as_ref().is_some_and(|it| it.contains(&node)) } - pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> { - self.type_mismatches.as_deref()?.get(&pat.into()) + pub fn expr_has_type_mismatch(&self, expr: ExprId) -> bool { + self.expr_or_pat_has_type_mismatch(expr.into()) } - pub fn type_mismatches(&self) -> impl Iterator<Item = (ExprOrPatId, &TypeMismatch)> { - self.type_mismatches - .as_deref() - .into_iter() - .flatten() - .map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch)) - } - pub fn expr_type_mismatches(&self) -> impl Iterator<Item = (ExprId, &TypeMismatch)> { - self.type_mismatches.as_deref().into_iter().flatten().filter_map( - |(expr_or_pat, mismatch)| match *expr_or_pat { - ExprOrPatId::ExprId(expr) => Some((expr, mismatch)), - _ => None, - }, - ) + pub fn pat_has_type_mismatch(&self, pat: PatId) -> bool { + self.expr_or_pat_has_type_mismatch(pat.into()) + } + pub fn exprs_have_type_mismatches(&self) -> bool { + self.nodes_with_type_mismatches + .as_ref() + .is_some_and(|it| it.iter().any(|node| node.is_expr())) + } + pub fn has_type_mismatches(&self) -> bool { + self.nodes_with_type_mismatches.is_some() } pub fn placeholder_types<'db>(&self) -> impl Iterator<Item = (TypeRefId, Ty<'db>)> { self.type_of_type_placeholder.iter().map(|(&type_ref, ty)| (type_ref, ty.as_ref())) @@ -1007,10 +1076,10 @@ impl InferenceResult { None => self.type_of_expr.get(id).map(|it| it.as_ref()), } } - pub fn type_of_pat_with_adjust<'db>(&self, id: PatId) -> Option<Ty<'db>> { + pub fn type_of_pat_with_adjust<'db>(&self, id: PatId) -> Ty<'db> { match self.pat_adjustments.get(&id).and_then(|adjustments| adjustments.last()) { - Some(adjusted) => Some(adjusted.as_ref()), - None => self.type_of_pat.get(id).map(|it| it.as_ref()), + Some(adjusted) => adjusted.source.as_ref(), + None => self.pat_ty(id), } } pub fn is_erroneous(&self) -> bool { @@ -1025,7 +1094,7 @@ impl InferenceResult { self.tuple_field_access_types[id.0 as usize].as_ref() } - pub fn pat_adjustment(&self, id: PatId) -> Option<&[StoredTy]> { + pub fn pat_adjustment(&self, id: PatId) -> Option<&[PatAdjustment]> { self.pat_adjustments.get(&id).map(|it| &**it) } @@ -1100,23 +1169,37 @@ impl InferenceResult { .values() .flat_map(|captures| captures.iter().map(|capture| capture.captured_ty(db))) } + + pub fn is_skipped_ref_pat(&self, pat: PatId) -> bool { + self.skipped_ref_pats.contains(&pat) + } +} + +#[derive(Debug, Clone, Copy)] +enum DerefPatBorrowMode { + Borrow(Mutability), + Box, } /// The inference context contains all information needed during type inference. -#[derive(Clone, Debug)] +#[derive(Debug)] pub(crate) struct InferenceContext<'body, 'db> { pub(crate) db: &'db dyn HirDatabase, - pub(crate) owner: ExpressionStoreOwnerId, + pub(crate) owner: InferBodyId, + pub(crate) store_owner: ExpressionStoreOwnerId, + pub(crate) generic_def: GenericDefId, pub(crate) store: &'body ExpressionStore, /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext /// and resolve the path via its methods. This will ensure proper error reporting. pub(crate) resolver: Resolver<'db>, target_features: OnceCell<(TargetFeatures<'db>, TargetFeatureIsSafeInTarget)>, - pub(crate) unstable_features: MethodResolutionUnstableFeatures, pub(crate) edition: Edition, - pub(crate) generic_def: GenericDefId, + allow_using_generic_params: bool, + generics: OnceCell<Generics<'db>>, + identity_args: OnceCell<GenericArgs<'db>>, pub(crate) table: unify::InferenceTable<'db>, pub(crate) lang_items: &'db LangItems, + pub(crate) features: &'db UnstableFeatures, /// The traits in scope, disregarding block modules. This is used for caching purposes. traits_in_scope: FxHashSet<TraitId>, pub(crate) result: InferenceResult, @@ -1147,6 +1230,9 @@ pub(crate) struct InferenceContext<'body, 'db> { deferred_call_resolutions: FxHashMap<ExprId, Vec<DeferredCallResolution<'db>>>, diagnostics: Diagnostics, + vars_emitted_type_must_be_known_for: FxHashSet<Term<'db>>, + + defined_anon_consts: RefCell<ThinVec<AnonConstId>>, } #[derive(Clone, Debug)] @@ -1196,32 +1282,23 @@ fn find_continuable<'a, 'db>( impl<'body, 'db> InferenceContext<'body, 'db> { fn new( db: &'db dyn HirDatabase, - owner: ExpressionStoreOwnerId, + owner: InferBodyId, + store_owner: ExpressionStoreOwnerId, + generic_def: GenericDefId, store: &'body ExpressionStore, resolver: Resolver<'db>, + allow_using_generic_params: bool, ) -> Self { - let trait_env = match owner { - ExpressionStoreOwnerId::Signature(generic_def_id) => { - db.trait_environment(ExpressionStoreOwnerId::from(generic_def_id)) - } - ExpressionStoreOwnerId::Body(def_with_body_id) => { - db.trait_environment(ExpressionStoreOwnerId::Body(def_with_body_id)) - } - ExpressionStoreOwnerId::VariantFields(variant_id) => { - db.trait_environment(ExpressionStoreOwnerId::VariantFields(variant_id)) - } - }; - let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), Some(owner)); + let trait_env = db.trait_environment(store_owner); + let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner); let types = crate::next_solver::default_types(db); InferenceContext { result: InferenceResult::new(types.types.error), return_ty: types.types.error, // set in collect_* calls types, target_features: OnceCell::new(), - unstable_features: MethodResolutionUnstableFeatures::from_def_map( - resolver.top_level_def_map(), - ), lang_items: table.interner().lang_items(), + features: resolver.top_level_def_map().features(), edition: resolver.krate().data(db).edition, table, tuple_field_accesses_rev: Default::default(), @@ -1229,7 +1306,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { return_coercion: None, db, owner, - generic_def: owner.generic_def(db), + store_owner, + generic_def, + allow_using_generic_params, + generics: OnceCell::new(), + identity_args: OnceCell::new(), store, traits_in_scope: resolver.traits_in_scope(db), resolver, @@ -1238,7 +1319,99 @@ impl<'body, 'db> InferenceContext<'body, 'db> { deferred_cast_checks: Vec::new(), inside_assignment: false, diagnostics: Diagnostics::default(), + vars_emitted_type_must_be_known_for: FxHashSet::default(), deferred_call_resolutions: FxHashMap::default(), + defined_anon_consts: RefCell::new(ThinVec::new()), + } + } + + fn merge(&mut self, other: &InferenceResult) { + let InferenceResult { + method_resolutions, + field_resolutions, + variant_resolutions, + assoc_resolutions, + tuple_field_access_types: _, + type_of_expr, + type_of_pat, + type_of_binding, + type_of_type_placeholder, + type_of_opaque, + has_errors: _, + diagnostics: _, + error_ty: _, + expr_adjustments, + pat_adjustments, + binding_modes, + skipped_ref_pats, + coercion_casts, + closures_data, + nodes_with_type_mismatches, + defined_anon_consts: _, + } = &mut self.result; + merge_hash_maps(method_resolutions, &other.method_resolutions); + merge_hash_maps(variant_resolutions, &other.variant_resolutions); + merge_hash_maps(assoc_resolutions, &other.assoc_resolutions); + field_resolutions.extend(other.field_resolutions.iter().map( + |(&field_expr, &field_resolution)| { + let mut field_resolution = field_resolution; + if let Either::Right(tuple_field) = &mut field_resolution { + let tys = other.tuple_field_access_type(tuple_field.tuple); + tuple_field.tuple = + TupleId(self.tuple_field_accesses_rev.insert_full(tys).0 as u32); + }; + (field_expr, field_resolution) + }, + )); + merge_arena_maps(type_of_expr, &other.type_of_expr); + merge_arena_maps(type_of_pat, &other.type_of_pat); + merge_arena_maps(type_of_binding, &other.type_of_binding); + merge_hash_maps(type_of_type_placeholder, &other.type_of_type_placeholder); + merge_hash_maps(type_of_opaque, &other.type_of_opaque); + merge_hash_maps(expr_adjustments, &other.expr_adjustments); + merge_hash_maps(pat_adjustments, &other.pat_adjustments); + merge_arena_maps(binding_modes, &other.binding_modes); + merge_hash_set(skipped_ref_pats, &other.skipped_ref_pats); + merge_hash_set(coercion_casts, &other.coercion_casts); + merge_hash_maps(closures_data, &other.closures_data); + if let Some(other_nodes_with_type_mismatches) = &other.nodes_with_type_mismatches { + merge_hash_set( + nodes_with_type_mismatches.get_or_insert_default(), + other_nodes_with_type_mismatches, + ); + } + self.defined_anon_consts.borrow_mut().extend(other.defined_anon_consts.iter().copied()); + + fn merge_hash_set<T: Hash + Eq + Clone>(dest: &mut FxHashSet<T>, source: &FxHashSet<T>) { + dest.extend(source.iter().cloned()); + } + + #[cfg_attr(debug_assertions, track_caller)] + fn merge_hash_maps<K: Hash + Eq + Clone, V: Clone + PartialEq>( + dest: &mut FxHashMap<K, V>, + source: &FxHashMap<K, V>, + ) { + if cfg!(debug_assertions) { + for (key, src) in source { + assert!(dest.get(key).is_none_or(|dst| dst == src)); + } + } + + dest.extend(source.iter().map(|(k, v)| (k.clone(), v.clone()))); + } + + #[cfg_attr(debug_assertions, track_caller)] + fn merge_arena_maps<K, V: Clone + PartialEq>( + dest: &mut ArenaMap<la_arena::Idx<K>, V>, + source: &ArenaMap<la_arena::Idx<K>, V>, + ) { + if cfg!(debug_assertions) { + for (key, src) in source.iter() { + assert!(dest.get(key).is_none_or(|dst| dst == src)); + } + } + + dest.extend(source.iter().map(|(k, v)| (k, v.clone()))); } } @@ -1249,7 +1422,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { fn target_features(&self) -> (&TargetFeatures<'db>, TargetFeatureIsSafeInTarget) { let (target_features, target_feature_is_safe) = self.target_features.get_or_init(|| { - let target_features = match self.owner { + let target_features = match self.store_owner { ExpressionStoreOwnerId::Body(DefWithBodyId::FunctionId(id)) => { TargetFeatures::from_fn(self.db, id) } @@ -1264,31 +1437,43 @@ impl<'body, 'db> InferenceContext<'body, 'db> { (target_features, *target_feature_is_safe) } + /// How should a deref pattern find the place for its inner pattern to match on? + /// + /// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner + /// pattern's scrutinee by calling `DerefMut::deref_mut`, and otherwise we call `Deref::deref`. + /// However, for boxes we can use a built-in deref instead, which doesn't borrow the scrutinee; + /// in this case, we return `DerefPatBorrowMode::Box`. + fn deref_pat_borrow_mode(&self, pointer_ty: Ty<'_>, inner: PatId) -> DerefPatBorrowMode { + if pointer_ty.is_box() { + DerefPatBorrowMode::Box + } else { + let mutability = + if self.pat_has_ref_mut_binding(inner) { Mutability::Mut } else { Mutability::Not }; + DerefPatBorrowMode::Borrow(mutability) + } + } + #[inline] fn set_tainted_by_errors(&mut self) { self.result.has_errors = true; } - /// Clones `self` and calls `resolve_all()` on it. - // FIXME: Remove this. - pub(crate) fn fixme_resolve_all_clone(&self) -> InferenceResult { - let mut ctx = self.clone(); - - ctx.type_inference_fallback(); - - // Comment from rustc: - // Even though coercion casts provide type hints, we check casts after fallback for - // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - let cast_checks = std::mem::take(&mut ctx.deferred_cast_checks); - for mut cast in cast_checks.into_iter() { - if let Err(diag) = cast.check(&mut ctx) { - ctx.diagnostics.push(diag); + /// Copy the inference of defined anon consts to ourselves, so that we don't need to lookup the defining + /// anon const when looking the type of something. + fn merge_anon_consts(&mut self) { + let mut defined_anon_consts = std::mem::take(&mut *self.defined_anon_consts.borrow_mut()); + defined_anon_consts.retain(|&konst| { + if konst.loc(self.db).owner != self.store_owner { + // This comes from the signature, we don't define it. + return false; } - } - - ctx.table.select_obligations_where_possible(); - ctx.resolve_all() + let const_infer = InferenceResult::of(self.db, konst); + self.merge(const_infer); + true + }); + // Caution, other defined anon consts might have been added by `merge()`! + self.defined_anon_consts.borrow_mut().append(&mut defined_anon_consts); } // FIXME: This function should be private in module. It is currently only used in the consteval, since we need @@ -1297,14 +1482,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> { // there is no problem in it being `pub(crate)`, remove this comment. fn resolve_all(self) -> InferenceResult { let InferenceContext { - mut table, + table, mut result, tuple_field_accesses_rev, diagnostics, types, + vars_emitted_type_must_be_known_for, .. } = self; - let mut diagnostics = diagnostics.finish(); + let diagnostics = diagnostics.finish(); // Destructure every single field so whenever new fields are added to `InferenceResult` we // don't forget to handle them here. let InferenceResult { @@ -1317,97 +1503,66 @@ impl<'body, 'db> InferenceContext<'body, 'db> { type_of_binding, type_of_type_placeholder, type_of_opaque, - type_mismatches, + skipped_ref_pats, closures_data, has_errors, error_ty: _, pat_adjustments, binding_modes: _, expr_adjustments, - tuple_field_access_types: _, + tuple_field_access_types, coercion_casts: _, - diagnostics: _, + diagnostics: result_diagnostics, + nodes_with_type_mismatches, + defined_anon_consts: result_defined_anon_consts, } = &mut result; + *result_defined_anon_consts = self.defined_anon_consts.into_inner(); + result_defined_anon_consts.shrink_to_fit(); + + let mut resolver = + WriteBackCtxt::new(table, diagnostics, vars_emitted_type_must_be_known_for); + + skipped_ref_pats.shrink_to_fit(); for ty in type_of_expr.values_mut() { - *ty = table.resolve_completely(ty.as_ref()).store(); - *has_errors = *has_errors || ty.as_ref().references_non_lt_error(); + resolver.resolve_completely(ty); } type_of_expr.shrink_to_fit(); for ty in type_of_pat.values_mut() { - *ty = table.resolve_completely(ty.as_ref()).store(); - *has_errors = *has_errors || ty.as_ref().references_non_lt_error(); + resolver.resolve_completely(ty); } type_of_pat.shrink_to_fit(); for ty in type_of_binding.values_mut() { - *ty = table.resolve_completely(ty.as_ref()).store(); - *has_errors = *has_errors || ty.as_ref().references_non_lt_error(); + resolver.resolve_completely(ty); } type_of_binding.shrink_to_fit(); for ty in type_of_type_placeholder.values_mut() { - *ty = table.resolve_completely(ty.as_ref()).store(); - *has_errors = *has_errors || ty.as_ref().references_non_lt_error(); + resolver.resolve_completely(ty); } type_of_type_placeholder.shrink_to_fit(); type_of_opaque.shrink_to_fit(); - if let Some(type_mismatches) = type_mismatches { + if let Some(nodes_with_type_mismatches) = nodes_with_type_mismatches { *has_errors = true; - for mismatch in type_mismatches.values_mut() { - mismatch.expected = table.resolve_completely(mismatch.expected.as_ref()).store(); - mismatch.actual = table.resolve_completely(mismatch.actual.as_ref()).store(); - } - type_mismatches.shrink_to_fit(); + nodes_with_type_mismatches.shrink_to_fit(); } - diagnostics.retain_mut(|diagnostic| { - use InferenceDiagnostic::*; - match diagnostic { - ExpectedFunction { found: ty, .. } - | UnresolvedField { receiver: ty, .. } - | UnresolvedMethodCall { receiver: ty, .. } => { - *ty = table.resolve_completely(ty.as_ref()).store(); - // FIXME: Remove this when we are on par with rustc in terms of inference - if ty.as_ref().references_non_lt_error() { - return false; - } - - if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic - && let Some(ty) = field_with_same_name - { - *ty = table.resolve_completely(ty.as_ref()).store(); - if ty.as_ref().references_non_lt_error() { - *field_with_same_name = None; - } - } - } - TypedHole { expected: ty, .. } => { - *ty = table.resolve_completely(ty.as_ref()).store(); - } - _ => (), - } - true - }); - diagnostics.shrink_to_fit(); for (_, subst) in method_resolutions.values_mut() { - *subst = table.resolve_completely(subst.as_ref()).store(); - *has_errors = - *has_errors || subst.as_ref().types().any(|ty| ty.references_non_lt_error()); + resolver.resolve_completely(subst); } method_resolutions.shrink_to_fit(); for (_, subst) in assoc_resolutions.values_mut() { - *subst = table.resolve_completely(subst.as_ref()).store(); - *has_errors = - *has_errors || subst.as_ref().types().any(|ty| ty.references_non_lt_error()); + resolver.resolve_completely(subst); } assoc_resolutions.shrink_to_fit(); for adjustment in expr_adjustments.values_mut().flatten() { - adjustment.target = table.resolve_completely(adjustment.target.as_ref()).store(); - *has_errors = *has_errors || adjustment.target.as_ref().references_non_lt_error(); + resolver.resolve_completely(&mut adjustment.target); } expr_adjustments.shrink_to_fit(); - for adjustment in pat_adjustments.values_mut().flatten() { - *adjustment = table.resolve_completely(adjustment.as_ref()).store(); - *has_errors = *has_errors || adjustment.as_ref().references_non_lt_error(); + for adjustments in pat_adjustments.values_mut() { + for adjustment in &mut *adjustments { + resolver.resolve_completely(&mut adjustment.source); + } + adjustments.shrink_to_fit(); } pat_adjustments.shrink_to_fit(); for closure_data in closures_data.values_mut() { @@ -1419,7 +1574,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { }; for (place, _, sources) in fake_reads { - *place = table.resolve_completely(std::mem::replace(place, dummy_place())); + resolver.resolve_completely_with_default(place, dummy_place()); place.projections.shrink_to_fit(); for source in &mut *sources { source.shrink_to_fit(); @@ -1430,7 +1585,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { for min_capture in min_captures.values_mut() { for captured in &mut *min_capture { let CapturedPlace { place, info, mutability: _ } = captured; - *place = table.resolve_completely(std::mem::replace(place, dummy_place())); + resolver.resolve_completely_with_default(place, dummy_place()); let CaptureInfo { sources, capture_kind: _ } = info; for source in &mut *sources { source.shrink_to_fit(); @@ -1442,17 +1597,18 @@ impl<'body, 'db> InferenceContext<'body, 'db> { min_captures.shrink_to_fit(); } closures_data.shrink_to_fit(); - result.tuple_field_access_types = tuple_field_accesses_rev + *tuple_field_access_types = tuple_field_accesses_rev .into_iter() - .map(|subst| table.resolve_completely(subst).store()) - .inspect(|subst| { - *has_errors = - *has_errors || subst.as_ref().iter().any(|ty| ty.references_non_lt_error()); + .map(|mut subst| { + resolver.resolve_completely(&mut subst); + subst.store() }) .collect(); - result.tuple_field_access_types.shrink_to_fit(); + tuple_field_access_types.shrink_to_fit(); - result.diagnostics = diagnostics; + let (diagnostics, resolver_has_errors) = resolver.resolve_diagnostics(); + *result_diagnostics = diagnostics; + *has_errors |= resolver_has_errors; result } @@ -1462,17 +1618,19 @@ impl<'body, 'db> InferenceContext<'body, 'db> { data.type_ref, &data.store, InferenceTyDiagnosticSource::Signature, + ExpressionStoreOwnerId::Signature(id.into()), LifetimeElisionKind::for_const(self.interner(), id.loc(self.db).container), ); self.return_ty = return_ty; } - fn collect_static(&mut self, data: &StaticSignature) { + fn collect_static(&mut self, id: StaticId, data: &StaticSignature) { let return_ty = self.make_ty( data.type_ref, &data.store, InferenceTyDiagnosticSource::Signature, + ExpressionStoreOwnerId::Signature(id.into()), LifetimeElisionKind::Elided(self.types.regions.statik), ); @@ -1484,6 +1642,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let mut param_tys = self.with_ty_lowering( &data.store, InferenceTyDiagnosticSource::Signature, + ExpressionStoreOwnerId::Signature(func.into()), LifetimeElisionKind::for_fn_params(data), |ctx| data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>(), ); @@ -1498,7 +1657,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { GenericArgs::for_item_with_defaults( self.interner(), va_list.into(), - |_, id, _| self.table.next_var_for_param(id), + |_, id, _| self.table.var_for_def(id, Span::Dummy), ), ), None => self.err_ty(), @@ -1506,23 +1665,25 @@ impl<'body, 'db> InferenceContext<'body, 'db> { param_tys.push(va_list_ty); } - let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.next_ty_var())); + let mut param_tys = param_tys.into_iter(); if let Some(self_param) = self_param && let Some(ty) = param_tys.next() { let ty = self.process_user_written_ty(ty); self.write_binding_ty(self_param, ty); } - for (ty, pat) in param_tys.zip(params) { + for pat in params { + let ty = param_tys.next().unwrap_or_else(|| self.table.next_ty_var(Span::Dummy)); let ty = self.process_user_written_ty(ty); - self.infer_top_pat(*pat, ty, None); + self.infer_top_pat(*pat, ty, PatOrigin::Param); } self.return_ty = match data.ret_type { Some(return_ty) => { let return_ty = self.with_ty_lowering( &data.store, InferenceTyDiagnosticSource::Signature, + ExpressionStoreOwnerId::Signature(func.into()), LifetimeElisionKind::for_fn_ret(self.interner()), |ctx| { ctx.impl_trait_mode(ImplTraitLoweringMode::Opaque); @@ -1547,6 +1708,33 @@ impl<'body, 'db> InferenceContext<'body, 'db> { &self.table.infer_ctxt } + /// If `ty` is an error, returns an infer var instead. Otherwise, returns it. + /// + /// "Refreshing" types like this is useful for getting better types, but it is also + /// very dangerous: we might create duplicate diagnostics, for example if we try + /// to resolve it and fail. rustc doesn't do that for this reason (and is in general + /// more strict with how it uses error types; an error type in inputs will almost + /// always cause it to infer an error type in output, while we infer some type as much + /// as we can). + /// + /// Unfortunately, we cannot allow ourselves to do that. Not only we more often work + /// with incomplete code, we also have assists, for example "Generate constant", that + /// will assume the inferred type is the expected type even if the expression itself + /// cannot be inferred. Therefore, we choose a middle ground: refresh the type, + /// but if we return a new var, mark it so that no diagnostics will be issued on it. + fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { + if ty.is_ty_error() { + let var = self.table.next_ty_var(Span::Dummy); + + // Suppress future errors on this var. Add more things here when we add more diagnostics. + self.vars_emitted_type_must_be_known_for.insert(var.into()); + + var + } else { + ty + } + } + fn infer_body(&mut self, body_expr: ExprId) { match self.return_coercion { Some(_) => self.infer_return(body_expr), @@ -1590,13 +1778,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } } - fn write_pat_adj(&mut self, pat: PatId, adjustments: Box<[StoredTy]>) { - if adjustments.is_empty() { - return; - } - self.result.pat_adjustments.entry(pat).or_default().extend(adjustments); - } - pub(crate) fn write_method_resolution( &mut self, expr: ExprId, @@ -1623,10 +1804,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.result.type_of_pat.insert(pat, ty.store()); } - fn write_type_placeholder_ty(&mut self, type_ref: TypeRefId, ty: Ty<'db>) { - self.result.type_of_type_placeholder.insert(type_ref, ty.store()); - } - fn write_binding_ty(&mut self, id: BindingId, ty: Ty<'db>) { self.result.type_of_binding.insert(id, ty.store()); } @@ -1654,17 +1831,30 @@ impl<'body, 'db> InferenceContext<'body, 'db> { &mut self, store: &ExpressionStore, types_source: InferenceTyDiagnosticSource, + store_owner: ExpressionStoreOwnerId, lifetime_elision: LifetimeElisionKind<'db>, f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> R, ) -> R { + let infer_vars = match types_source { + InferenceTyDiagnosticSource::Body => Some(&mut InferenceTyLoweringVarsCtx { + table: &mut self.table, + type_of_type_placeholder: &mut self.result.type_of_type_placeholder, + } as _), + InferenceTyDiagnosticSource::Signature => None, + }; let mut ctx = TyLoweringContext::new( self.db, &self.resolver, store, &self.diagnostics, types_source, + store_owner, self.generic_def, + &self.generics, lifetime_elision, + self.allow_using_generic_params, + infer_vars, + &self.defined_anon_consts, ); f(&mut ctx) } @@ -1676,6 +1866,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.with_ty_lowering( self.store, InferenceTyDiagnosticSource::Body, + self.store_owner, LifetimeElisionKind::Infer, f, ) @@ -1686,29 +1877,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> { type_ref: TypeRefId, store: &ExpressionStore, type_source: InferenceTyDiagnosticSource, + store_owner: ExpressionStoreOwnerId, lifetime_elision: LifetimeElisionKind<'db>, ) -> Ty<'db> { - let ty = self - .with_ty_lowering(store, type_source, lifetime_elision, |ctx| ctx.lower_ty(type_ref)); - let ty = self.process_user_written_ty(ty); - - // Record the association from placeholders' TypeRefId to type variables. - // We only record them if their number matches. This assumes TypeRef::walk and TypeVisitable process the items in the same order. - let type_variables = collect_type_inference_vars(&ty); - let mut placeholder_ids = vec![]; - TypeRef::walk(type_ref, store, &mut |type_ref_id, type_ref| { - if matches!(type_ref, TypeRef::Placeholder) { - placeholder_ids.push(type_ref_id); - } + let ty = self.with_ty_lowering(store, type_source, store_owner, lifetime_elision, |ctx| { + ctx.lower_ty(type_ref) }); - - if placeholder_ids.len() == type_variables.len() { - for (placeholder_id, type_variable) in placeholder_ids.into_iter().zip(type_variables) { - self.write_type_placeholder_ty(placeholder_id, type_variable); - } - } - - ty + self.process_user_written_ty(ty) } pub(crate) fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { @@ -1716,28 +1891,57 @@ impl<'body, 'db> InferenceContext<'body, 'db> { type_ref, self.store, InferenceTyDiagnosticSource::Body, + self.store_owner, LifetimeElisionKind::Infer, ) } - pub(crate) fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty<'db>) -> Const<'db> { - let const_ = self.with_ty_lowering( - self.store, - InferenceTyDiagnosticSource::Body, - LifetimeElisionKind::Infer, - |ctx| ctx.lower_const(const_ref, ty), - ); - self.insert_type_vars(const_) + fn generics(&self) -> &Generics<'db> { + self.generics.get_or_init(|| crate::generics::generics(self.db, self.generic_def)) + } + + fn identity_args(&self) -> GenericArgs<'db> { + *self.identity_args.get_or_init(|| { + GenericArgs::identity_for_item(self.interner(), self.store_owner.into()) + }) } - pub(crate) fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> { - let const_ = self.with_ty_lowering( + pub(crate) fn create_body_anon_const( + &mut self, + expr: ExprId, + expected_ty: Ty<'db>, + allow_using_generic_params: bool, + ) -> Const<'db> { + never!(expected_ty.has_infer(), "cannot have infer vars in an anon const's ty"); + let konst = create_anon_const( + self.interner(), + self.store_owner, self.store, - InferenceTyDiagnosticSource::Body, - LifetimeElisionKind::Infer, - |ctx| ctx.lower_path_as_const(path, ty), + expr, + &self.resolver, + expected_ty, + &|| self.generics(), + Some(&mut |span| self.table.next_const_var(span)), + (!(allow_using_generic_params && self.allow_using_generic_params)).then_some(0), ); - self.insert_type_vars(const_) + + if let Ok(konst) = konst + && let ConstKind::Unevaluated(konst) = konst.kind() + && let GeneralConstId::AnonConstId(konst) = konst.def.0 + { + self.defined_anon_consts.borrow_mut().push(konst); + } + + self.write_expr_ty(expr, expected_ty); + // FIXME: Report an error if needed. + konst.unwrap_or_else(|_| self.table.next_const_var(Span::Dummy)) + } + + pub(crate) fn make_path_as_body_const(&mut self, path: &Path) -> Const<'db> { + let forbid_params_after = if self.allow_using_generic_params { None } else { Some(0) }; + // FIXME: Report errors. + path_to_const(self.db, &self.resolver, &|| self.generics(), forbid_params_after, path) + .unwrap_or_else(|_| self.table.next_const_var(Span::Dummy)) } fn err_ty(&self) -> Ty<'db> { @@ -1748,17 +1952,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let lt = self.with_ty_lowering( self.store, InferenceTyDiagnosticSource::Body, + self.store_owner, LifetimeElisionKind::Infer, |ctx| ctx.lower_lifetime(lifetime_ref), ); self.insert_type_vars(lt) } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. - fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { - self.table.insert_type_vars_shallow(ty) - } - fn insert_type_vars<T>(&mut self, ty: T) -> T where T: TypeFoldable<DbInterner<'db>>, @@ -1766,10 +1966,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.insert_type_vars(ty) } - fn unify(&mut self, ty1: Ty<'db>, ty2: Ty<'db>) -> bool { - self.table.unify(ty1, ty2) - } - /// Attempts to returns the deeply last field of nested structures, but /// does not apply any normalization in its search. Returns the same type /// if input `ty` is not a structure at all. @@ -1796,7 +1992,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { return self.err_ty(); } match ty.kind() { - TyKind::Adt(adt_def, substs) => match adt_def.def_id().0 { + TyKind::Adt(adt_def, substs) => match adt_def.def_id() { AdtId::StructId(struct_id) => { match self .db @@ -1806,7 +2002,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { .map(|it| it.get()) { Some(field) => { - ty = field.instantiate(self.interner(), substs); + ty = field.instantiate(self.interner(), substs).skip_norm_wip(); } None => break, } @@ -1846,12 +2042,28 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.shallow_resolve(ty) } - fn resolve_associated_type( + pub(crate) fn resolve_vars_if_possible<T: TypeFoldable<DbInterner<'db>>>(&self, t: T) -> T { + self.table.resolve_vars_if_possible(t) + } + + pub(crate) fn structurally_resolve_type(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> Ty<'db> { + let result = self.table.try_structurally_resolve_type(node.into(), ty); + if result.is_ty_var() { self.type_must_be_known_at_this_point(node, ty) } else { result } + } + + pub(crate) fn emit_type_mismatch( &mut self, - inner_ty: Ty<'db>, - assoc_ty: Option<TypeAliasId>, - ) -> Ty<'db> { - self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[]) + node: ExprOrPatId, + expected: Ty<'db>, + found: Ty<'db>, + ) { + if self.result.nodes_with_type_mismatches.get_or_insert_default().insert(node) { + self.diagnostics.push(InferenceDiagnostic::TypeMismatch { + node, + expected: expected.store(), + found: found.store(), + }); + } } fn demand_eqtype( @@ -1860,14 +2072,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> { expected: Ty<'db>, actual: Ty<'db>, ) -> Result<(), ()> { - let result = self.demand_eqtype_fixme_no_diag(expected, actual); + let result = self + .table + .at(&ObligationCause::new(id)) + .eq(expected, actual) + .map(|infer_ok| self.table.register_infer_ok(infer_ok)); if result.is_err() { - self.result - .type_mismatches - .get_or_insert_default() - .insert(id, TypeMismatch { expected: expected.store(), actual: actual.store() }); + self.emit_type_mismatch(id, expected, actual); } - result + result.map_err(drop) } fn demand_eqtype_fixme_no_diag( @@ -1877,21 +2090,27 @@ impl<'body, 'db> InferenceContext<'body, 'db> { ) -> Result<(), ()> { let result = self .table - .at(&ObligationCause::new()) + .at(&ObligationCause::dummy()) .eq(expected, actual) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); result.map_err(drop) } - fn demand_suptype(&mut self, expected: Ty<'db>, actual: Ty<'db>) { + fn demand_suptype( + &mut self, + id: ExprOrPatId, + expected: Ty<'db>, + actual: Ty<'db>, + ) -> Result<(), ()> { let result = self .table - .at(&ObligationCause::new()) + .at(&ObligationCause::new(id)) .sup(expected, actual) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); - if let Err(_err) = result { - // FIXME: Emit diagnostic. + if result.is_err() { + self.emit_type_mismatch(id, expected, actual); } + result.map_err(drop) } fn demand_coerce( @@ -1902,7 +2121,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { allow_two_phase: AllowTwoPhase, expr_is_read: ExprIsRead, ) -> Ty<'db> { - let result = self.coerce(expr.into(), checked_ty, expected, allow_two_phase, expr_is_read); + let result = self.coerce(expr, checked_ty, expected, allow_two_phase, expr_is_read); if let Err(_err) = result { // FIXME: Emit diagnostic. } @@ -1910,19 +2129,24 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } pub(crate) fn type_must_be_known_at_this_point( - &self, - _id: ExprOrPatId, - _ty: Ty<'db>, + &mut self, + node: ExprOrPatId, + ty: Ty<'db>, ) -> Ty<'db> { - // FIXME: Emit an diagnostic. + if self.vars_emitted_type_must_be_known_for.insert(ty.into()) { + self.push_diagnostic(InferenceDiagnostic::TypeMustBeKnown { + at_point: node.into(), + top_term: None, + }); + } self.types.types.error } - pub(crate) fn require_type_is_sized(&mut self, ty: Ty<'db>) { + pub(crate) fn require_type_is_sized(&mut self, ty: Ty<'db>, span: Span) { if !ty.references_non_lt_error() && let Some(sized_trait) = self.lang_items.Sized { - self.table.register_bound(ty, sized_trait, ObligationCause::new()); + self.table.register_bound(ty, sized_trait, ObligationCause::new(span)); } } @@ -1940,39 +2164,16 @@ impl<'body, 'db> InferenceContext<'body, 'db> { ty.unwrap_or_else(|| self.expr_ty(e)) } - fn resolve_associated_type_with_params( - &mut self, - inner_ty: Ty<'db>, - assoc_ty: Option<TypeAliasId>, - // FIXME(GATs): these are args for the trait ref, args for assoc type itself should be - // handled when we support them. - params: &[GenericArg<'db>], - ) -> Ty<'db> { - match assoc_ty { - Some(res_assoc_ty) => { - let alias = Ty::new_alias( - self.interner(), - AliasTy::new( - self.interner(), - AliasTyKind::Projection { def_id: res_assoc_ty.into() }, - iter::once(inner_ty.into()).chain(params.iter().copied()), - ), - ); - self.table.try_structurally_resolve_type(alias) - } - None => self.err_ty(), - } - } - fn resolve_variant( &mut self, node: ExprOrPatId, - path: Option<&Path>, + path: &Path, value_ns: bool, ) -> (Ty<'db>, Option<VariantId>) { - let path = match path { - Some(path) => path, - None => return (self.err_ty(), None), + let interner = self.interner(); + let mut vars_ctx = InferenceTyLoweringVarsCtx { + table: &mut self.table, + type_of_type_placeholder: &mut self.result.type_of_type_placeholder, }; let mut ctx = TyLoweringContext::new( self.db, @@ -1980,21 +2181,26 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.store, &self.diagnostics, InferenceTyDiagnosticSource::Body, + self.store_owner, self.generic_def, + &self.generics, LifetimeElisionKind::Infer, + self.allow_using_generic_params, + Some(&mut vars_ctx), + &self.defined_anon_consts, ); if let Some(type_anchor) = path.type_anchor() { let mut segments = path.segments(); if segments.is_empty() { - return (self.err_ty(), None); + return (self.types.types.error, None); } let (mut ty, type_ns) = ctx.lower_ty_ext(type_anchor); - ty = self.table.process_user_written_ty(ty); + ty = ctx.expect_table().process_user_written_ty(ty); if let Some(TypeNs::SelfType(impl_)) = type_ns && let Some(trait_ref) = self.db.impl_trait(impl_) - && let trait_ref = trait_ref.instantiate_identity() + && let trait_ref = trait_ref.instantiate_identity().skip_norm_wip() && let Some(assoc_type) = trait_ref .def_id .0 @@ -2002,16 +2208,20 @@ impl<'body, 'db> InferenceContext<'body, 'db> { .associated_type_by_name(segments.first().unwrap().name) { // `<Self>::AssocType` - let args = self.infcx().fill_rest_fresh_args(assoc_type.into(), trait_ref.args); + let args = ctx.expect_table().infer_ctxt.fill_rest_fresh_args( + node.into(), + assoc_type.into(), + trait_ref.args, + ); let alias = Ty::new_alias( - self.interner(), + interner, AliasTy::new_from_args( - self.interner(), + interner, AliasTyKind::Projection { def_id: assoc_type.into() }, args, ), ); - ty = self.table.try_structurally_resolve_type(alias); + ty = ctx.expect_table().try_structurally_resolve_type(node.into(), alias); segments = segments.skip(1); } @@ -2027,15 +2237,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> { segments = segments.skip(1); variant.into() } else { - return (self.err_ty(), None); + return (self.types.types.error, None); } } - None => return (self.err_ty(), None), + None => return (self.types.types.error, None), }; if !segments.is_empty() { // FIXME: Report an error. - return (self.err_ty(), None); + return (self.types.types.error, None); } else { return (ty, Some(variant)); } @@ -2045,31 +2255,34 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let interner = DbInterner::conjure(); let (resolution, unresolved) = if value_ns { let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else { - return (self.err_ty(), None); + return (self.types.types.error, None); }; match res { ResolveValueResult::ValueNs(value) => match value { ValueNs::EnumVariantId(var) => { - let args = path_ctx.substs_from_path(var.into(), true, false); + let args = path_ctx.substs_from_path(var.into(), true, false, node.into()); drop(ctx); let ty = self .db .ty(var.lookup(self.db).parent.into()) - .instantiate(interner, args); + .instantiate(interner, args) + .skip_norm_wip(); let ty = self.insert_type_vars(ty); return (ty, Some(var.into())); } ValueNs::StructId(strukt) => { - let args = path_ctx.substs_from_path(strukt.into(), true, false); + let args = + path_ctx.substs_from_path(strukt.into(), true, false, node.into()); drop(ctx); - let ty = self.db.ty(strukt.into()).instantiate(interner, args); + let ty = + self.db.ty(strukt.into()).instantiate(interner, args).skip_norm_wip(); let ty = self.insert_type_vars(ty); return (ty, Some(strukt.into())); } ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None), _ => { drop(ctx); - return (self.err_ty(), None); + return (self.types.types.error, None); } }, ResolveValueResult::Partial(typens, unresolved) => (typens, Some(unresolved)), @@ -2077,41 +2290,45 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } else { match path_ctx.resolve_path_in_type_ns() { Some((it, idx)) => (it, idx), - None => return (self.err_ty(), None), + None => return (self.types.types.error, None), } }; return match resolution { TypeNs::AdtId(AdtId::StructId(strukt)) => { - let args = path_ctx.substs_from_path(strukt.into(), true, false); + let args = path_ctx.substs_from_path(strukt.into(), true, false, node.into()); drop(ctx); - let ty = self.db.ty(strukt.into()).instantiate(interner, args); + let ty = self.db.ty(strukt.into()).instantiate(interner, args).skip_norm_wip(); let ty = self.insert_type_vars(ty); forbid_unresolved_segments(self, (ty, Some(strukt.into())), unresolved) } TypeNs::AdtId(AdtId::UnionId(u)) => { - let args = path_ctx.substs_from_path(u.into(), true, false); + let args = path_ctx.substs_from_path(u.into(), true, false, node.into()); drop(ctx); - let ty = self.db.ty(u.into()).instantiate(interner, args); + let ty = self.db.ty(u.into()).instantiate(interner, args).skip_norm_wip(); let ty = self.insert_type_vars(ty); forbid_unresolved_segments(self, (ty, Some(u.into())), unresolved) } TypeNs::EnumVariantId(var) => { - let args = path_ctx.substs_from_path(var.into(), true, false); + let args = path_ctx.substs_from_path(var.into(), true, false, node.into()); drop(ctx); - let ty = self.db.ty(var.lookup(self.db).parent.into()).instantiate(interner, args); + let ty = self + .db + .ty(var.lookup(self.db).parent.into()) + .instantiate(interner, args) + .skip_norm_wip(); let ty = self.insert_type_vars(ty); forbid_unresolved_segments(self, (ty, Some(var.into())), unresolved) } TypeNs::SelfType(impl_id) => { - let mut ty = self.db.impl_self_ty(impl_id).instantiate_identity(); + let mut ty = self.db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); let Some(remaining_idx) = unresolved else { drop(ctx); let Some(mod_path) = path.mod_path() else { never!("resolver should always resolve lang item paths"); - return (self.err_ty(), None); + return (self.types.types.error, None); }; - return self.resolve_variant_on_alias(ty, None, mod_path); + return self.resolve_variant_on_alias(node, ty, None, mod_path); }; let mut remaining_segments = path.segments().skip(remaining_idx); @@ -2127,7 +2344,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { // If we can resolve to an enum variant, it takes priority over associated type // of the same name. if let TyKind::Adt(adt_def, _) = ty.kind() - && let AdtId::EnumId(id) = adt_def.def_id().0 + && let AdtId::EnumId(id) = adt_def.def_id() { let enum_data = id.enum_variants(self.db); if let Some(variant) = enum_data.variant(current_segment.name) { @@ -2137,7 +2354,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { // We still have unresolved paths, but enum variants never have // associated types! // FIXME: Report an error. - (self.err_ty(), None) + (self.types.types.error, None) }; } } @@ -2151,12 +2368,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> { // `lower_partly_resolved_path()` returns `None` as type namespace unless // `remaining_segments` is empty, which is never the case here. We don't know // which namespace the new `ty` is in until normalized anyway. - (ty, _) = path_ctx.lower_partly_resolved_path(resolution, true); + (ty, _) = path_ctx.lower_partly_resolved_path(resolution, true, node.into()); tried_resolving_once = true; - ty = self.table.process_user_written_ty(ty); + ty = path_ctx.expect_table().process_user_written_ty(ty); if ty.is_ty_error() { - return (self.err_ty(), None); + return (self.types.types.error, None); } remaining_segments = remaining_segments.skip(1); @@ -2175,7 +2392,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } TypeNs::TraitId(_) => { let Some(remaining_idx) = unresolved else { - return (self.err_ty(), None); + return (self.types.types.error, None); }; let remaining_segments = path.segments().skip(remaining_idx); @@ -2184,8 +2401,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> { path_ctx.ignore_last_segment(); } - let (mut ty, _) = path_ctx.lower_partly_resolved_path(resolution, true); - ty = self.table.process_user_written_ty(ty); + let (mut ty, _) = + path_ctx.lower_partly_resolved_path(resolution, true, node.into()); + ty = ctx.expect_table().process_user_written_ty(ty); if let Some(segment) = remaining_segments.get(1) && let Some((AdtId::EnumId(id), _)) = ty.as_adt() @@ -2198,7 +2416,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { // We still have unresolved paths, but enum variants never have // associated types! // FIXME: Report an error. - (self.err_ty(), None) + (self.types.types.error, None) }; } } @@ -2216,27 +2434,28 @@ impl<'body, 'db> InferenceContext<'body, 'db> { TypeNs::TypeAliasId(it) => { let Some(mod_path) = path.mod_path() else { never!("resolver should always resolve lang item paths"); - return (self.err_ty(), None); + return (self.types.types.error, None); }; - let args = path_ctx.substs_from_path_segment(it.into(), true, None, false); + let args = + path_ctx.substs_from_path_segment(it.into(), true, None, false, node.into()); drop(ctx); let interner = DbInterner::conjure(); - let ty = self.db.ty(it.into()).instantiate(interner, args); + let ty = self.db.ty(it.into()).instantiate(interner, args).skip_norm_wip(); let ty = self.insert_type_vars(ty); - self.resolve_variant_on_alias(ty, unresolved, mod_path) + self.resolve_variant_on_alias(node, ty, unresolved, mod_path) } TypeNs::AdtSelfType(_) => { // FIXME this could happen in array size expressions, once we're checking them - (self.err_ty(), None) + (self.types.types.error, None) } TypeNs::GenericParam(_) => { // FIXME potentially resolve assoc type - (self.err_ty(), None) + (self.types.types.error, None) } TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::ModuleId(_) => { // FIXME diagnostic - (self.err_ty(), None) + (self.types.types.error, None) } }; @@ -2256,12 +2475,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> { fn resolve_variant_on_alias( &mut self, + node: ExprOrPatId, ty: Ty<'db>, unresolved: Option<usize>, path: &ModPath, ) -> (Ty<'db>, Option<VariantId>) { let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0); - let ty = self.table.try_structurally_resolve_type(ty); + let ty = self.table.try_structurally_resolve_type(node.into(), ty); match remaining { None => { let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id { @@ -2293,19 +2513,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } } - fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> { - trait_.trait_items(self.db).associated_type_by_name(&Name::new_symbol_root(sym::Output)) - } - - fn resolve_future_future_output(&self) -> Option<TypeAliasId> { - let ItemContainerId::TraitId(trait_) = - self.lang_items.IntoFutureIntoFuture?.lookup(self.db).container - else { - return None; - }; - self.resolve_output_on(trait_) - } - fn resolve_boxed_box(&self) -> Option<AdtId> { let struct_ = self.lang_items.OwnedBox?; Some(struct_.into()) @@ -2317,7 +2524,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } fn has_new_range_feature(&self) -> bool { - self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range) + self.features.new_range } fn resolve_range(&self) -> Option<AdtId> { @@ -2374,6 +2581,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { Either::Right(&self.traits_in_scope) } } + + fn has_applicable_non_exhaustive(&self, def: AttrDefId) -> bool { + AttrFlags::query(self.db, def).contains(AttrFlags::NON_EXHAUSTIVE) + && def.krate(self.db) != self.krate() + } } /// When inferring an expression, we propagate downward whatever type hint we @@ -2432,7 +2644,7 @@ impl<'db> Expectation<'db> { Expectation::None } - fn resolve(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> { + fn resolve(&self, table: &unify::InferenceTable<'db>) -> Expectation<'db> { match self { Expectation::None => Expectation::None, Expectation::HasType(t) => Expectation::HasType(table.shallow_resolve(*t)), @@ -2443,7 +2655,7 @@ impl<'db> Expectation<'db> { } } - fn to_option(&self, table: &mut unify::InferenceTable<'db>) -> Option<Ty<'db>> { + fn to_option(&self, table: &unify::InferenceTable<'db>) -> Option<Ty<'db>> { match self.resolve(table) { Expectation::None => None, Expectation::HasType(t) @@ -2454,15 +2666,15 @@ impl<'db> Expectation<'db> { fn only_has_type(&self, table: &mut unify::InferenceTable<'db>) -> Option<Ty<'db>> { match self { - Expectation::HasType(t) => Some(table.shallow_resolve(*t)), + Expectation::HasType(t) => Some(table.resolve_vars_if_possible(*t)), Expectation::Castable(_) | Expectation::RValueLikeUnsized(_) | Expectation::None => { None } } } - fn coercion_target_type(&self, table: &mut unify::InferenceTable<'db>) -> Ty<'db> { - self.only_has_type(table).unwrap_or_else(|| table.next_ty_var()) + fn coercion_target_type(&self, table: &mut unify::InferenceTable<'db>, span: Span) -> Ty<'db> { + self.only_has_type(table).unwrap_or_else(|| table.next_ty_var(span)) } /// Comment copied from rustc: @@ -2482,10 +2694,14 @@ impl<'db> Expectation<'db> { /// an expected type. Otherwise, we might write parts of the type /// when checking the 'then' block which are incompatible with the /// 'else' branch. - fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> { + fn adjust_for_branches( + &self, + table: &mut unify::InferenceTable<'db>, + span: Span, + ) -> Expectation<'db> { match *self { Expectation::HasType(ety) => { - let ety = table.structurally_resolve_type(ety); + let ety = table.try_structurally_resolve_type(span, ety); if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) } } Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety), diff --git a/crates/hir-ty/src/infer/autoderef.rs b/crates/hir-ty/src/infer/autoderef.rs index a6c7b2dbb9..0ba3b3dd05 100644 --- a/crates/hir-ty/src/infer/autoderef.rs +++ b/crates/hir-ty/src/infer/autoderef.rs @@ -5,7 +5,7 @@ use std::iter; use rustc_ast_ir::Mutability; use crate::{ - Adjust, Adjustment, OverloadedDeref, + Adjust, Adjustment, OverloadedDeref, Span, autoderef::{Autoderef, AutoderefCtx, AutoderefKind, GeneralAutoderef}, infer::unify::InferenceTable, next_solver::{ @@ -15,12 +15,16 @@ use crate::{ }; impl<'db> InferenceTable<'db> { - pub(crate) fn autoderef(&self, base_ty: Ty<'db>) -> Autoderef<'_, 'db, usize> { - Autoderef::new(&self.infer_ctxt, self.param_env, base_ty) + pub(crate) fn autoderef(&self, base_ty: Ty<'db>, span: Span) -> Autoderef<'_, 'db, usize> { + Autoderef::new(&self.infer_ctxt, self.param_env, base_ty, span) } - pub(crate) fn autoderef_with_tracking(&self, base_ty: Ty<'db>) -> Autoderef<'_, 'db> { - Autoderef::new_with_tracking(&self.infer_ctxt, self.param_env, base_ty) + pub(crate) fn autoderef_with_tracking( + &self, + base_ty: Ty<'db>, + span: Span, + ) -> Autoderef<'_, 'db> { + Autoderef::new_with_tracking(&self.infer_ctxt, self.param_env, base_ty, span) } } diff --git a/crates/hir-ty/src/infer/callee.rs b/crates/hir-ty/src/infer/callee.rs index 3d478912a3..057ba7fa86 100644 --- a/crates/hir-ty/src/infer/callee.rs +++ b/crates/hir-ty/src/infer/callee.rs @@ -2,17 +2,17 @@ use std::iter; -use intern::sym; +use rustc_abi::ExternAbi; use tracing::debug; -use hir_def::{CallableDefId, hir::ExprId, signatures::FunctionSignature}; +use hir_def::{CallableDefId, ConstParamId, hir::ExprId, signatures::FunctionSignature}; use rustc_type_ir::{ InferTy, Interner, inherent::{GenericArgs as _, IntoKind, Ty as _}, }; use crate::{ - Adjust, Adjustment, AutoBorrow, FnAbi, + Adjust, Adjustment, AutoBorrow, autoderef::{GeneralAutoderef, InferenceContextAutoderef}, infer::{ AllowTwoPhase, AutoBorrowMutability, Expectation, InferenceContext, InferenceDiagnostic, @@ -20,7 +20,7 @@ use crate::{ }, method_resolution::{MethodCallee, TreatNotYetDefinedOpaques}, next_solver::{ - FnSig, Ty, TyKind, + ConstKind, FnSig, Ty, TyKind, infer::{BoundRegionConversionTime, traits::ObligationCause}, }, }; @@ -43,13 +43,21 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let original_callee_ty = self.infer_expr_no_expect(callee_expr, ExprIsRead::Yes); - let expr_ty = self.table.try_structurally_resolve_type(original_callee_ty); + let expr_ty = + self.table.try_structurally_resolve_type(callee_expr.into(), original_callee_ty); - let mut autoderef = GeneralAutoderef::new_from_inference_context(self, expr_ty); + let mut autoderef = + GeneralAutoderef::new_from_inference_context(self, expr_ty, callee_expr.into()); let mut result = None; + let mut error_reported = false; while result.is_none() && autoderef.next().is_some() { - result = - Self::try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &mut autoderef); + result = Self::try_overloaded_call_step( + call_expr, + callee_expr, + arg_exprs, + &mut autoderef, + &mut error_reported, + ); } // FIXME: rustc does some ABI checks here, but the ABI mapping is in rustc_target and we don't have access to that crate. @@ -65,16 +73,18 @@ impl<'db> InferenceContext<'_, 'db> { self.infer_expr_no_expect(arg, ExprIsRead::Yes); } - self.push_diagnostic(InferenceDiagnostic::ExpectedFunction { - call_expr, - found: original_callee_ty.store(), - }); + if !error_reported { + self.push_diagnostic(InferenceDiagnostic::ExpectedFunction { + call_expr, + found: original_callee_ty.store(), + }); + } self.types.types.error } Some(CallStep::Builtin(callee_ty)) => { - self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected) + self.confirm_builtin_call(callee_expr, call_expr, callee_ty, arg_exprs, expected) } Some(CallStep::DeferredClosure(_def_id, fn_sig)) => { @@ -87,7 +97,7 @@ impl<'db> InferenceContext<'_, 'db> { }; // we must check that return type of called functions is WF: - self.table.register_wf_obligation(output.into(), ObligationCause::new()); + self.table.register_wf_obligation(output.into(), ObligationCause::new(call_expr)); output } @@ -97,9 +107,11 @@ impl<'db> InferenceContext<'_, 'db> { callee_expr: ExprId, arg_exprs: &[ExprId], autoderef: &mut InferenceContextAutoderef<'_, '_, 'db>, + error_reported: &mut bool, ) -> Option<CallStep<'db>> { let final_ty = autoderef.final_ty(); - let adjusted_ty = autoderef.ctx().table.try_structurally_resolve_type(final_ty); + let adjusted_ty = + autoderef.ctx().table.try_structurally_resolve_type(callee_expr.into(), final_ty); // If the callee is a function pointer or a closure, then we're all set. match adjusted_ty.kind() { @@ -119,12 +131,13 @@ impl<'db> InferenceContext<'_, 'db> { { let closure_sig = args.as_closure().sig(); let closure_sig = autoderef.ctx().infcx().instantiate_binder_with_fresh_vars( + callee_expr.into(), BoundRegionConversionTime::FnCall, closure_sig, ); let adjust_steps = autoderef.adjust_steps_as_infer_ok(); let adjustments = autoderef.ctx().table.register_infer_ok(adjust_steps); - let def_id = def_id.0.loc(autoderef.ctx().db).1; + let def_id = def_id.0.loc(autoderef.ctx().db).expr; autoderef.ctx().record_deferred_call_resolution( def_id, DeferredCallResolution { @@ -149,15 +162,16 @@ impl<'db> InferenceContext<'_, 'db> { let closure_args = args.as_coroutine_closure(); let coroutine_closure_sig = autoderef.ctx().infcx().instantiate_binder_with_fresh_vars( + callee_expr.into(), BoundRegionConversionTime::FnCall, closure_args.coroutine_closure_sig(), ); - let tupled_upvars_ty = autoderef.ctx().table.next_ty_var(); + let tupled_upvars_ty = autoderef.ctx().table.next_ty_var(call_expr.into()); // We may actually receive a coroutine back whose kind is different // from the closure that this dispatched from. This is because when // we have no captures, we automatically implement `FnOnce`. This // impl forces the closure kind to `FnOnce` i.e. `u8`. - let kind_ty = autoderef.ctx().table.next_ty_var(); + let kind_ty = autoderef.ctx().table.next_ty_var(call_expr.into()); let interner = autoderef.ctx().interner(); let call_sig = interner.mk_fn_sig( [coroutine_closure_sig.tupled_inputs_ty], @@ -168,13 +182,13 @@ impl<'db> InferenceContext<'_, 'db> { interner.coroutine_for_closure(def_id), tupled_upvars_ty, ), - coroutine_closure_sig.c_variadic, - coroutine_closure_sig.safety, - coroutine_closure_sig.abi, + coroutine_closure_sig.fn_sig_kind.c_variadic(), + coroutine_closure_sig.fn_sig_kind.safety(), + coroutine_closure_sig.fn_sig_kind.abi(), ); let adjust_steps = autoderef.adjust_steps_as_infer_ok(); let adjustments = autoderef.ctx().table.register_infer_ok(adjust_steps); - let def_id = def_id.0.loc(autoderef.ctx().db).1; + let def_id = def_id.0.loc(autoderef.ctx().db).expr; autoderef.ctx().record_deferred_call_resolution( def_id, DeferredCallResolution { @@ -211,6 +225,7 @@ impl<'db> InferenceContext<'_, 'db> { autoderef .ctx() .type_must_be_known_at_this_point(callee_expr.into(), adjusted_ty); + *error_reported = true; return None; } @@ -230,8 +245,8 @@ impl<'db> InferenceContext<'_, 'db> { // is implemented, and use this information for diagnostic. autoderef .ctx() - .try_overloaded_call_traits(adjusted_ty, Some(arg_exprs)) - .or_else(|| autoderef.ctx().try_overloaded_call_traits(adjusted_ty, None)) + .try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs)) + .or_else(|| autoderef.ctx().try_overloaded_call_traits(call_expr, adjusted_ty, None)) .map(|(autoref, method)| { let adjustments = autoderef.adjust_steps_as_infer_ok(); let mut adjustments = autoderef.ctx().table.register_infer_ok(adjustments); @@ -243,6 +258,7 @@ impl<'db> InferenceContext<'_, 'db> { fn try_overloaded_call_traits( &mut self, + call_expr: ExprId, adjusted_ty: Ty<'db>, opt_arg_exprs: Option<&[ExprId]>, ) -> Option<(Option<Adjustment>, MethodCallee<'db>)> { @@ -258,36 +274,38 @@ impl<'db> InferenceContext<'_, 'db> { // ...or *ideally*, we just have `LendingFn`/`LendingFnMut`, which // would naturally unify these two trait hierarchies in the most // general way. + let call_trait_choices = if self.shallow_resolve(adjusted_ty).is_coroutine_closure() { [ - (self.lang_items.AsyncFn, sym::async_call, true), - (self.lang_items.AsyncFnMut, sym::async_call_mut, true), - (self.lang_items.AsyncFnOnce, sym::async_call_once, false), - (self.lang_items.Fn, sym::call, true), - (self.lang_items.FnMut, sym::call_mut, true), - (self.lang_items.FnOnce, sym::call_once, false), + (self.lang_items.AsyncFn, self.lang_items.AsyncFn_async_call, true), + (self.lang_items.AsyncFnMut, self.lang_items.AsyncFnMut_async_call_mut, true), + (self.lang_items.AsyncFnOnce, self.lang_items.AsyncFnOnce_async_call_once, false), + (self.lang_items.Fn, self.lang_items.Fn_call, true), + (self.lang_items.FnMut, self.lang_items.FnMut_call_mut, true), + (self.lang_items.FnOnce, self.lang_items.FnOnce_call_once, false), ] } else { [ - (self.lang_items.Fn, sym::call, true), - (self.lang_items.FnMut, sym::call_mut, true), - (self.lang_items.FnOnce, sym::call_once, false), - (self.lang_items.AsyncFn, sym::async_call, true), - (self.lang_items.AsyncFnMut, sym::async_call_mut, true), - (self.lang_items.AsyncFnOnce, sym::async_call_once, false), + (self.lang_items.Fn, self.lang_items.Fn_call, true), + (self.lang_items.FnMut, self.lang_items.FnMut_call_mut, true), + (self.lang_items.FnOnce, self.lang_items.FnOnce_call_once, false), + (self.lang_items.AsyncFn, self.lang_items.AsyncFn_async_call, true), + (self.lang_items.AsyncFnMut, self.lang_items.AsyncFnMut_async_call_mut, true), + (self.lang_items.AsyncFnOnce, self.lang_items.AsyncFnOnce_async_call_once, false), ] }; // Try the options that are least restrictive on the caller first. - for (opt_trait_def_id, method_name, borrow) in call_trait_choices { - let Some(trait_def_id) = opt_trait_def_id else { + for (opt_trait_def_id, opt_method_def_id, borrow) in call_trait_choices { + let (Some(trait_def_id), Some(method_def_id)) = (opt_trait_def_id, opt_method_def_id) + else { continue; }; let opt_input_type = opt_arg_exprs.map(|arg_exprs| { Ty::new_tup_from_iter( self.interner(), - arg_exprs.iter().map(|_| self.table.next_ty_var()), + arg_exprs.iter().map(|&arg| self.table.next_ty_var(arg.into())), ) }); @@ -298,9 +316,9 @@ impl<'db> InferenceContext<'_, 'db> { // one which may apply. So if we treat opaques as inference variables // `Box<impl FnOnce()>: Fn` is considered ambiguous and chosen. if let Some(ok) = self.table.lookup_method_for_operator( - ObligationCause::new(), - method_name, + ObligationCause::new(call_expr), trait_def_id, + method_def_id, adjusted_ty, opt_input_type, TreatNotYetDefinedOpaques::AsRigid, @@ -337,12 +355,21 @@ impl<'db> InferenceContext<'_, 'db> { fn check_legacy_const_generics( &mut self, callee: Option<CallableDefId>, + callee_ty: Ty<'db>, args: &[ExprId], ) -> Box<[u32]> { - let func = match callee { - Some(CallableDefId::FunctionId(func)) => func, + let (func, fn_generic_args) = match (callee, callee_ty.kind()) { + (Some(CallableDefId::FunctionId(func)), TyKind::FnDef(_, fn_generic_args)) => { + (func, fn_generic_args) + } _ => return Default::default(), }; + let generics = crate::generics::generics(self.db, func.into()); + let const_params = generics + .iter_self_type_or_consts() + .filter(|(_, param_data)| param_data.const_param().is_some()) + .map(|(id, _)| ConstParamId::from_unchecked(id)) + .collect::<Vec<_>>(); let data = FunctionSignature::of(self.db, func); let Some(legacy_const_generics_indices) = data.legacy_const_generics_indices(self.db, func) @@ -364,11 +391,29 @@ impl<'db> InferenceContext<'_, 'db> { } // check legacy const parameters - for arg_idx in legacy_const_generics_indices.iter().copied() { + for (const_idx, arg_idx) in legacy_const_generics_indices.iter().copied().enumerate() { if arg_idx >= args.len() as u32 { continue; } - let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly + + if let Some(const_arg) = fn_generic_args.get(const_idx).and_then(|it| it.konst()) + && let ConstKind::Infer(_) = const_arg.kind() + { + // Instantiate the generic arg with an error type, to prevent errors from it. + // FIXME: Actually lower the expression as const. + _ = self + .table + .at(&ObligationCause::dummy()) + .eq(self.types.consts.error, const_arg) + .map(|infer_ok| self.table.register_infer_ok(infer_ok)); + } + + let expected = if let Some(&const_param) = const_params.get(const_idx) { + Expectation::has_type(self.db.const_param_ty(const_param)) + } else { + Expectation::None + }; + self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes); // FIXME: evaluate and unify with the const } @@ -378,6 +423,7 @@ impl<'db> InferenceContext<'_, 'db> { fn confirm_builtin_call( &mut self, + callee_expr: ExprId, call_expr: ExprId, callee_ty: Ty<'db>, arg_exprs: &[ExprId], @@ -385,8 +431,11 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let (fn_sig, def_id) = match callee_ty.kind() { TyKind::FnDef(def_id, args) => { - let fn_sig = - self.db.callable_item_signature(def_id.0).instantiate(self.interner(), args); + let fn_sig = self + .db + .callable_item_signature(def_id.0) + .instantiate(self.interner(), args) + .skip_norm_wip(); (fn_sig, Some(def_id.0)) } @@ -401,11 +450,13 @@ impl<'db> InferenceContext<'_, 'db> { // renormalize the associated types at this point, since they // previously appeared within a `Binder<>` and hence would not // have been normalized before. - let fn_sig = self - .infcx() - .instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, fn_sig); + let fn_sig = self.infcx().instantiate_binder_with_fresh_vars( + callee_expr.into(), + BoundRegionConversionTime::FnCall, + fn_sig, + ); - let indices_to_skip = self.check_legacy_const_generics(def_id, arg_exprs); + let indices_to_skip = self.check_legacy_const_generics(def_id, callee_ty, arg_exprs); self.check_call_arguments( call_expr, fn_sig.inputs(), @@ -413,16 +464,17 @@ impl<'db> InferenceContext<'_, 'db> { expected, arg_exprs, &indices_to_skip, - fn_sig.c_variadic, + fn_sig.c_variadic(), TupleArgumentsFlag::DontTupleArguments, ); - if fn_sig.abi == FnAbi::RustCall + if fn_sig.abi() == ExternAbi::RustCall && let Some(ty) = fn_sig.inputs().last().copied() && let Some(tuple_trait) = self.lang_items.Tuple { - self.table.register_bound(ty, tuple_trait, ObligationCause::new()); - self.require_type_is_sized(ty); + let span = arg_exprs.last().copied().unwrap_or(call_expr); + self.table.register_bound(ty, tuple_trait, ObligationCause::new(span)); + self.require_type_is_sized(ty, span.into()); } fn_sig.output() @@ -446,7 +498,7 @@ impl<'db> InferenceContext<'_, 'db> { expected, arg_exprs, &[], - fn_sig.c_variadic, + fn_sig.c_variadic(), TupleArgumentsFlag::TupleArguments, ); @@ -467,7 +519,7 @@ impl<'db> InferenceContext<'_, 'db> { expected, arg_exprs, &[], - method.sig.c_variadic, + method.sig.c_variadic(), TupleArgumentsFlag::TupleArguments, ); @@ -495,7 +547,7 @@ impl<'a, 'db> DeferredCallResolution<'db> { assert!(ctx.infcx().closure_kind(self.closure_ty).is_some()); // We may now know enough to figure out fn vs fnmut etc. - match ctx.try_overloaded_call_traits(self.closure_ty, None) { + match ctx.try_overloaded_call_traits(self.call_expr, self.closure_ty, None) { Some((autoref, method_callee)) => { // One problem is that when we get here, we are going // to have a newly instantiated function signature diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs index e5ee734474..93aed344d4 100644 --- a/crates/hir-ty/src/infer/cast.rs +++ b/crates/hir-ty/src/infer/cast.rs @@ -10,7 +10,7 @@ use rustc_hash::FxHashSet; use rustc_type_ir::{ InferTy, TypeVisitableExt, UintTy, elaborate, error::TypeError, - inherent::{AdtDef, BoundExistentialPredicates as _, IntoKind, Ty as _}, + inherent::{BoundExistentialPredicates as _, IntoKind, Ty as _}, }; use stdx::never; @@ -125,13 +125,14 @@ impl<'db> CastCheck<'db> { &mut self, ctx: &mut InferenceContext<'_, 'db>, ) -> Result<(), InferenceDiagnostic> { - self.expr_ty = ctx.table.try_structurally_resolve_type(self.expr_ty); - self.cast_ty = ctx.table.try_structurally_resolve_type(self.cast_ty); + self.expr_ty = + ctx.table.try_structurally_resolve_type(self.source_expr.into(), self.expr_ty); + self.cast_ty = ctx.table.try_structurally_resolve_type(self.expr.into(), self.cast_ty); // This should always come first so that we apply the coercion, which impacts infer vars. if ctx .coerce( - self.source_expr.into(), + self.source_expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No, @@ -147,7 +148,8 @@ impl<'db> CastCheck<'db> { return Ok(()); } - if !self.cast_ty.has_infer_types() && !ctx.table.is_sized(self.cast_ty) { + if !self.cast_ty.has_infer_types() && !ctx.table.type_is_sized_modulo_regions(self.cast_ty) + { return Err(InferenceDiagnostic::CastToUnsized { expr: self.expr, cast_ty: self.cast_ty.store(), @@ -167,7 +169,7 @@ impl<'db> CastCheck<'db> { let sig = self.expr_ty.fn_sig(ctx.interner()); let fn_ptr = Ty::new_fn_ptr(ctx.interner(), sig); match ctx.coerce( - self.source_expr.into(), + self.source_expr, self.expr_ty, fn_ptr, AllowTwoPhase::No, @@ -198,8 +200,9 @@ impl<'db> CastCheck<'db> { }, // array-ptr-cast CastTy::Ptr(t, m) => { - let t = ctx.table.try_structurally_resolve_type(t); - if !ctx.table.is_sized(t) { + let t = + ctx.table.try_structurally_resolve_type(self.expr.into(), t); + if !ctx.table.type_is_sized_modulo_regions(t) { return Err(CastError::IllegalCast); } self.check_ref_cast(ctx, inner_ty, mutbl, t, m) @@ -261,8 +264,8 @@ impl<'db> CastCheck<'db> { t_cast: Ty<'db>, m_cast: Mutability, ) -> Result<(), CastError> { - let t_expr = ctx.table.try_structurally_resolve_type(t_expr); - let t_cast = ctx.table.try_structurally_resolve_type(t_cast); + let t_expr = ctx.table.try_structurally_resolve_type(self.expr.into(), t_expr); + let t_cast = ctx.table.try_structurally_resolve_type(self.expr.into(), t_cast); if m_expr >= m_cast && let TyKind::Array(ety, _) = t_expr.kind() @@ -275,7 +278,7 @@ impl<'db> CastCheck<'db> { let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr); if ctx .coerce( - self.source_expr.into(), + self.source_expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, @@ -305,8 +308,8 @@ impl<'db> CastCheck<'db> { src: Ty<'db>, dst: Ty<'db>, ) -> Result<(), CastError> { - let src_kind = pointer_kind(src, ctx).map_err(|_| CastError::Unknown)?; - let dst_kind = pointer_kind(dst, ctx).map_err(|_| CastError::Unknown)?; + let src_kind = pointer_kind(self.expr, src, ctx).map_err(|_| CastError::Unknown)?; + let dst_kind = pointer_kind(self.expr, dst, ctx).map_err(|_| CastError::Unknown)?; match (src_kind, dst_kind) { (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()), @@ -371,7 +374,7 @@ impl<'db> CastCheck<'db> { // This is `fcx.demand_eqtype`, but inlined to give a better error. if ctx .table - .at(&ObligationCause::dummy()) + .at(&ObligationCause::new(self.expr)) .eq(src_obj, dst_obj) .map(|infer_ok| ctx.table.register_infer_ok(infer_ok)) .is_err() @@ -456,7 +459,7 @@ impl<'db> CastCheck<'db> { ctx: &mut InferenceContext<'_, 'db>, expr_ty: Ty<'db>, ) -> Result<(), CastError> { - match pointer_kind(expr_ty, ctx).map_err(|_| CastError::Unknown)? { + match pointer_kind(self.expr, expr_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownExprPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -470,7 +473,7 @@ impl<'db> CastCheck<'db> { ctx: &mut InferenceContext<'_, 'db>, cast_ty: Ty<'db>, ) -> Result<(), CastError> { - match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { + match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -486,7 +489,7 @@ impl<'db> CastCheck<'db> { ctx: &mut InferenceContext<'_, 'db>, cast_ty: Ty<'db>, ) -> Result<(), CastError> { - match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { + match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -515,12 +518,13 @@ enum PointerKind<'db> { } fn pointer_kind<'db>( + expr: ExprId, ty: Ty<'db>, ctx: &mut InferenceContext<'_, 'db>, ) -> Result<Option<PointerKind<'db>>, ()> { - let ty = ctx.table.try_structurally_resolve_type(ty); + let ty = ctx.table.try_structurally_resolve_type(expr.into(), ty); - if ctx.table.is_sized(ty) { + if ctx.table.type_is_sized_modulo_regions(ty) { return Ok(Some(PointerKind::Thin)); } @@ -528,7 +532,7 @@ fn pointer_kind<'db>( TyKind::Slice(_) | TyKind::Str => Ok(Some(PointerKind::Length)), TyKind::Dynamic(bounds, _) => Ok(Some(PointerKind::VTable(bounds))), TyKind::Adt(adt_def, subst) => { - let id = adt_def.def_id().0; + let id = adt_def.def_id(); let AdtId::StructId(id) = id else { never!("`{:?}` should be sized but is not?", ty); return Err(()); @@ -538,15 +542,16 @@ fn pointer_kind<'db>( if let Some((last_field, _)) = struct_data.fields().iter().last() { let last_field_ty = ctx.db.field_types(id.into())[last_field] .get() - .instantiate(ctx.interner(), subst); - pointer_kind(last_field_ty, ctx) + .instantiate(ctx.interner(), subst) + .skip_norm_wip(); + pointer_kind(expr, last_field_ty, ctx) } else { Ok(Some(PointerKind::Thin)) } } TyKind::Tuple(subst) => match subst.iter().next_back() { None => Ok(Some(PointerKind::Thin)), - Some(ty) => pointer_kind(ty, ctx), + Some(ty) => pointer_kind(expr, ty, ctx), }, TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)), TyKind::Alias(..) => Ok(Some(PointerKind::OfAlias)), diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 2207bc37e8..ab111736d5 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -5,25 +5,27 @@ pub(crate) mod analysis; use std::{iter, mem, ops::ControlFlow}; use hir_def::{ - TraitId, - hir::{ClosureKind, CoroutineSource, ExprId, PatId}, + AdtId, TraitId, + hir::{ClosureKind, CoroutineKind, CoroutineSource, ExprId, PatId}, type_ref::TypeRefId, }; +use rustc_abi::ExternAbi; use rustc_type_ir::{ - ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, CoroutineClosureArgs, - CoroutineClosureArgsParts, Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, + AliasTyKind, ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, + CoroutineClosureArgs, CoroutineClosureArgsParts, InferTy, Interner, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, inherent::{BoundExistentialPredicates, GenericArgs as _, IntoKind, Ty as _}, }; -use tracing::debug; +use tracing::{debug, instrument}; use crate::{ - FnAbi, + Span, db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId}, - infer::{BreakableKind, Diverges, coerce::CoerceMany}, + infer::{BreakableKind, Diverges, coerce::CoerceMany, pat::PatOrigin}, next_solver::{ - AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig, - PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind, + AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArg, PolyFnSig, + PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, TermId, Ty, TyKind, + Unnormalized, abi::Safety, infer::{ BoundRegionConversionTime, InferOk, InferResult, @@ -46,6 +48,22 @@ struct ClosureSignatures<'db> { } impl<'db> InferenceContext<'_, 'db> { + fn poll_option_ty(&mut self, item_ty: Ty<'db>) -> Ty<'db> { + let interner = self.interner(); + + let (Some(option), Some(poll)) = (self.lang_items.Option, self.lang_items.Poll) else { + return self.types.types.error; + }; + + let option_ty = Ty::new_adt( + interner, + AdtId::EnumId(option), + interner.mk_args(&[GenericArg::from(item_ty)]), + ); + + Ty::new_adt(interner, AdtId::EnumId(poll), interner.mk_args(&[GenericArg::from(option_ty)])) + } + pub(super) fn infer_closure( &mut self, body: ExprId, @@ -62,23 +80,31 @@ impl<'db> InferenceContext<'_, 'db> { // It's always helpful for inference if we know the kind of // closure sooner rather than later, so first examine the expected // type, and see if can glean a closure kind from there. - let (expected_sig, expected_kind) = match expected.to_option(&mut self.table) { + let (expected_sig, expected_kind) = match expected.to_option(&self.table) { Some(ty) => { - let ty = self.table.try_structurally_resolve_type(ty); - self.deduce_closure_signature(ty, closure_kind) + let ty = self.table.try_structurally_resolve_type(closure_expr.into(), ty); + self.deduce_closure_signature(closure_expr, ty, closure_kind) } None => (None, None), }; - let ClosureSignatures { bound_sig, mut liberated_sig } = - self.sig_of_closure(arg_types, ret_type, expected_sig); + let ClosureSignatures { bound_sig, mut liberated_sig } = self.sig_of_closure( + closure_expr, + args, + arg_types, + ret_type, + expected_sig, + closure_kind, + ); debug!(?bound_sig, ?liberated_sig); - let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into()); + let parent_args = self.identity_args(); - let tupled_upvars_ty = self.table.next_ty_var(); + let tupled_upvars_ty = self.table.next_ty_var(closure_expr.into()); + let closure_loc = + InternedClosure { owner: self.owner, expr: closure_expr, kind: closure_kind }; // FIXME: We could probably actually just unify this further -- // instead of having a `FnSig` and a `Option<CoroutineTypes>`, // we can have a `ClosureSignature { Coroutine { .. }, Closure { .. } }`, @@ -91,9 +117,9 @@ impl<'db> InferenceContext<'_, 'db> { interner.mk_fn_sig( [Ty::new_tup(interner, sig.inputs())], sig.output(), - sig.c_variadic, - sig.safety, - sig.abi, + sig.c_variadic(), + sig.safety(), + sig.abi(), ) }); @@ -103,7 +129,7 @@ impl<'db> InferenceContext<'_, 'db> { Some(kind) => Ty::from_closure_kind(interner, kind), // Create a type variable (for now) to represent the closure kind. // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.table.next_ty_var(), + None => self.table.next_ty_var(closure_expr.into()), }; let closure_args = ClosureArgs::new( @@ -116,15 +142,26 @@ impl<'db> InferenceContext<'_, 'db> { }, ); - let closure_id = - InternedClosureId::new(self.db, InternedClosure(self.owner, closure_expr)); + let closure_id = InternedClosureId::new(self.db, closure_loc); (Ty::new_closure(interner, closure_id.into(), closure_args.args), None) } - ClosureKind::Coroutine(_) | ClosureKind::AsyncBlock { .. } => { + ClosureKind::OldCoroutine(_) | ClosureKind::Coroutine { .. } => { let yield_ty = match closure_kind { - ClosureKind::Coroutine(_) => self.table.next_ty_var(), - ClosureKind::AsyncBlock { .. } => self.types.types.unit, + ClosureKind::OldCoroutine(_) + | ClosureKind::Coroutine { kind: CoroutineKind::Gen, .. } => { + let yield_ty = self.table.next_ty_var(closure_expr.into()); + self.require_type_is_sized(yield_ty, closure_expr.into()); + yield_ty + } + ClosureKind::Coroutine { kind: CoroutineKind::Async, .. } => { + self.types.types.unit + } + ClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. } => { + let yield_ty = self.table.next_ty_var(closure_expr.into()); + self.require_type_is_sized(yield_ty, closure_expr.into()); + self.poll_option_ty(yield_ty) + } _ => unreachable!(), }; @@ -137,8 +174,8 @@ impl<'db> InferenceContext<'_, 'db> { // later during upvar analysis. Regular coroutines always have the kind // ty of `().` let kind_ty = match closure_kind { - ClosureKind::AsyncBlock { source: CoroutineSource::Closure } => { - self.table.next_ty_var() + ClosureKind::Coroutine { source: CoroutineSource::Closure, .. } => { + self.table.next_ty_var(closure_expr.into()) } _ => self.types.types.unit, }; @@ -155,31 +192,39 @@ impl<'db> InferenceContext<'_, 'db> { }, ); - let coroutine_id = - InternedCoroutineId::new(self.db, InternedClosure(self.owner, closure_expr)); + let coroutine_id = InternedCoroutineId::new(self.db, closure_loc); ( Ty::new_coroutine(interner, coroutine_id.into(), coroutine_args.args), Some((resume_ty, yield_ty)), ) } - ClosureKind::AsyncClosure => { - // async closures always return the type ascribed after the `->` (if present), - // and yield `()`. - let (bound_return_ty, bound_yield_ty) = - (bound_sig.skip_binder().output(), self.types.types.unit); + ClosureKind::CoroutineClosure(coroutine_kind) => { + let (bound_return_ty, bound_yield_ty) = match coroutine_kind { + CoroutineKind::Gen => { + (self.types.types.unit, self.table.next_ty_var(closure_expr.into())) + } + CoroutineKind::Async => { + (bound_sig.skip_binder().output(), self.types.types.unit) + } + CoroutineKind::AsyncGen => { + let yield_ty = self.table.next_ty_var(closure_expr.into()); + (self.types.types.unit, self.poll_option_ty(yield_ty)) + } + }; + // Compute all of the variables that will be used to populate the coroutine. - let resume_ty = self.table.next_ty_var(); + let resume_ty = self.table.next_ty_var(closure_expr.into()); let closure_kind_ty = match expected_kind { Some(kind) => Ty::from_closure_kind(interner, kind), // Create a type variable (for now) to represent the closure kind. // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.table.next_ty_var(), + None => self.table.next_ty_var(closure_expr.into()), }; - let coroutine_captures_by_ref_ty = self.table.next_ty_var(); + let coroutine_captures_by_ref_ty = self.table.next_ty_var(closure_expr.into()); let closure_args = CoroutineClosureArgs::new( interner, @@ -198,9 +243,9 @@ impl<'db> InferenceContext<'_, 'db> { ), ], Ty::new_tup(interner, &[bound_yield_ty, bound_return_ty]), - sig.c_variadic, - sig.safety, - sig.abi, + sig.c_variadic(), + sig.safety(), + sig.abi(), ) }), ), @@ -214,15 +259,12 @@ impl<'db> InferenceContext<'_, 'db> { // Create a type variable (for now) to represent the closure kind. // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.table.next_ty_var(), + None => self.table.next_ty_var(closure_expr.into()), }; - let coroutine_upvars_ty = self.table.next_ty_var(); + let coroutine_upvars_ty = self.table.next_ty_var(closure_expr.into()); - let coroutine_closure_id = InternedCoroutineClosureId::new( - self.db, - InternedClosure(self.owner, closure_expr), - ); + let coroutine_closure_id = InternedCoroutineClosureId::new(self.db, closure_loc); // We need to turn the liberated signature that we got from HIR, which // looks something like `|Args...| -> T`, into a signature that is suitable @@ -245,9 +287,9 @@ impl<'db> InferenceContext<'_, 'db> { liberated_sig = interner.mk_fn_sig( liberated_sig.inputs().iter().copied(), coroutine_output_ty, - liberated_sig.c_variadic, - liberated_sig.safety, - liberated_sig.abi, + liberated_sig.c_variadic(), + liberated_sig.safety(), + liberated_sig.abi(), ); ( @@ -263,7 +305,7 @@ impl<'db> InferenceContext<'_, 'db> { // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(bound_sig.skip_binder().inputs()) { - self.infer_top_pat(*arg_pat, *arg_ty, None); + self.infer_top_pat(*arg_pat, *arg_ty, PatOrigin::Param); } // FIXME: lift these out into a struct @@ -316,24 +358,27 @@ impl<'db> InferenceContext<'_, 'db> { /// are about to type check: fn deduce_closure_signature( &mut self, + closure_expr: ExprId, expected_ty: Ty<'db>, closure_kind: ClosureKind, ) -> (Option<PolyFnSig<'db>>, Option<rustc_type_ir::ClosureKind>) { match expected_ty.kind() { TyKind::Alias(AliasTy { kind: rustc_type_ir::Opaque { def_id }, args, .. }) => self .deduce_closure_signature_from_predicates( + closure_expr, expected_ty, closure_kind, def_id - .expect_opaque_ty() + .0 .predicates(self.db) .iter_instantiated_copied(self.interner(), args.as_slice()) + .map(Unnormalized::skip_norm_wip) .map(|clause| clause.as_predicate()), ), TyKind::Dynamic(object_type, ..) => { let sig = object_type.projection_bounds().into_iter().find_map(|pb| { let pb = pb.with_self_ty(self.interner(), Ty::new_unit(self.interner())); - self.deduce_sig_from_projection(closure_kind, pb) + self.deduce_sig_from_projection(closure_expr, closure_kind, pb) }); let kind = object_type .principal_def_id() @@ -342,6 +387,7 @@ impl<'db> InferenceContext<'_, 'db> { } TyKind::Infer(rustc_type_ir::TyVar(vid)) => self .deduce_closure_signature_from_predicates( + closure_expr, Ty::new_var(self.interner(), self.table.infer_ctxt.root_var(vid)), closure_kind, self.table.obligations_for_self_ty(vid).into_iter().map(|obl| obl.predicate), @@ -351,9 +397,9 @@ impl<'db> InferenceContext<'_, 'db> { let expected_sig = sig_tys.with(hdr); (Some(expected_sig), Some(rustc_type_ir::ClosureKind::Fn)) } - ClosureKind::Coroutine(_) - | ClosureKind::AsyncClosure - | ClosureKind::AsyncBlock { .. } => (None, None), + ClosureKind::OldCoroutine(_) + | ClosureKind::Coroutine { .. } + | ClosureKind::CoroutineClosure(_) => (None, None), }, _ => (None, None), } @@ -361,6 +407,7 @@ impl<'db> InferenceContext<'_, 'db> { fn deduce_closure_signature_from_predicates( &mut self, + closure_expr: ExprId, expected_ty: Ty<'db>, closure_kind: ClosureKind, predicates: impl DoubleEndedIterator<Item = Predicate<'db>>, @@ -388,6 +435,7 @@ impl<'db> InferenceContext<'_, 'db> { bound_predicate.skip_binder() { let inferred_sig = self.deduce_sig_from_projection( + closure_expr, closure_kind, bound_predicate.rebind(proj_predicate), ); @@ -430,18 +478,17 @@ impl<'db> InferenceContext<'_, 'db> { // This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty` // even though the normalized form may not name `expected_ty`. However, this matches the existing // behaviour of the old solver and would be technically a breaking change to fix. - let generalized_fnptr_sig = self.table.next_ty_var(); + let generalized_fnptr_sig = self.table.next_ty_var(closure_expr.into()); let inferred_fnptr_sig = Ty::new_fn_ptr(self.interner(), inferred_sig); // FIXME: Report diagnostics. _ = self .table .infer_ctxt - .at(&ObligationCause::new(), self.table.param_env) + .at(&ObligationCause::new(closure_expr), self.table.param_env) .eq(inferred_fnptr_sig, generalized_fnptr_sig) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); - let resolved_sig = - self.table.infer_ctxt.resolve_vars_if_possible(generalized_fnptr_sig); + let resolved_sig = self.resolve_vars_if_possible(generalized_fnptr_sig); if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { expected_sig = Some(resolved_sig.fn_sig(self.interner())); @@ -465,8 +512,10 @@ impl<'db> InferenceContext<'_, 'db> { if let Some(trait_def_id) = trait_def_id { let found_kind = match closure_kind { - ClosureKind::Closure => self.fn_trait_kind_from_def_id(trait_def_id), - ClosureKind::AsyncClosure => self + ClosureKind::Closure | ClosureKind::CoroutineClosure(CoroutineKind::Gen) => { + self.fn_trait_kind_from_def_id(trait_def_id) + } + ClosureKind::CoroutineClosure(CoroutineKind::Async) => self .async_fn_trait_kind_from_def_id(trait_def_id) .or_else(|| self.fn_trait_kind_from_def_id(trait_def_id)), _ => None, @@ -501,6 +550,7 @@ impl<'db> InferenceContext<'_, 'db> { /// know that. fn deduce_sig_from_projection( &mut self, + closure_expr: ExprId, closure_kind: ClosureKind, projection: PolyProjectionPredicate<'db>, ) -> Option<PolyFnSig<'db>> { @@ -512,14 +562,18 @@ impl<'db> InferenceContext<'_, 'db> { ClosureKind::Closure if Some(def_id) == self.lang_items.FnOnceOutput => { self.extract_sig_from_projection(projection) } - ClosureKind::AsyncClosure if Some(def_id) == self.lang_items.AsyncFnOnceOutput => { + ClosureKind::CoroutineClosure(CoroutineKind::Async) + if Some(def_id) == self.lang_items.AsyncFnOnceOutput => + { self.extract_sig_from_projection(projection) } // It's possible we've passed the closure to a (somewhat out-of-fashion) // `F: FnOnce() -> Fut, Fut: Future<Output = T>` style bound. Let's still // guide inference here, since it's beneficial for the user. - ClosureKind::AsyncClosure if Some(def_id) == self.lang_items.FnOnceOutput => { - self.extract_sig_from_projection_and_future_bound(projection) + ClosureKind::CoroutineClosure(CoroutineKind::Async) + if Some(def_id) == self.lang_items.FnOnceOutput => + { + self.extract_sig_from_projection_and_future_bound(closure_expr, projection) } _ => None, } @@ -531,7 +585,7 @@ impl<'db> InferenceContext<'_, 'db> { &self, projection: PolyProjectionPredicate<'db>, ) -> Option<PolyFnSig<'db>> { - let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection); + let projection = self.resolve_vars_if_possible(projection); let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); debug!(?arg_param_ty); @@ -574,9 +628,10 @@ impl<'db> InferenceContext<'_, 'db> { /// projection, and the output will be an unconstrained type variable instead. fn extract_sig_from_projection_and_future_bound( &mut self, + closure_expr: ExprId, projection: PolyProjectionPredicate<'db>, ) -> Option<PolyFnSig<'db>> { - let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection); + let projection = self.resolve_vars_if_possible(projection); let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); debug!(?arg_param_ty); @@ -603,7 +658,7 @@ impl<'db> InferenceContext<'_, 'db> { bound.predicate.kind().skip_binder() && let ret_projection = bound.predicate.kind().rebind(ret_projection) && let Some(ret_projection) = ret_projection.no_bound_vars() - && let SolverDefId::TypeAliasId(assoc_type) = ret_projection.def_id() + && let TermId::TypeAliasId(assoc_type) = ret_projection.def_id().0 && Some(assoc_type) == self.lang_items.FutureOutput { return_ty = Some(ret_projection.term.expect_type()); @@ -625,7 +680,7 @@ impl<'db> InferenceContext<'_, 'db> { // // FIXME: We probably should store this signature inference output in a way // that does not misuse a `FnSig` type, but that can be done separately. - let return_ty = return_ty.unwrap_or_else(|| self.table.next_ty_var()); + let return_ty = return_ty.unwrap_or_else(|| self.table.next_ty_var(closure_expr.into())); let sig = projection.rebind(self.interner().mk_fn_sig_safe_rust_abi(input_tys, return_ty)); @@ -634,14 +689,29 @@ impl<'db> InferenceContext<'_, 'db> { fn sig_of_closure( &mut self, - decl_inputs: &[Option<TypeRefId>], - decl_output: Option<TypeRefId>, + closure_expr: ExprId, + decl_inputs: &[PatId], + decl_input_tys: &[Option<TypeRefId>], + decl_output_ty: Option<TypeRefId>, expected_sig: Option<PolyFnSig<'db>>, + closure_kind: ClosureKind, ) -> ClosureSignatures<'db> { if let Some(e) = expected_sig { - self.sig_of_closure_with_expectation(decl_inputs, decl_output, e) + self.sig_of_closure_with_expectation( + closure_expr, + decl_inputs, + decl_input_tys, + decl_output_ty, + e, + closure_kind, + ) } else { - self.sig_of_closure_no_expectation(decl_inputs, decl_output) + self.sig_of_closure_no_expectation( + closure_expr, + decl_input_tys, + decl_output_ty, + closure_kind, + ) } } @@ -649,10 +719,13 @@ impl<'db> InferenceContext<'_, 'db> { /// types that the user gave into a signature. fn sig_of_closure_no_expectation( &mut self, + closure_expr: ExprId, decl_inputs: &[Option<TypeRefId>], decl_output: Option<TypeRefId>, + closure_kind: ClosureKind, ) -> ClosureSignatures<'db> { - let bound_sig = self.supplied_sig_of_closure(decl_inputs, decl_output); + let bound_sig = + self.supplied_sig_of_closure(closure_expr, decl_inputs, decl_output, closure_kind); self.closure_sigs(bound_sig) } @@ -706,18 +779,28 @@ impl<'db> InferenceContext<'_, 'db> { /// regions with depth 1, which are bound then by the closure. fn sig_of_closure_with_expectation( &mut self, - decl_inputs: &[Option<TypeRefId>], - decl_output: Option<TypeRefId>, + closure_expr: ExprId, + decl_inputs: &[PatId], + decl_input_tys: &[Option<TypeRefId>], + decl_output_ty: Option<TypeRefId>, expected_sig: PolyFnSig<'db>, + closure_kind: ClosureKind, ) -> ClosureSignatures<'db> { // Watch out for some surprises and just ignore the // expectation if things don't see to match up with what we // expect. if expected_sig.c_variadic() { - return self.sig_of_closure_no_expectation(decl_inputs, decl_output); - } else if expected_sig.skip_binder().inputs_and_output.len() != decl_inputs.len() + 1 { - return self - .sig_of_closure_with_mismatched_number_of_arguments(decl_inputs, decl_output); + return self.sig_of_closure_no_expectation( + closure_expr, + decl_input_tys, + decl_output_ty, + closure_kind, + ); + } else if expected_sig.skip_binder().inputs_and_output.len() != decl_input_tys.len() + 1 { + return self.sig_of_closure_with_mismatched_number_of_arguments( + decl_input_tys, + decl_output_ty, + ); } // Create a `PolyFnSig`. Note the oddity that late bound @@ -728,9 +811,9 @@ impl<'db> InferenceContext<'_, 'db> { self.interner().mk_fn_sig( sig.inputs().iter().copied(), sig.output(), - sig.c_variadic, + sig.c_variadic(), Safety::Safe, - FnAbi::RustCall, + ExternAbi::RustCall, ) }); @@ -746,9 +829,21 @@ impl<'db> InferenceContext<'_, 'db> { // Along the way, it also writes out entries for types that the user // wrote into our typeck results, which are then later used by the privacy // check. - match self.merge_supplied_sig_with_expectation(decl_inputs, decl_output, closure_sigs) { + match self.merge_supplied_sig_with_expectation( + closure_expr, + decl_inputs, + decl_input_tys, + decl_output_ty, + closure_sigs, + closure_kind, + ) { Ok(infer_ok) => self.table.register_infer_ok(infer_ok), - Err(_) => self.sig_of_closure_no_expectation(decl_inputs, decl_output), + Err(_) => self.sig_of_closure_no_expectation( + closure_expr, + decl_input_tys, + decl_output_ty, + closure_kind, + ), } } @@ -767,15 +862,23 @@ impl<'db> InferenceContext<'_, 'db> { /// strategy. fn merge_supplied_sig_with_expectation( &mut self, - decl_inputs: &[Option<TypeRefId>], - decl_output: Option<TypeRefId>, + closure_expr: ExprId, + decl_inputs: &[PatId], + decl_input_tys: &[Option<TypeRefId>], + decl_output_ty: Option<TypeRefId>, mut expected_sigs: ClosureSignatures<'db>, + closure_kind: ClosureKind, ) -> InferResult<'db, ClosureSignatures<'db>> { // Get the signature S that the user gave. // // (See comment on `sig_of_closure_with_expectation` for the // meaning of these letters.) - let supplied_sig = self.supplied_sig_of_closure(decl_inputs, decl_output); + let supplied_sig = self.supplied_sig_of_closure( + closure_expr, + decl_input_tys, + decl_output_ty, + closure_kind, + ); debug!(?supplied_sig); @@ -796,25 +899,28 @@ impl<'db> InferenceContext<'_, 'db> { self.table.commit_if_ok(|table| { let mut all_obligations = PredicateObligations::new(); let supplied_sig = table.infer_ctxt.instantiate_binder_with_fresh_vars( + closure_expr.into(), BoundRegionConversionTime::FnCall, supplied_sig, ); // The liberated version of this signature should be a subtype // of the liberated form of the expectation. - for (supplied_ty, expected_ty) in iter::zip( - supplied_sig.inputs().iter().copied(), + for ((decl_input, supplied_ty), expected_ty) in iter::zip( + iter::zip(decl_inputs, supplied_sig.inputs().iter().copied()), expected_sigs.liberated_sig.inputs().iter().copied(), ) { // Check that E' = S'. - let cause = ObligationCause::new(); + let cause = ObligationCause::new(*decl_input); let InferOk { value: (), obligations } = table.infer_ctxt.at(&cause, table.param_env).eq(expected_ty, supplied_ty)?; all_obligations.extend(obligations); } let supplied_output_ty = supplied_sig.output(); - let cause = ObligationCause::new(); + let cause = ObligationCause::new( + decl_output_ty.map(Span::TypeRefId).unwrap_or(closure_expr.into()), + ); let InferOk { value: (), obligations } = table .infer_ctxt @@ -822,18 +928,15 @@ impl<'db> InferenceContext<'_, 'db> { .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; all_obligations.extend(obligations); - let inputs = supplied_sig - .inputs() - .iter() - .copied() - .map(|ty| table.infer_ctxt.resolve_vars_if_possible(ty)); + let inputs = + supplied_sig.inputs().iter().copied().map(|ty| table.resolve_vars_if_possible(ty)); expected_sigs.liberated_sig = table.interner().mk_fn_sig( inputs, supplied_output_ty, - expected_sigs.liberated_sig.c_variadic, + expected_sigs.liberated_sig.c_variadic(), Safety::Safe, - FnAbi::RustCall, + ExternAbi::RustCall, ); Ok(InferOk { value: expected_sigs, obligations: all_obligations }) @@ -846,25 +949,54 @@ impl<'db> InferenceContext<'_, 'db> { /// Also, record this closure signature for later. fn supplied_sig_of_closure( &mut self, + closure_expr: ExprId, decl_inputs: &[Option<TypeRefId>], decl_output: Option<TypeRefId>, + closure_kind: ClosureKind, ) -> PolyFnSig<'db> { let interner = self.interner(); let supplied_return = match decl_output { - Some(output) => { - let output = self.make_body_ty(output); - self.process_user_written_ty(output) - } - None => self.table.next_ty_var(), + Some(output) => self.make_body_ty(output), + None => match closure_kind { + // In the case of the async block that we create for a function body, + // we expect the return type of the block to match that of the enclosing + // function. + ClosureKind::Coroutine { + kind: CoroutineKind::Async, + source: CoroutineSource::Fn, + } => { + debug!("closure is async fn body"); + self.deduce_future_output_from_obligations(closure_expr).unwrap_or_else(|| { + // AFAIK, deducing the future output + // always succeeds *except* in error cases + // like #65159. I'd like to return Error + // here, but I can't because I can't + // easily (and locally) prove that we + // *have* reported an + // error. --nikomatsakis + self.table.next_ty_var(closure_expr.into()) + }) + } + // All `gen {}` and `async gen {}` must return unit. + ClosureKind::Coroutine { + kind: CoroutineKind::Gen | CoroutineKind::AsyncGen, + .. + } => self.types.types.unit, + + // For async blocks, we just fall back to `_` here. + // For closures/coroutines, we know nothing about the return + // type unless it was supplied. + ClosureKind::Coroutine { kind: CoroutineKind::Async, .. } + | ClosureKind::OldCoroutine(_) + | ClosureKind::Closure + | ClosureKind::CoroutineClosure(_) => self.table.next_ty_var(closure_expr.into()), + }, }; // First, convert the types that the user supplied (if any). let supplied_arguments = decl_inputs.iter().map(|&input| match input { - Some(input) => { - let input = self.make_body_ty(input); - self.process_user_written_ty(input) - } - None => self.table.next_ty_var(), + Some(input) => self.make_body_ty(input), + None => self.table.next_ty_var(closure_expr.into()), }); Binder::dummy(interner.mk_fn_sig( @@ -872,10 +1004,114 @@ impl<'db> InferenceContext<'_, 'db> { supplied_return, false, Safety::Safe, - FnAbi::RustCall, + ExternAbi::RustCall, )) } + /// Invoked when we are translating the coroutine that results + /// from desugaring an `async fn`. Returns the "sugared" return + /// type of the `async fn` -- that is, the return type that the + /// user specified. The "desugared" return type is an `impl + /// Future<Output = T>`, so we do this by searching through the + /// obligations to extract the `T`. + #[instrument(skip(self), level = "debug", ret)] + fn deduce_future_output_from_obligations(&mut self, body_def_id: ExprId) -> Option<Ty<'db>> { + let ret_coercion = self + .return_coercion + .as_ref() + .unwrap_or_else(|| panic!("async fn coroutine outside of a fn")); + + let ret_ty = ret_coercion.expected_ty(); + let ret_ty = self.table.resolve_vars_with_obligations(ret_ty); + + let get_future_output = |predicate: Predicate<'db>| { + // Search for a pending obligation like + // + // `<R as Future>::Output = T` + // + // where R is the return type we are expecting. This type `T` + // will be our output. + let bound_predicate = predicate.kind(); + if let PredicateKind::Clause(ClauseKind::Projection(proj_predicate)) = + bound_predicate.skip_binder() + { + self.deduce_future_output_from_projection(bound_predicate.rebind(proj_predicate)) + } else { + None + } + }; + + let output_ty = match ret_ty.kind() { + TyKind::Infer(InferTy::TyVar(ret_vid)) => self + .table + .obligations_for_self_ty(ret_vid) + .into_iter() + .find_map(|obligation| get_future_output(obligation.predicate))?, + TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => { + return Some(self.types.types.error); + } + TyKind::Alias(AliasTy { kind: AliasTyKind::Opaque { def_id }, args, .. }) => def_id + .0 + .predicates(self.db) + .iter_instantiated_copied(self.interner(), &args) + .map(Unnormalized::skip_norm_wip) + .find_map(|p| get_future_output(p.as_predicate()))?, + TyKind::Error(_) => return Some(ret_ty), + _ => { + panic!("invalid async fn coroutine return type: {ret_ty:?}") + } + }; + + Some(output_ty) + } + + /// Given a projection like + /// + /// `<X as Future>::Output = T` + /// + /// where `X` is some type that has no late-bound regions, returns + /// `Some(T)`. If the projection is for some other trait, returns + /// `None`. + fn deduce_future_output_from_projection( + &self, + predicate: PolyProjectionPredicate<'db>, + ) -> Option<Ty<'db>> { + debug!("deduce_future_output_from_projection(predicate={:?})", predicate); + + // We do not expect any bound regions in our predicate, so + // skip past the bound vars. + let Some(predicate) = predicate.no_bound_vars() else { + debug!("deduce_future_output_from_projection: has late-bound regions"); + return None; + }; + + // Check that this is a projection from the `Future` trait. + let trait_def_id = predicate.projection_term.trait_def_id(self.interner()).0; + if Some(trait_def_id) != self.lang_items.Future { + debug!("deduce_future_output_from_projection: not a future"); + return None; + } + + // The `Future` trait has only one associated item, `Output`, + // so check that this is what we see. + let output_assoc_item = self.lang_items.FutureOutput; + if output_assoc_item.map(Into::into) != Some(predicate.def_id().0) { + panic!( + "projecting associated item `{:?}` from future, which is not Output `{:?}`", + predicate.projection_term.kind(self.interner()), + output_assoc_item, + ); + } + + // Extract the type from the projection. Note that there can + // be no bound variables in this type because the "self type" + // does not have any regions in it. + let output_ty = self.resolve_vars_if_possible(predicate.term); + debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty); + // This is a projection on a Fn trait so will always be a type. + Some(output_ty.expect_type()) + } + /// Converts the types that the user supplied, in case that doing /// so should yield an error, but returns back a signature where /// all parameters are of type `ty::Error`. @@ -903,7 +1139,7 @@ impl<'db> InferenceContext<'_, 'db> { err_ty, false, Safety::Safe, - FnAbi::RustCall, + ExternAbi::RustCall, )); debug!("supplied_sig_of_closure: result={:?}", result); diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs index 668d7496cd..5ea43bc03c 100644 --- a/crates/hir-ty/src/infer/closure/analysis.rs +++ b/crates/hir-ty/src/infer/closure/analysis.rs @@ -41,10 +41,11 @@ use hir_def::{ resolver::ValueNs, }; use macros::{TypeFoldable, TypeVisitable}; +use rustc_abi::ExternAbi; use rustc_ast_ir::Mutability; use rustc_hash::{FxBuildHasher, FxHashMap}; use rustc_type_ir::{ - BoundVar, ClosureKind, TypeVisitableExt as _, + BoundVar, ClosureKind, inherent::{AdtDef as _, GenericArgs as _, IntoKind as _, Ty as _}, }; use smallvec::{SmallVec, smallvec}; @@ -52,7 +53,7 @@ use span::Edition; use tracing::{debug, instrument}; use crate::{ - FnAbi, + Span, infer::{ CaptureInfo, CaptureSourceStack, CapturedPlace, InferenceContext, UpvarCapture, closure::analysis::expr_use_visitor::{ @@ -195,7 +196,7 @@ type InferredCaptureInformation = Vec<(Place, CaptureInfo)>; impl<'a, 'db> InferenceContext<'a, 'db> { pub(crate) fn closure_analyze(&mut self) { - let upvars = crate::upvars::upvars_mentioned(self.db, self.owner) + let upvars = crate::upvars::upvars_mentioned(self.db, self.store_owner) .unwrap_or(const { &FxHashMap::with_hasher(FxBuildHasher) }); for root_expr in self.store.expr_roots() { self.analyze_closures_in_expr(root_expr, upvars); @@ -284,7 +285,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // coroutine-closures that are `move` since otherwise they themselves will // be borrowing from the outer environment, so there's no self-borrows occurring. if let UpvarArgs::Coroutine(..) = args - && let hir_def::hir::ClosureKind::AsyncBlock { source: CoroutineSource::Closure } = + && let hir_def::hir::ClosureKind::Coroutine { source: CoroutineSource::Closure, .. } = closure_kind && let parent_hir_id = ExpressionStore::closure_for_coroutine(closure_expr_id) && let parent_ty = self.result.expr_ty(parent_hir_id) @@ -310,8 +311,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // // FIXME(async_closures): This could be cleaned up. It's a bit janky that we're just // moving all of the `LocalSource::AsyncFn` locals here. - if let hir_def::hir::ClosureKind::AsyncBlock { + if let hir_def::hir::ClosureKind::Coroutine { source: CoroutineSource::Fn | CoroutineSource::Closure, + .. } = closure_kind { let Expr::Block { statements, .. } = &self.store[body] else { @@ -328,7 +330,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let Expr::Path(path) = &self.store[init] else { panic!(); }; - let update_guard = self.resolver.update_to_inner_scope(self.db, self.owner, init); + let update_guard = + self.resolver.update_to_inner_scope(self.db, self.store_owner, init); let Some(ValueNs::LocalBinding(local_id)) = self.resolver.resolve_path_in_value_ns_fully( self.db, @@ -402,9 +405,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // For coroutine-closures, we additionally must compute the // `coroutine_captures_by_ref_ty` type, which is used to generate the by-ref // version of the coroutine-closure's output coroutine. - if let UpvarArgs::CoroutineClosure(args) = args - && !args.references_error() - { + if let UpvarArgs::CoroutineClosure(args) = args { let closure_env_region: Region<'_> = Region::new_bound( self.interner(), rustc_type_ir::INNERMOST, @@ -459,7 +460,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { tupled_upvars_ty_for_borrow, false, Safety::Safe, - FnAbi::Rust, + ExternAbi::Rust, ), self.types.coroutine_captures_by_ref_bound_var_kinds, ), @@ -506,7 +507,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // Build a tuple (U0..Un) of the final upvar types U0..Un // and unify the upvar tuple type in the closure with it: let final_tupled_upvars_type = Ty::new_tup(self.interner(), &final_upvar_tys); - self.demand_suptype(args.tupled_upvars_ty(), final_tupled_upvars_type); + _ = self.demand_suptype( + closure_expr_id.into(), + args.tupled_upvars_ty(), + final_tupled_upvars_type, + ); let fake_reads = delegate.fake_reads; @@ -737,7 +742,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { }; let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else { - let mutability = self.determine_capture_mutability(&place); + let mutability = self.determine_capture_mutability(closure_def_id, &place); let min_cap_list = vec![CapturedPlace { place, info: capture_info, mutability }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); continue; @@ -846,7 +851,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // Only need to insert when we don't have an ancestor in the existing min capture list if !ancestor_found { - let mutability = self.determine_capture_mutability(&place); + let mutability = self.determine_capture_mutability(closure_def_id, &place); let captured_place = CapturedPlace { place, info: updated_capture_info, mutability }; min_cap_list.push(captured_place); @@ -915,12 +920,12 @@ impl<'a, 'db> InferenceContext<'a, 'db> { self.result.closures_data.insert(closure_def_id, closure_data); } - fn normalize_capture_place(&self, place: Place) -> Place { - let mut place = self.infcx().resolve_vars_if_possible(place); + fn normalize_capture_place(&mut self, span: Span, place: Place) -> Place { + let place = self.infcx().resolve_vars_if_possible(place); // In the new solver, types in HIR `Place`s can contain unnormalized aliases, // which can ICE later (e.g. when projecting fields for diagnostics). - let cause = ObligationCause::misc(); + let cause = ObligationCause::new(span); let at = self.table.at(&cause); match normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( at, @@ -940,11 +945,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { } normalized } - Err(_errors) => { - place.base_ty = self.types.types.error.store(); - for proj in &mut place.projections { - proj.ty = self.types.types.error.store(); - } + Err(errors) => { + self.table.trait_errors.extend(errors); place } } @@ -997,7 +999,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { } } - fn place_for_root_variable(&self, closure_def_id: ExprId, var_hir_id: BindingId) -> Place { + fn place_for_root_variable(&mut self, closure_def_id: ExprId, var_hir_id: BindingId) -> Place { let place = Place { base_ty: self.result.binding_ty(var_hir_id).store(), base: PlaceBase::Upvar { closure: closure_def_id, var_id: var_hir_id }, @@ -1006,13 +1008,13 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // Normalize eagerly when inserting into `capture_information`, so all downstream // capture analysis can assume a normalized `Place`. - self.normalize_capture_place(place) + self.normalize_capture_place(var_hir_id.into(), place) } /// A captured place is mutable if /// 1. Projections don't include a Deref of an immut-borrow, **and** /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow. - fn determine_capture_mutability(&mut self, place: &Place) -> Mutability { + fn determine_capture_mutability(&mut self, closure_expr: ExprId, place: &Place) -> Mutability { let var_hir_id = match place.base { PlaceBase::Upvar { var_id, .. } => var_id, _ => unreachable!(), @@ -1025,7 +1027,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { }; for pointer_ty in place.deref_tys() { - match self.table.structurally_resolve_type(pointer_ty).kind() { + match self.structurally_resolve_type(closure_expr.into(), pointer_ty).kind() { // We don't capture derefs of raw ptrs TyKind::RawPtr(_, _) => unreachable!(), @@ -1127,7 +1129,7 @@ fn restrict_repr_packed_field_ref_capture( // Return true for fields of packed structs. match p.kind { ProjectionKind::Field { .. } => match ty.kind() { - TyKind::Adt(def, _) if def.repr().packed() => { + TyKind::Adt(def, _) if def.is_packed() => { // We stop here regardless of field alignment. Field alignment can change as // types change, including the types of private fields in other crates, and that // shouldn't affect how we compute our captures. @@ -1210,7 +1212,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind { let mut dummy_capture_info = CaptureInfo { sources: SmallVec::new(), capture_kind: dummy_capture_kind }; - let place = ctx.normalize_capture_place(place_with_id.place.clone()); + let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone()); let place = restrict_capture_precision(place, &mut dummy_capture_info); @@ -1226,7 +1228,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind { }; assert_eq!(self.closure_def_id, upvar_closure); - let place = ctx.normalize_capture_place(place_with_id.place.clone()); + let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone()); self.capture_information.push(( place, @@ -1241,7 +1243,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind { }; assert_eq!(self.closure_def_id, upvar_closure); - let place = ctx.normalize_capture_place(place_with_id.place.clone()); + let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone()); self.capture_information.push(( place, @@ -1266,7 +1268,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind { let mut capture_info = CaptureInfo { sources: place_with_id.origins.iter().cloned().collect(), capture_kind }; - let place = ctx.normalize_capture_place(place_with_id.place.clone()); + let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone()); // We only want repr packed restriction to be applied to reading references into a packed // struct, and not when the data is being moved. Therefore we call this method here instead diff --git a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 099fa18168..deafff6b43 100644 --- a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -14,20 +14,21 @@ use hir_def::{ }, resolver::ValueNs, }; -use rustc_ast_ir::{try_visit, visit::VisitorResult}; -use rustc_type_ir::{ - FallibleTypeFolder, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, - inherent::{AdtDef, IntoKind, Ty as _}, -}; +use macros::{TypeFoldable, TypeVisitable}; +use rustc_type_ir::inherent::{IntoKind, Ty as _}; use smallvec::{SmallVec, smallvec}; +use stdx::impl_from; use syntax::ast::{BinaryOp, UnaryOp}; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; use crate::{ - Adjust, Adjustment, AutoBorrow, BindingMode, - infer::{CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind}, + Adjust, Adjustment, AutoBorrow, Span, + infer::{ + ByRef, CaptureSourceStack, DerefPatBorrowMode, InferenceContext, PatAdjust, PatAdjustment, + UpvarCapture, closure::analysis::BorrowKind, + }, method_resolution::CandidateId, - next_solver::{DbInterner, ErrorGuaranteed, StoredTy, Ty, TyKind}, + next_solver::{ErrorGuaranteed, StoredTy, Ty, TyKind}, upvars::UpvarsRef, utils::EnumerateAndAdjustIterator, }; @@ -69,12 +70,13 @@ pub enum PlaceBase { Upvar { closure: ExprId, var_id: BindingId }, } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] pub struct Projection { /// Type after the projection is applied. pub ty: StoredTy, /// Defines the kind of access made by the projection. + #[type_visitable(ignore)] pub kind: ProjectionKind, } @@ -82,61 +84,17 @@ pub struct Projection { /// always correspond to a syntactic place expression. For example, when /// processing a pattern, a `Place` can be used to refer to the sub-value /// currently being inspected. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] pub struct Place { /// The type of the `PlaceBase` pub base_ty: StoredTy, /// The "outermost" place that holds this value. + #[type_visitable(ignore)] pub base: PlaceBase, /// How this place is derived from the base place. pub projections: Vec<Projection>, } -impl<'db> TypeVisitable<DbInterner<'db>> for Place { - fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, visitor: &mut V) -> V::Result { - let Self { base_ty, base: _, projections } = self; - try_visit!(base_ty.as_ref().visit_with(visitor)); - for proj in projections { - let Projection { ty, kind: _ } = proj; - try_visit!(ty.as_ref().visit_with(visitor)); - } - V::Result::output() - } -} - -impl<'db> TypeFoldable<DbInterner<'db>> for Place { - fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>( - self, - folder: &mut F, - ) -> Result<Self, F::Error> { - let Self { base_ty, base, projections } = self; - let base_ty = base_ty.as_ref().try_fold_with(folder)?.store(); - let projections = projections - .into_iter() - .map(|proj| { - let Projection { ty, kind } = proj; - let ty = ty.as_ref().try_fold_with(folder)?.store(); - Ok(Projection { ty, kind }) - }) - .collect::<Result<_, _>>()?; - Ok(Self { base_ty, base, projections }) - } - - fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { - let Self { base_ty, base, projections } = self; - let base_ty = base_ty.as_ref().fold_with(folder).store(); - let projections = projections - .into_iter() - .map(|proj| { - let Projection { ty, kind } = proj; - let ty = ty.as_ref().fold_with(folder).store(); - Projection { ty, kind } - }) - .collect(); - Self { base_ty, base, projections } - } -} - impl Place { /// Returns an iterator of the types that have to be dereferenced to access /// the `Place`. @@ -214,6 +172,13 @@ impl PlaceWithOrigin { origin_stack.push(origin); } } + + pub(crate) fn span(&self) -> Span { + match self.origins.first() { + Some(origin) => origin.final_source().into(), + None => Span::Dummy, + } + } } /// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists. @@ -446,14 +411,6 @@ pub(crate) struct ExprUseVisitor<'a, 'b, 'db, D: Delegate<'db>> { upvars: UpvarsRef<'db>, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum PatWalkMode { - /// `let`, `match`. - Declaration, - /// Destructuring assignment. - Assignment, -} - impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { /// Creates the ExprUseVisitor, configuring it with the various options provided: /// @@ -477,7 +434,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { let param_place = self.cat_rvalue(param.into(), param_ty); self.fake_read_scrutinee(param_place.clone(), false); - self.walk_pat(param_place, param, false, PatWalkMode::Declaration)?; + self.walk_pat(param_place, param, false)?; } self.consume_expr(body)?; @@ -518,7 +475,6 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { Ok(()) } - // FIXME: It's suspicious that this is public; clippy should probably use `walk_expr`. #[instrument(skip(self), level = "debug")] pub(crate) fn consume_expr(&mut self, expr: ExprId) -> Result { let place_with_id = self.cat_expr(expr)?; @@ -700,8 +656,8 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { self.walk_expr(value)?; let expr_place = self.cat_expr(value)?; let update_guard = - self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.owner, expr); - self.walk_pat(expr_place, target, false, PatWalkMode::Assignment)?; + self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.store_owner, expr); + self.walk_pat(expr_place, target, false)?; self.cx.resolver.reset_to_guard(update_guard); } @@ -784,7 +740,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { let expr_place = self.cat_expr(expr)?; f(self)?; self.fake_read_scrutinee(expr_place.clone(), els.is_some()); - self.walk_pat(expr_place, pat, false, PatWalkMode::Declaration)?; + self.walk_pat(expr_place, pat, false)?; if let Some(els) = els { self.walk_expr(els)?; } @@ -803,9 +759,9 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // Select just those fields of the `with` // expression that will actually be used - match self.cx.table.structurally_resolve_type(with_place.place.ty()).kind() { + match self.cx.structurally_resolve_type(with_expr.into(), with_place.place.ty()).kind() { TyKind::Adt(adt, args) if adt.is_struct() => { - let AdtId::StructId(adt) = adt.def_id().0 else { unreachable!() }; + let AdtId::StructId(adt) = adt.def_id() else { unreachable!() }; let adt_fields = VariantId::from(adt).fields(self.cx.db).fields(); let adt_field_types = self.cx.db.field_types(adt.into()); // Consume those fields of the with expression that are needed. @@ -815,7 +771,10 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { let field_place = self.cat_projection( with_expr.into(), with_place.clone(), - adt_field_types[f_index].get().instantiate(self.cx.interner(), args), + adt_field_types[f_index] + .get() + .instantiate(self.cx.interner(), args) + .skip_norm_wip(), ProjectionKind::Field { field_idx: f_index.into_raw().into_u32(), variant_idx: 0, @@ -840,6 +799,11 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { self.cx.result.expr_adjustment(expr).unwrap_or_default().into() } + fn pat_adjustments(&self, pat: PatId) -> SmallVec<[PatAdjustment; 5]> { + // Due to borrowck problems, we cannot borrow the adjustments, unfortunately. + self.cx.result.pat_adjustment(pat).unwrap_or_default().into() + } + /// Invoke the appropriate delegate calls for anything that gets /// consumed or borrowed as part of the automatic adjustment /// process. @@ -896,7 +860,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { } fn walk_arm(&mut self, discr_place: PlaceWithOrigin, arm: &MatchArm) -> Result { - self.walk_pat(discr_place, arm.pat, arm.guard.is_some(), PatWalkMode::Declaration)?; + self.walk_pat(discr_place, arm.pat, arm.guard.is_some())?; if let Some(e) = arm.guard { self.consume_expr(e)?; @@ -920,14 +884,33 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { /// Do note that discrepancies like these do still create obscure corners /// in the semantics of the language, and should be avoided if possible. #[instrument(skip(self), level = "debug")] - fn walk_pat( - &mut self, - discr_place: PlaceWithOrigin, - pat: PatId, - has_guard: bool, - mode: PatWalkMode, - ) -> Result { + fn walk_pat(&mut self, discr_place: PlaceWithOrigin, pat: PatId, has_guard: bool) -> Result { self.cat_pattern(discr_place.clone(), pat, &mut |this, place, pat| { + let walk_deref_pat = |this: &mut Self, subpattern: PatId, place: PlaceWithOrigin| { + // A deref pattern is a bit special: the binding mode of its inner bindings + // determines whether to borrow *at the level of the deref pattern* rather than + // borrowing the bound place (since that inner place is inside the temporary that + // stores the result of calling `deref()`/`deref_mut()` so can't be captured). + // Deref patterns on boxes don't borrow, so we ignore them here. + // HACK: this could be a fake pattern corresponding to a deref inserted by match + // ergonomics, in which case `pat.hir_id` will be the id of the subpattern. + if let DerefPatBorrowMode::Borrow(mutability) = + this.cx.deref_pat_borrow_mode(place.place.ty(), subpattern) + { + let bk = BorrowKind::from_mutbl(mutability); + this.delegate.borrow(place, bk, this.cx); + } + }; + + let pat = match pat { + CatPatternPat::PatId(pat) => pat, + CatPatternPat::DerefPat { inner } => { + debug!("walk_pat: Deref {{ inner: {:?} }}", inner); + walk_deref_pat(this, inner, place); + return Ok(()); + } + }; + debug!("walk_pat: pat.kind={:?}", this.cx.store[pat]); let read_discriminant = { let place = place.clone(); @@ -961,17 +944,18 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // In a cases of pattern like `let pat = upvar`, don't use the span // of the pattern, as this just looks confusing, instead use the span // of the discriminant. - match this.cx.result.binding_mode(pat) { - Some(BindingMode::Ref(m)) => { + match this.cx.result.binding_mode(pat).ok_or(ErrorGuaranteed)?.0 { + ByRef::Yes(m) => { let bk = BorrowKind::from_mutbl(m); this.delegate.borrow(place, bk, this.cx); } - None | Some(BindingMode::Move) => { + ByRef::No => { debug!("walk_pat binding consuming pat"); this.consume_or_copy(place); } } } + Pat::Deref { inner: subpattern } => walk_deref_pat(this, subpattern, place), Pat::Path(ref path) => { // A `Path` pattern is just a name like `Foo`. This is either a // named constant or else it refers to an ADT variant @@ -987,20 +971,14 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { this.cx.store.pat_path_hygiene(pat), ); let is_normal_const = matches!(resolution, Some(ValueNs::ConstId(_))); - if mode == PatWalkMode::Assignment - && let Some(ValueNs::LocalBinding(local)) = resolution - { - let pat_ty = this.pat_ty(pat)?; - let place = this.cat_local(pat.into(), pat_ty, local)?; - this.delegate.mutate(place, this.cx); - } else if is_assoc_const || is_normal_const { + if is_assoc_const || is_normal_const { // Named constants have to be equated with the value // being matched, so that's a read of the value being matched. // // FIXME: Does the MIR code skip this read when matching on a ZST? // If so, we can also skip it here. read_discriminant(this); - } else if this.is_multivariant_adt(place.place.ty()) { + } else if this.is_multivariant_adt(pat.into(), place.place.ty()) { // Otherwise, this is a struct/enum variant, and so it's // only a read if we need to read the discriminant. read_discriminant(this); @@ -1019,7 +997,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { read_discriminant(this); } Pat::Record { .. } | Pat::TupleStruct { .. } => { - if this.is_multivariant_adt(place.place.ty()) { + if this.is_multivariant_adt(pat.into(), place.place.ty()) { read_discriminant(this); } } @@ -1035,7 +1013,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { read_discriminant(this); } } - Pat::Expr(expr) if mode == PatWalkMode::Assignment => { + Pat::Expr(expr) => { // Destructuring assignment. this.mutate_expr(expr)?; } @@ -1044,13 +1022,13 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { | Pat::Ref { .. } | Pat::Tuple { .. } | Pat::Wild - | Pat::Missing => { + | Pat::Missing + | Pat::Rest => { // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses // are made later as these patterns contains subpatterns. // If the PatKind is Missing, Wild or Err, any relevant accesses are made when processing // the other patterns that are part of the match } - Pat::Expr(_) => {} } Ok(()) @@ -1167,6 +1145,13 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { } } +#[derive(Debug, Clone, Copy)] +enum CatPatternPat { + PatId(PatId), + DerefPat { inner: PatId }, +} +impl_from!(PatId for CatPatternPat); + /// The job of the methods whose name starts with `cat_` is to analyze /// expressions and construct the corresponding [`Place`]s. The `cat` /// stands for "categorize", this is a leftover from long ago when @@ -1196,10 +1181,6 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { self.node_ty(expr.into()) } - fn pat_ty(&mut self, pat: PatId) -> Result<Ty<'db>> { - self.node_ty(pat.into()) - } - fn expr_ty_adjusted(&mut self, expr: ExprId) -> Result<Ty<'db>> { self.expect_and_resolve_type(self.cx.result.type_of_expr_with_adjust(expr)) } @@ -1219,18 +1200,52 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // that these are never attached to binding patterns, so // actually this is somewhat "disjoint" from the code below // that aims to account for `ref x`. - if let Some(vec) = self.cx.result.pat_adjustments.get(&pat) - && let Some(first_adjust) = vec.first() + if let Some(vec) = self.cx.result.pat_adjustment(pat) { + if let Some(first_adjust) = vec.first() { + debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust); + return Ok(first_adjust.source.as_ref()); + } + } else if let Pat::Ref { pat: subpat, .. } = self.cx.store[pat] + && self.cx.result.is_skipped_ref_pat(pat) { - debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust); - return Ok(first_adjust.as_ref()); + return self.pat_ty_adjusted(subpat); } + self.pat_ty_unadjusted(pat) } /// Like [`Self::pat_ty_adjusted`], but ignores implicit `&` patterns. fn pat_ty_unadjusted(&mut self, pat: PatId) -> Result<Ty<'db>> { - Ok(self.cx.result.pat_ty(pat)) + let base_ty = self.node_ty(pat.into())?; + trace!(?base_ty); + + // This code detects whether we are looking at a `ref x`, + // and if so, figures out what the type *being borrowed* is. + match self.cx.store[pat] { + Pat::Bind { .. } => { + let bm = self.cx.result.binding_mode(pat).ok_or(ErrorGuaranteed)?; + + if let ByRef::Yes(_) = bm.0 { + // a bind-by-ref means that the base_ty will be the type of the ident itself, + // but what we want here is the type of the underlying value being borrowed. + // So peel off one-level, turning the &T into T. + match self + .cx + .structurally_resolve_type(pat.into(), base_ty) + .builtin_deref(false) + { + Some(ty) => Ok(ty), + None => { + debug!("By-ref binding of non-derefable type: {base_ty:?}"); + Err(ErrorGuaranteed) + } + } + } else { + Ok(base_ty) + } + } + _ => Ok(base_ty), + } } fn cat_expr(&mut self, expr: ExprId) -> Result<PlaceWithOrigin> { @@ -1335,7 +1350,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { Expr::Path(ref path) => { let resolver_guard = - self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.owner, expr); + self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.store_owner, expr); let resolution = self.cx.resolver.resolve_path_in_value_ns_fully( self.cx.db, path, @@ -1423,7 +1438,8 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { let place_ty = self.expr_ty(expr)?; let base_ty = self.expr_ty_adjusted(base)?; - let TyKind::Ref(region, _, mutbl) = self.cx.table.structurally_resolve_type(base_ty).kind() + let TyKind::Ref(region, _, mutbl) = + self.cx.structurally_resolve_type(base.into(), base_ty).kind() else { return Err(ErrorGuaranteed); }; @@ -1440,7 +1456,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { ) -> Result<PlaceWithOrigin> { let base_curr_ty = base_place.place.ty(); let Some(deref_ty) = - self.cx.table.structurally_resolve_type(base_curr_ty).builtin_deref(true) + self.cx.structurally_resolve_type(node, base_curr_ty).builtin_deref(true) else { debug!("explicit deref of non-derefable type: {:?}", base_curr_ty); return Err(ErrorGuaranteed); @@ -1467,7 +1483,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { /// Here `pat_hir_id` is the ExprId of the pattern itself. fn total_fields_in_tuple(&mut self, pat_id: PatId) -> usize { let ty = self.cx.result.pat_ty(pat_id); - match self.cx.table.structurally_resolve_type(ty).kind() { + match self.cx.structurally_resolve_type(pat_id.into(), ty).kind() { TyKind::Tuple(args) => args.len(), _ => panic!("tuple pattern not applied to a tuple"), } @@ -1486,7 +1502,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { op: &mut F, ) -> Result where - F: FnMut(&mut Self, PlaceWithOrigin, PatId) -> Result, + F: FnMut(&mut Self, PlaceWithOrigin, CatPatternPat) -> Result, { // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithId` correspondingly. // `PlaceWithId`s are constructed differently from patterns. For example, in @@ -1520,11 +1536,26 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // Then we see that to get the same result, we must start with // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. - let adjustments_len = self.cx.result.pat_adjustment(pat).map_or(0, |it| it.len()); - for _ in 0..adjustments_len { + let adjustments = self.pat_adjustments(pat); + let mut adjusts = adjustments.iter().peekable(); + while let Some(adjust) = adjusts.next() { debug!("applying adjustment to place_with_id={:?}", place_with_id); - // FIXME: We need to adjust this once we implement deref patterns (or pin ergonomics, for that matter). - place_with_id = self.cat_deref(pat.into(), place_with_id)?; + place_with_id = match adjust.kind { + PatAdjust::BuiltinDeref => self.cat_deref(pat.into(), place_with_id)?, + PatAdjust::OverloadedDeref => { + // This adjustment corresponds to an overloaded deref; unless it's on a box, it + // borrows the scrutinee to call `Deref::deref` or `DerefMut::deref_mut`. Invoke + // the callback before setting `place_with_id` to the temporary storing the + // result of the deref. + op(self, place_with_id.clone(), CatPatternPat::DerefPat { inner: pat })?; + let target_ty = match adjusts.peek() { + Some(next_adjust) => next_adjust.source.as_ref(), + // At the end of the deref chain, we get `pat`'s scrutinee. + None => self.pat_ty_unadjusted(pat)?, + }; + self.pat_deref_place(pat.into(), place_with_id, pat, target_ty)? + } + }; } let place_with_id = place_with_id; // lose mutability debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id); @@ -1538,7 +1569,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)` // result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with // that (where the `ref` on `x` is implied). - op(self, place_with_id.clone(), pat)?; + op(self, place_with_id.clone(), pat.into())?; match self.cx.store[pat] { Pat::Tuple { args: ref subpats, ellipsis: dots_pos } => { @@ -1618,16 +1649,20 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { let subplace = self.cat_deref(pat.into(), place_with_id)?; self.cat_pattern(subplace, subpat, op)?; } + Pat::Deref { inner: subpat } => { + let ty = self.pat_ty_adjusted(subpat)?; + let place = self.pat_deref_place(pat.into(), place_with_id, subpat, ty)?; + self.cat_pattern(place, subpat, op)?; + } Pat::Slice { prefix: ref before, slice, suffix: ref after } => { let Some(element_ty) = self .cx - .table - .structurally_resolve_type(place_with_id.place.ty()) + .structurally_resolve_type(pat.into(), place_with_id.place.ty()) .builtin_index() else { debug!("explicit index of non-indexable type {:?}", place_with_id); - panic!("explicit index of non-indexable type"); + return Err(ErrorGuaranteed); }; let elt_place = self.cat_projection( pat.into(), @@ -1660,6 +1695,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { | Pat::ConstBlock(..) | Pat::Range { .. } | Pat::Missing + | Pat::Rest | Pat::Wild => { // always ok } @@ -1668,6 +1704,29 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { Ok(()) } + /// Represents the place matched on by a deref pattern's interior. + fn pat_deref_place( + &mut self, + node: ExprOrPatId, + base_place: PlaceWithOrigin, + inner: PatId, + target_ty: Ty<'db>, + ) -> Result<PlaceWithOrigin> { + match self.cx.deref_pat_borrow_mode(base_place.place.ty(), inner) { + // Deref patterns on boxes are lowered using a built-in deref. + DerefPatBorrowMode::Box => self.cat_deref(node, base_place), + // For other types, we create a temporary to match on. + DerefPatBorrowMode::Borrow(mutability) => { + let re_erased = self.cx.types.regions.erased; + let ty = Ty::new_ref(self.cx.interner(), re_erased, target_ty, mutability); + // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ... + let base = self.cat_rvalue(node, ty); + // ... and the inner pattern matches on the place behind that reference. + self.cat_deref(node, base) + } + } + } + /// Checks whether a type has multiple variants, and therefore, whether a /// read of the discriminant might be necessary. Note that the actual MIR /// builder code does a more specific check, filtering out variants that @@ -1682,13 +1741,13 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { /// FIXME(never_patterns): update this comment once the aforementioned MIR builder /// code is changed to be insensitive to inhhabitedness. #[instrument(skip(self), level = "debug")] - fn is_multivariant_adt(&mut self, ty: Ty<'db>) -> bool { - if let TyKind::Adt(def, _) = self.cx.table.structurally_resolve_type(ty).kind() { + fn is_multivariant_adt(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> bool { + if let TyKind::Adt(def, _) = self.cx.structurally_resolve_type(node, ty).kind() { // Note that if a non-exhaustive SingleVariant is defined in another crate, we need // to assume that more cases will be added to the variant in the future. This mean // that we should handle non-exhaustive SingleVariant the same way we would handle // a MultiVariant. - match def.def_id().0 { + match def.def_id() { AdtId::StructId(_) | AdtId::UnionId(_) => false, AdtId::EnumId(did) => { let has_foreign_non_exhaustive = || { diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index 732a583047..343919f5ba 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -38,10 +38,7 @@ use std::ops::ControlFlow; use hir_def::{ - CallableDefId, TraitId, - attrs::AttrFlags, - hir::{ExprId, ExprOrPatId}, - signatures::FunctionSignature, + CallableDefId, TraitId, attrs::AttrFlags, hir::ExprId, signatures::FunctionSignature, }; use rustc_ast_ir::Mutability; use rustc_type_ir::{ @@ -55,12 +52,10 @@ use smallvec::SmallVec; use tracing::{debug, instrument}; use crate::{ - Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, TargetFeatures, + Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, Span, TargetFeatures, autoderef::Autoderef, db::{HirDatabase, InternedClosure, InternedClosureId}, - infer::{ - AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch, expr::ExprIsRead, - }, + infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, expr::ExprIsRead}, next_solver::{ Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper, Canonical, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, @@ -155,10 +150,8 @@ where let snapshot = self.infcx().start_snapshot(); let result = f(self); match result { - Ok(_) => {} - Err(_) => { - self.infcx().rollback_to(snapshot); - } + Ok(_) => self.infcx().commit_from(snapshot), + Err(_) => self.infcx().rollback_to(snapshot), } result } @@ -321,14 +314,15 @@ where if b.is_infer() { // Two unresolved type variables: create a `Coerce` predicate. - let target_ty = if self.use_lub { self.infcx().next_ty_var() } else { b }; + let target_ty = + if self.use_lub { self.infcx().next_ty_var(self.cause.span()) } else { b }; let mut obligations = PredicateObligations::with_capacity(2); for &source_ty in &[a, b] { if source_ty != target_ty { obligations.push(Obligation::new( self.interner(), - self.cause.clone(), + self.cause, self.param_env(), Binder::dummy(PredicateKind::Coerce(CoercePredicate { a: source_ty, @@ -381,7 +375,8 @@ where let mut first_error = None; let mut r_borrow_var = None; - let mut autoderef = Autoderef::new_with_tracking(self.infcx(), self.param_env(), a); + let mut autoderef = + Autoderef::new_with_tracking(self.infcx(), self.param_env(), a, self.cause.span()); let mut found = None; for (referent_ty, autoderefs) in autoderef.by_ref() { @@ -468,7 +463,7 @@ where } else { if r_borrow_var.is_none() { // create var lazily, at most once - let r = self.infcx().next_region_var(); + let r = self.infcx().next_region_var(self.cause.span()); r_borrow_var = Some(r); // [4] above } r_borrow_var.unwrap() @@ -629,7 +624,7 @@ where (TyKind::Ref(_, ty_a, mutbl_a), TyKind::Ref(_, _, mutbl_b)) => { coerce_mutbls(mutbl_a, mutbl_b)?; - let r_borrow = self.infcx().next_region_var(); + let r_borrow = self.infcx().next_region_var(self.cause.span()); // We don't allow two-phase borrows here, at least for initial // implementation. If it happens that this coercion is a function argument, @@ -663,7 +658,7 @@ where // the `CoerceUnsized` target type and the expected type. // We only have the latter, so we use an inference variable // for the former and let type inference do the rest. - let coerce_target = self.infcx().next_ty_var(); + let coerce_target = self.infcx().next_ty_var(self.cause.span()); let mut coercion = self.unify_and( coerce_target, @@ -673,7 +668,7 @@ where )?; // Create an obligation for `Source: CoerceUnsized<Target>`. - let cause = self.cause.clone(); + let cause = self.cause; let pred = TraitRef::new( self.interner(), coerce_unsized_did.into(), @@ -693,6 +688,7 @@ where errored: false, unsize_did, coerce_unsized_did, + span: self.cause.span(), }, ) .is_break() @@ -714,7 +710,7 @@ where self.commit_if_ok(|this| { if let TyKind::FnPtr(_, hdr_b) = b.kind() && fn_ty_a.safety().is_safe() - && !hdr_b.safety.is_safe() + && !hdr_b.safety().is_safe() { let unsafe_a = Ty::safe_to_unsafe_fn_ty(this.interner(), fn_ty_a); this.unify_and( @@ -763,7 +759,8 @@ where return Err(TypeError::ForceInlineCast); } - if b_hdr.safety.is_safe() && attrs.contains(AttrFlags::HAS_TARGET_FEATURE) { + if b_hdr.safety().is_safe() && attrs.contains(AttrFlags::HAS_TARGET_FEATURE) + { let fn_target_features = TargetFeatures::from_fn_no_implications(self.db(), def_id); // Allow the coercion if the current function has all the features that would be @@ -811,7 +808,7 @@ where // `fn(arg0,arg1,...) -> _` // or // `unsafe fn(arg0,arg1,...) -> _` - let safety = hdr.safety; + let safety = hdr.safety(); let closure_sig = self.interner().signature_unclosure(args_a.as_closure().sig(), safety); let pointer_ty = Ty::new_fn_ptr(self.interner(), closure_sig); @@ -894,24 +891,18 @@ impl<'db> InferenceContext<'_, 'db> { /// The expressions *must not* have any preexisting adjustments. pub(crate) fn coerce( &mut self, - expr: ExprOrPatId, + expr: ExprId, expr_ty: Ty<'db>, mut target: Ty<'db>, allow_two_phase: AllowTwoPhase, expr_is_read: ExprIsRead, ) -> RelateResult<'db, Ty<'db>> { - let source = self.table.try_structurally_resolve_type(expr_ty); - target = self.table.try_structurally_resolve_type(target); + let source = self.table.try_structurally_resolve_type(expr.into(), expr_ty); + target = self.table.try_structurally_resolve_type(expr.into(), target); debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); - let cause = ObligationCause::new(); - let coerce_never = match expr { - ExprOrPatId::ExprId(idx) => { - self.expr_guaranteed_to_constitute_read_for_never(idx, expr_is_read) - } - // `PatId` is passed for `PatKind::Path`. - ExprOrPatId::PatId(_) => false, - }; + let cause = ObligationCause::new(expr); + let coerce_never = self.expr_guaranteed_to_constitute_read_for_never(expr, expr_is_read); let mut coerce = Coerce { delegate: InferenceCoercionDelegate(self), cause, @@ -922,11 +913,7 @@ impl<'db> InferenceContext<'_, 'db> { let ok = coerce.commit_if_ok(|coerce| coerce.coerce(source, target))?; let (adjustments, _) = self.table.register_infer_ok(ok); - match expr { - ExprOrPatId::ExprId(expr) => self.write_expr_adj(expr, adjustments.into_boxed_slice()), - ExprOrPatId::PatId(pat) => self - .write_pat_adj(pat, adjustments.into_iter().map(|adjust| adjust.target).collect()), - } + self.write_expr_adj(expr, adjustments.into_boxed_slice()); Ok(target) } @@ -943,8 +930,8 @@ impl<'db> InferenceContext<'_, 'db> { new: ExprId, new_ty: Ty<'db>, ) -> RelateResult<'db, Ty<'db>> { - let prev_ty = self.table.try_structurally_resolve_type(prev_ty); - let new_ty = self.table.try_structurally_resolve_type(new_ty); + let prev_ty = self.table.try_structurally_resolve_type(new.into(), prev_ty); + let new_ty = self.table.try_structurally_resolve_type(new.into(), new_ty); debug!( "coercion::try_find_coercion_lub({:?}, {:?}, exprs={:?} exprs)", prev_ty, @@ -989,8 +976,12 @@ impl<'db> InferenceContext<'_, 'db> { match self.table.commit_if_ok(|table| { // We need to eagerly handle nested obligations due to lazy norm. let mut ocx = ObligationCtxt::new(&table.infer_ctxt); - let value = - ocx.lub(&ObligationCause::new(), table.param_env, prev_ty, new_ty)?; + let value = ocx.lub( + &ObligationCause::new(new), + table.param_env, + prev_ty, + new_ty, + )?; if ocx.try_evaluate_obligations().is_empty() { Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) } else { @@ -1038,7 +1029,7 @@ impl<'db> InferenceContext<'_, 'db> { let sig = self .table .infer_ctxt - .at(&ObligationCause::new(), self.table.param_env) + .at(&ObligationCause::new(new), self.table.param_env) .lub(a_sig, b_sig) .map(|ok| self.table.register_infer_ok(ok))?; @@ -1084,7 +1075,7 @@ impl<'db> InferenceContext<'_, 'db> { // operate on values and not places, so a never coercion is valid. let mut coerce = Coerce { delegate: InferenceCoercionDelegate(self), - cause: ObligationCause::new(), + cause: ObligationCause::new(new), allow_two_phase: AllowTwoPhase::No, coerce_never: true, use_lub: true, @@ -1120,7 +1111,7 @@ impl<'db> InferenceContext<'_, 'db> { .commit_if_ok(|table| { table .infer_ctxt - .at(&ObligationCause::new(), table.param_env) + .at(&ObligationCause::new(new), table.param_env) .lub(prev_ty, new_ty) }) .unwrap_err()) @@ -1335,7 +1326,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { // To be honest, I'm not entirely sure why we do this. // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why icx.coerce( - expression.into(), + expression, expression_ty, self.expected_ty, AllowTwoPhase::No, @@ -1401,14 +1392,11 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { self.final_ty = Some(icx.types.types.error); - icx.result.type_mismatches.get_or_insert_default().insert( - expression.into(), - if label_expression_as_expected { - TypeMismatch { expected: found.store(), actual: expected.store() } - } else { - TypeMismatch { expected: expected.store(), actual: found.store() } - }, - ); + if label_expression_as_expected { + icx.emit_type_mismatch(expression.into(), found, expected); + } else { + icx.emit_type_mismatch(expression.into(), expected, found); + } } } @@ -1466,9 +1454,9 @@ fn coerce<'db>( ) -> Result<(Vec<Adjustment>, Ty<'db>), TypeError<DbInterner<'db>>> { let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(tys); + let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(Span::Dummy, tys); - let cause = ObligationCause::new(); + let cause = ObligationCause::dummy(); // FIXME: Target features. let target_features = TargetFeatures::default(); let mut coerce = Coerce { @@ -1610,8 +1598,8 @@ fn coerce<'db>( } fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool { - let InternedClosure(owner, expr) = closure.loc(db); - upvars_mentioned(db, owner) + let InternedClosure { owner, expr, .. } = closure.loc(db); + upvars_mentioned(db, owner.expression_store_owner(db)) .is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty())) } @@ -1626,11 +1614,16 @@ struct CoerceVisitor<'a, D> { errored: bool, unsize_did: TraitId, coerce_unsized_did: TraitId, + span: Span, } impl<'a, 'db, D: CoerceDelegate<'db>> ProofTreeVisitor<'db> for CoerceVisitor<'a, D> { type Result = ControlFlow<()>; + fn span(&self) -> Span { + self.span + } + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result { let Some(pred) = goal.goal().predicate.as_trait_clause() else { return ControlFlow::Continue(()); diff --git a/crates/hir-ty/src/infer/diagnostics.rs b/crates/hir-ty/src/infer/diagnostics.rs index 2bdc6f9491..dd0efea4d7 100644 --- a/crates/hir-ty/src/infer/diagnostics.rs +++ b/crates/hir-ty/src/infer/diagnostics.rs @@ -2,22 +2,28 @@ //! and a wrapper around [`TyLoweringContext`] ([`InferenceTyLoweringContext`]) that replaces //! it and takes care of diagnostics in inference. -use std::cell::RefCell; +use std::cell::{OnceCell, RefCell}; use std::ops::{Deref, DerefMut}; use either::Either; -use hir_def::GenericDefId; -use hir_def::expr_store::ExpressionStore; use hir_def::expr_store::path::Path; +use hir_def::{ExpressionStoreOwnerId, GenericDefId}; +use hir_def::{expr_store::ExpressionStore, type_ref::TypeRefId}; use hir_def::{hir::ExprOrPatId, resolver::Resolver}; use la_arena::{Idx, RawIdx}; +use rustc_hash::FxHashMap; use thin_vec::ThinVec; use crate::{ - InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic, - db::HirDatabase, - lower::path::{PathDiagnosticCallback, PathLoweringContext}, - lower::{LifetimeElisionKind, TyLoweringContext}, + InferenceDiagnostic, InferenceTyDiagnosticSource, Span, TyLoweringDiagnostic, + db::{AnonConstId, HirDatabase}, + generics::Generics, + infer::unify::InferenceTable, + lower::{ + ForbidParamsAfterReason, LifetimeElisionKind, TyLoweringContext, TyLoweringInferVarsCtx, + path::{PathDiagnosticCallback, PathLoweringContext}, + }, + next_solver::{Const, Region, StoredTy, Ty}, }; // Unfortunately, this struct needs to use interior mutability (but we encapsulate it) @@ -35,7 +41,7 @@ impl Diagnostics { fn push_ty_diagnostics( &self, source: InferenceTyDiagnosticSource, - diagnostics: Vec<TyLoweringDiagnostic>, + diagnostics: ThinVec<TyLoweringDiagnostic>, ) { self.0.borrow_mut().extend( diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }), @@ -52,10 +58,38 @@ pub(crate) struct PathDiagnosticCallbackData<'a> { diagnostics: &'a Diagnostics, } +pub(super) struct InferenceTyLoweringVarsCtx<'a, 'db> { + pub(super) table: &'a mut InferenceTable<'db>, + pub(super) type_of_type_placeholder: &'a mut FxHashMap<TypeRefId, StoredTy>, +} + +impl<'db> TyLoweringInferVarsCtx<'db> for InferenceTyLoweringVarsCtx<'_, 'db> { + fn next_ty_var(&mut self, span: Span) -> Ty<'db> { + let ty = self.table.infer_ctxt.next_ty_var(span); + + if let Span::TypeRefId(type_ref) = span { + self.type_of_type_placeholder.insert(type_ref, ty.store()); + } + + ty + } + fn next_const_var(&mut self, span: Span) -> Const<'db> { + self.table.infer_ctxt.next_const_var(span) + } + fn next_region_var(&mut self, span: Span) -> Region<'db> { + self.table.infer_ctxt.next_region_var(span) + } + + fn as_table(&mut self) -> Option<&mut InferenceTable<'db>> { + Some(self.table) + } +} + pub(super) struct InferenceTyLoweringContext<'db, 'a> { ctx: TyLoweringContext<'db, 'a>, diagnostics: &'a Diagnostics, source: InferenceTyDiagnosticSource, + defined_anon_consts: &'a RefCell<ThinVec<AnonConstId>>, } impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> { @@ -66,14 +100,28 @@ impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> { store: &'a ExpressionStore, diagnostics: &'a Diagnostics, source: InferenceTyDiagnosticSource, + def: ExpressionStoreOwnerId, generic_def: GenericDefId, + generics: &'a OnceCell<Generics<'db>>, lifetime_elision: LifetimeElisionKind<'db>, + allow_using_generic_params: bool, + infer_vars: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>, + defined_anon_consts: &'a RefCell<ThinVec<AnonConstId>>, ) -> Self { - Self { - ctx: TyLoweringContext::new(db, resolver, store, generic_def, lifetime_elision), - diagnostics, - source, + let mut ctx = TyLoweringContext::new( + db, + resolver, + store, + def, + generic_def, + generics, + lifetime_elision, + ) + .with_infer_vars_behavior(infer_vars); + if !allow_using_generic_params { + ctx.forbid_params_after(0, ForbidParamsAfterReason::AnonConst); } + Self { ctx, diagnostics, source, defined_anon_consts } } #[inline] @@ -135,5 +183,6 @@ impl Drop for InferenceTyLoweringContext<'_, '_> { fn drop(&mut self) { self.diagnostics .push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics)); + self.defined_anon_consts.borrow_mut().extend(self.ctx.defined_anon_consts.iter().copied()); } } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index d80ea71674..0675b5e857 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -4,11 +4,11 @@ use std::{iter::repeat_with, mem}; use either::Either; use hir_def::{ - FieldId, GenericDefId, ItemContainerId, Lookup, TupleFieldId, TupleId, + AdtId, FieldId, TupleFieldId, TupleId, VariantId, expr_store::path::{GenericArgs as HirGenericArgs, Path}, hir::{ Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId, - InlineAsmKind, LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp, + InlineAsmKind, LabelId, Pat, PatId, RecordLitField, RecordSpread, Statement, UnaryOp, }, resolver::ValueNs, signatures::VariantFields, @@ -16,35 +16,33 @@ use hir_def::{ use hir_def::{FunctionId, hir::ClosureKind}; use hir_expand::name::Name; use rustc_ast_ir::Mutability; +use rustc_hash::FxHashMap; use rustc_type_ir::{ InferTy, Interner, - inherent::{AdtDef, GenericArgs as _, IntoKind, Ty as _}, + inherent::{GenericArgs as _, IntoKind, Ty as _}, }; +use stdx::never; use syntax::ast::RangeOp; use tracing::debug; use crate::{ - Adjust, Adjustment, CallableDefId, DeclContext, DeclOrigin, Rawness, consteval, - generics::generics, - infer::{ - AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, - pat::contains_explicit_ref_binding, - }, - lower::{GenericPredicates, lower_mutability}, + Adjust, Adjustment, CallableDefId, Rawness, Span, + consteval::literal_ty, + infer::{AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, pat::PatOrigin}, + lower::lower_mutability, method_resolution::{self, CandidateId, MethodCallee, MethodError}, next_solver::{ - ClauseKind, FnSig, GenericArg, GenericArgs, TraitRef, Ty, TyKind, TypeError, + ClauseKind, FnSig, GenericArg, GenericArgs, Ty, TyKind, TypeError, infer::{ BoundRegionConversionTime, InferOk, traits::{Obligation, ObligationCause}, }, obligation_ctxt::ObligationCtxt, - util::clauses_as_obligations, }, }; use super::{ - BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, + BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, cast::CastCheck, find_breakable, }; @@ -63,17 +61,41 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let ty = self.infer_expr_inner(tgt_expr, expected, is_read); if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - let could_unify = self.unify(ty, expected_ty); - if !could_unify { - self.result.type_mismatches.get_or_insert_default().insert( - tgt_expr.into(), - TypeMismatch { expected: expected_ty.store(), actual: ty.store() }, - ); - } + _ = self.demand_eqtype(tgt_expr.into(), expected_ty, ty); } ty } + pub(crate) fn infer_expr_suptype_coerce_never( + &mut self, + expr: ExprId, + expected: &Expectation<'db>, + is_read: ExprIsRead, + ) -> Ty<'db> { + let ty = self.infer_expr_inner(expr, expected, is_read); + if ty.is_never() { + if let Some(adjustments) = self.result.expr_adjustments.get(&expr) { + return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments { + target.as_ref() + } else { + self.err_ty() + }; + } + + if let Some(target) = expected.only_has_type(&mut self.table) { + self.coerce(expr, ty, target, AllowTwoPhase::No, ExprIsRead::Yes) + .expect("never-to-any coercion should always succeed") + } else { + ty + } + } else { + if let Some(expected_ty) = expected.only_has_type(&mut self.table) { + _ = self.demand_suptype(expr.into(), expected_ty, ty); + } + ty + } + } + pub(crate) fn infer_expr_no_expect( &mut self, tgt_expr: ExprId, @@ -92,13 +114,10 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let ty = self.infer_expr_inner(expr, expected, is_read); if let Some(target) = expected.only_has_type(&mut self.table) { - match self.coerce(expr.into(), ty, target, AllowTwoPhase::No, is_read) { + match self.coerce(expr, ty, target, AllowTwoPhase::No, is_read) { Ok(res) => res, Err(_) => { - self.result.type_mismatches.get_or_insert_default().insert( - expr.into(), - TypeMismatch { expected: target.store(), actual: ty.store() }, - ); + self.emit_type_mismatch(expr.into(), target, ty); target } } @@ -154,7 +173,7 @@ impl<'db> InferenceContext<'_, 'db> { fn pat_guaranteed_to_constitute_read_for_never(&self, pat: PatId) -> bool { match &self.store[pat] { // Does not constitute a read. - Pat::Wild => false, + Pat::Wild | Pat::Rest => false, // This is unnecessarily restrictive when the pattern that doesn't // constitute a read is unreachable. @@ -175,6 +194,7 @@ impl<'db> InferenceContext<'_, 'db> { | Pat::Path(_) | Pat::Tuple { .. } | Pat::Box { .. } + | Pat::Deref { .. } | Pat::Ref { .. } | Pat::Lit(_) | Pat::Range { .. } @@ -251,13 +271,12 @@ impl<'db> InferenceContext<'_, 'db> { } } - #[expect(clippy::needless_return)] pub(crate) fn check_lhs_assignable(&self, lhs: ExprId) { if self.is_syntactic_place_expr(lhs) { return; } - // FIXME: Emit diagnostic. + self.push_diagnostic(InferenceDiagnostic::InvalidLhsOfAssignment { lhs }); } fn infer_expr_coerce_never( @@ -279,27 +298,21 @@ impl<'db> InferenceContext<'_, 'db> { } if let Some(target) = expected.only_has_type(&mut self.table) { - self.coerce(expr.into(), ty, target, AllowTwoPhase::No, ExprIsRead::Yes) + self.coerce(expr, ty, target, AllowTwoPhase::No, ExprIsRead::Yes) .expect("never-to-any coercion should always succeed") } else { ty } } else { if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - let could_unify = self.unify(ty, expected_ty); - if !could_unify { - self.result.type_mismatches.get_or_insert_default().insert( - expr.into(), - TypeMismatch { expected: expected_ty.store(), actual: ty.store() }, - ); - } + _ = self.demand_eqtype(expr.into(), ty, expected_ty); } ty } } #[tracing::instrument(level = "debug", skip(self, is_read), ret)] - fn infer_expr_inner( + pub(super) fn infer_expr_inner( &mut self, tgt_expr: ExprId, expected: &Expectation<'db>, @@ -312,7 +325,7 @@ impl<'db> InferenceContext<'_, 'db> { let ty = match expr { Expr::Missing => self.err_ty(), &Expr::If { condition, then_branch, else_branch } => { - let expected = &expected.adjust_for_branches(&mut self.table); + let expected = &expected.adjust_for_branches(&mut self.table, tgt_expr.into()); self.infer_expr_coerce_never( condition, &Expectation::HasType(self.types.types.bool), @@ -328,17 +341,23 @@ impl<'db> InferenceContext<'_, 'db> { coercion_sites[1] = else_branch; } let mut coerce = CoerceMany::with_coercion_sites( - expected.coercion_target_type(&mut self.table), + expected.coercion_target_type(&mut self.table, then_branch.into()), &coercion_sites, ); - coerce.coerce(self, &ObligationCause::new(), then_branch, then_ty, ExprIsRead::Yes); + coerce.coerce( + self, + &ObligationCause::new(then_branch), + then_branch, + then_ty, + ExprIsRead::Yes, + ); match else_branch { Some(else_branch) => { let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes); let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); coerce.coerce( self, - &ObligationCause::new(), + &ObligationCause::new(else_branch), else_branch, else_ty, ExprIsRead::Yes, @@ -349,7 +368,7 @@ impl<'db> InferenceContext<'_, 'db> { coerce.coerce_forced_unit( self, tgt_expr, - &ObligationCause::new(), + &ObligationCause::new(tgt_expr), true, ExprIsRead::Yes, ); @@ -366,11 +385,7 @@ impl<'db> InferenceContext<'_, 'db> { ExprIsRead::No }; let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read); - self.infer_top_pat( - pat, - input_ty, - Some(DeclContext { origin: DeclOrigin::LetExpr }), - ); + self.infer_top_pat(pat, input_ty, PatOrigin::LetExpr); self.types.types.bool } Expr::Block { statements, tail, label, id: _ } => { @@ -386,9 +401,7 @@ impl<'db> InferenceContext<'_, 'db> { .1 } &Expr::Loop { body, label } => { - // FIXME: should be: - // let ty = expected.coercion_target_type(&mut self.table); - let ty = self.table.next_ty_var(); + let ty = expected.coercion_target_type(&mut self.table, tgt_expr.into()); let (breaks, ()) = self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { this.infer_expr( @@ -449,15 +462,15 @@ impl<'db> InferenceContext<'_, 'db> { let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut all_arms_diverge = Diverges::Always; for arm in arms.iter() { - self.infer_top_pat(arm.pat, input_ty, None); + self.infer_top_pat(arm.pat, input_ty, PatOrigin::MatchArm); } - let expected = expected.adjust_for_branches(&mut self.table); + let expected = expected.adjust_for_branches(&mut self.table, tgt_expr.into()); let result_ty = match &expected { // We don't coerce to `()` so that if the match expression is a // statement it's branches can have any consistent type. Expectation::HasType(ty) if *ty != self.types.types.unit => *ty, - _ => self.table.next_ty_var(), + _ => self.table.next_ty_var((*expr).into()), }; let mut coerce = CoerceMany::new(result_ty); @@ -476,7 +489,7 @@ impl<'db> InferenceContext<'_, 'db> { all_arms_diverge &= self.diverges; coerce.coerce( self, - &ObligationCause::new(), + &ObligationCause::new(arm.expr), arm.expr, arm_ty, ExprIsRead::Yes, @@ -527,10 +540,11 @@ impl<'db> InferenceContext<'_, 'db> { match find_breakable(&mut self.breakables, label) { Some(ctxt) => match ctxt.coerce.take() { Some(mut coerce) => { + let expr = expr.unwrap_or(tgt_expr); coerce.coerce( self, - &ObligationCause::new(), - expr.unwrap_or(tgt_expr), + &ObligationCause::new(expr), + expr, val_ty, ExprIsRead::Yes, ); @@ -566,7 +580,7 @@ impl<'db> InferenceContext<'_, 'db> { } else { let unit = self.types.types.unit; let _ = self.coerce( - tgt_expr.into(), + tgt_expr, unit, yield_ty, AllowTwoPhase::No, @@ -586,80 +600,10 @@ impl<'db> InferenceContext<'_, 'db> { self.types.types.never } Expr::RecordLit { path, fields, spread, .. } => { - let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path.as_deref(), false); - - if let Some(t) = expected.only_has_type(&mut self.table) { - self.unify(ty, t); - } - - let substs = ty.as_adt().map(|(_, s)| s).unwrap_or(self.types.empty.generic_args); - if let Some(variant) = def_id { - self.write_variant_resolution(tgt_expr.into(), variant); - } - match def_id { - _ if fields.is_empty() => {} - Some(def) => { - let field_types = self.db.field_types(def); - let variant_data = def.fields(self.db); - let visibilities = VariantFields::field_visibilities(self.db, def); - for field in fields.iter() { - let field_def = { - match variant_data.field(&field.name) { - Some(local_id) => { - if !visibilities[local_id] - .is_visible_from(self.db, self.resolver.module()) - { - self.push_diagnostic( - InferenceDiagnostic::NoSuchField { - field: field.expr.into(), - private: Some(local_id), - variant: def, - }, - ); - } - Some(local_id) - } - None => { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - field: field.expr.into(), - private: None, - variant: def, - }); - None - } - } - }; - let field_ty = field_def.map_or(self.err_ty(), |it| { - field_types[it].get().instantiate(self.interner(), &substs) - }); - - // Field type might have some unknown types - // FIXME: we may want to emit a single type variable for all instance of type fields? - let field_ty = self.insert_type_vars(field_ty); - self.infer_expr_coerce( - field.expr, - &Expectation::has_type(field_ty), - ExprIsRead::Yes, - ); - } - } - None => { - for field in fields.iter() { - // Field projections don't constitute reads. - self.infer_expr_coerce(field.expr, &Expectation::None, ExprIsRead::No); - } - } - } - if let RecordSpread::Expr(expr) = *spread { - self.infer_expr_coerce_never(expr, &Expectation::has_type(ty), ExprIsRead::Yes); - } - ty + self.infer_record_expr(tgt_expr, expected, path, fields, *spread) } Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected), - Expr::Await { expr } => { - let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes); - self.resolve_associated_type(inner_ty, self.resolve_future_future_output()) - } + Expr::Await { expr } => self.infer_await_expr(tgt_expr, *expr), Expr::Cast { expr, type_ref } => { let cast_ty = self.make_body_ty(*type_ref); let expr_ty = @@ -667,34 +611,13 @@ impl<'db> InferenceContext<'_, 'db> { self.deferred_cast_checks.push(CastCheck::new(tgt_expr, *expr, expr_ty, cast_ty)); cast_ty } - Expr::Ref { expr, rawness, mutability } => { - let mutability = lower_mutability(*mutability); - let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected - .only_has_type(&mut self.table) - .as_ref() - .and_then(|t| t.as_reference_or_ptr()) - { - if exp_mutability == Mutability::Mut && mutability == Mutability::Not { - // FIXME: record type error - expected mut reference but found shared ref, - // which cannot be coerced - } - if exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { - // FIXME: record type error - expected reference but found ptr, - // which cannot be coerced - } - Expectation::rvalue_hint(self, exp_inner) - } else { - Expectation::none() - }; - let inner_ty = self.infer_expr_inner(*expr, &expectation, ExprIsRead::Yes); - match rawness { - Rawness::RawPtr => Ty::new_ptr(self.interner(), inner_ty, mutability), - Rawness::Ref => { - let lt = self.table.next_region_var(); - Ty::new_ref(self.interner(), lt, inner_ty, mutability) - } - } - } + Expr::Ref { expr, rawness, mutability } => self.infer_ref_expr( + *rawness, + lower_mutability(*mutability), + *expr, + expected, + tgt_expr, + ), &Expr::Box { expr } => self.infer_expr_box(expr, expected), Expr::UnaryOp { expr, op } => self.infer_unop_expr(*op, *expr, expected, tgt_expr), Expr::BinaryOp { lhs, rhs, op } => match op { @@ -715,29 +638,6 @@ impl<'db> InferenceContext<'_, 'db> { &Pat::Expr(expr) => { Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No)) } - Pat::Path(path) => { - let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); - let resolution = self.resolver.resolve_path_in_value_ns_fully( - self.db, - path, - self.store.pat_path_hygiene(target), - ); - self.resolver.reset_to_guard(resolver_guard); - - if matches!( - resolution, - Some( - ValueNs::ConstId(_) - | ValueNs::StructId(_) - | ValueNs::EnumVariantId(_) - ) - ) { - None - } else { - Some(self.infer_expr_path(path, target.into(), tgt_expr)) - } - } _ => None, }; let is_destructuring_assignment = lhs_ty.is_none(); @@ -748,9 +648,9 @@ impl<'db> InferenceContext<'_, 'db> { } else { let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes); let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); + self.resolver.update_to_inner_scope(self.db, self.store_owner, tgt_expr); self.inside_assignment = true; - self.infer_top_pat(target, rhs_ty, None); + self.infer_top_pat(target, rhs_ty, PatOrigin::DestructuringAssignment); self.inside_assignment = false; self.resolver.reset_to_guard(resolver_guard); } @@ -759,7 +659,7 @@ impl<'db> InferenceContext<'_, 'db> { // However, rustc lowers destructuring assignments into blocks, and blocks return `!` if they have no tail // expression and they diverge. Therefore, we have to do the same here, even though we don't lower destructuring // assignments into blocks. - self.table.new_maybe_never_var() + self.table.new_maybe_never_var(value.into()) } else { self.types.types.unit } @@ -814,8 +714,8 @@ impl<'db> InferenceContext<'_, 'db> { let base_t = self.infer_expr_no_expect(*base, ExprIsRead::Yes); let idx_t = self.infer_expr_no_expect(*index, ExprIsRead::Yes); - let base_t = self.table.structurally_resolve_type(base_t); - match self.lookup_indexing(tgt_expr, *base, base_t, idx_t) { + let base_t = self.structurally_resolve_type((*base).into(), base_t); + match self.lookup_indexing(tgt_expr, *base, *index, base_t, idx_t) { Some((trait_index_ty, trait_element_ty)) => { // two-phase not needed because index_ty is never mutable self.demand_coerce( @@ -835,14 +735,14 @@ impl<'db> InferenceContext<'_, 'db> { Expr::Tuple { exprs, .. } => { let mut tys = match expected .only_has_type(&mut self.table) - .map(|t| self.table.try_structurally_resolve_type(t).kind()) + .map(|t| self.table.try_structurally_resolve_type(tgt_expr.into(), t).kind()) { Some(TyKind::Tuple(substs)) => substs .iter() - .chain(repeat_with(|| self.table.next_ty_var())) + .chain(repeat_with(|| self.table.next_ty_var(Span::Dummy))) .take(exprs.len()) .collect::<Vec<_>>(), - _ => (0..exprs.len()).map(|_| self.table.next_ty_var()).collect(), + _ => exprs.iter().map(|&expr| self.table.next_ty_var(expr.into())).collect(), }; for (expr, ty) in exprs.iter().zip(tys.iter_mut()) { @@ -852,107 +752,51 @@ impl<'db> InferenceContext<'_, 'db> { Ty::new_tup(self.interner(), &tys) } - Expr::Array(array) => self.infer_expr_array(array, expected), - Expr::Literal(lit) => match lit { - Literal::Bool(..) => self.types.types.bool, - Literal::String(..) => self.types.types.static_str_ref, - Literal::ByteString(bs) => { - let byte_type = self.types.types.u8; - - let len = consteval::usize_const( - self.db, - Some(bs.len() as u128), - self.resolver.krate(), - ); - - let array_type = Ty::new_array_with_const_len(self.interner(), byte_type, len); - Ty::new_ref( - self.interner(), - self.types.regions.statik, - array_type, - Mutability::Not, - ) - } - Literal::CString(..) => Ty::new_ref( - self.interner(), - self.types.regions.statik, - self.lang_items.CStr.map_or_else( - || self.err_ty(), - |strukt| { - Ty::new_adt( - self.interner(), - strukt.into(), - self.types.empty.generic_args, - ) - }, - ), - Mutability::Not, - ), - Literal::Char(..) => self.types.types.char, - Literal::Int(_v, ty) => match ty { - Some(int_ty) => match int_ty { - hir_def::builtin_type::BuiltinInt::Isize => self.types.types.isize, - hir_def::builtin_type::BuiltinInt::I8 => self.types.types.i8, - hir_def::builtin_type::BuiltinInt::I16 => self.types.types.i16, - hir_def::builtin_type::BuiltinInt::I32 => self.types.types.i32, - hir_def::builtin_type::BuiltinInt::I64 => self.types.types.i64, - hir_def::builtin_type::BuiltinInt::I128 => self.types.types.i128, - }, - None => { - let expected_ty = expected.to_option(&mut self.table); - tracing::debug!(?expected_ty); - let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) { - Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty, - Some(TyKind::Char) => Some(self.types.types.u8), - Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => { - Some(self.types.types.usize) - } - _ => None, - }; - opt_ty.unwrap_or_else(|| self.table.next_int_var()) - } + Expr::Array(Array::ElementList { elements }) => { + self.infer_array_elements_expr(elements, expected, tgt_expr) + } + Expr::Array(Array::Repeat { initializer, repeat }) => { + self.infer_array_repeat_expr(*initializer, *repeat, expected, tgt_expr) + } + Expr::Literal(lit) => literal_ty( + self.interner(), + lit, + |_| { + let expected_ty = expected.to_option(&self.table); + tracing::debug!(?expected_ty); + let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) { + Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty, + Some(TyKind::Char) => Some(self.types.types.u8), + Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => { + Some(self.types.types.usize) + } + _ => None, + }; + opt_ty.unwrap_or_else(|| self.table.next_int_var()) }, - Literal::Uint(_v, ty) => match ty { - Some(int_ty) => match int_ty { - hir_def::builtin_type::BuiltinUint::Usize => self.types.types.usize, - hir_def::builtin_type::BuiltinUint::U8 => self.types.types.u8, - hir_def::builtin_type::BuiltinUint::U16 => self.types.types.u16, - hir_def::builtin_type::BuiltinUint::U32 => self.types.types.u32, - hir_def::builtin_type::BuiltinUint::U64 => self.types.types.u64, - hir_def::builtin_type::BuiltinUint::U128 => self.types.types.u128, - }, - None => { - let expected_ty = expected.to_option(&mut self.table); - let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) { - Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty, - Some(TyKind::Char) => Some(self.types.types.u8), - Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => { - Some(self.types.types.usize) - } - _ => None, - }; - opt_ty.unwrap_or_else(|| self.table.next_int_var()) - } + |_| { + let expected_ty = expected.to_option(&self.table); + let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) { + Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty, + Some(TyKind::Char) => Some(self.types.types.u8), + Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => { + Some(self.types.types.usize) + } + _ => None, + }; + opt_ty.unwrap_or_else(|| self.table.next_int_var()) }, - Literal::Float(_v, ty) => match ty { - Some(float_ty) => match float_ty { - hir_def::builtin_type::BuiltinFloat::F16 => self.types.types.f16, - hir_def::builtin_type::BuiltinFloat::F32 => self.types.types.f32, - hir_def::builtin_type::BuiltinFloat::F64 => self.types.types.f64, - hir_def::builtin_type::BuiltinFloat::F128 => self.types.types.f128, - }, - None => { - let opt_ty = expected - .to_option(&mut self.table) - .filter(|ty| matches!(ty.kind(), TyKind::Float(_))); - opt_ty.unwrap_or_else(|| self.table.next_float_var()) - } + |_| { + let opt_ty = expected + .to_option(&self.table) + .filter(|ty| matches!(ty.kind(), TyKind::Float(_))); + opt_ty.unwrap_or_else(|| self.table.next_float_var()) }, - }, + ), Expr::Underscore => { // Underscore expression is an error, we render a specialized diagnostic // to let the user know what type is expected though. - let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty()); + let expected = expected.to_option(&self.table).unwrap_or_else(|| self.err_ty()); self.push_diagnostic(InferenceDiagnostic::TypedHole { expr: tgt_expr, expected: expected.store(), @@ -972,17 +816,18 @@ impl<'db> InferenceContext<'_, 'db> { // allows them to be inferred based on how they are used later in the // function. if is_input { - let ty = this.table.structurally_resolve_type(ty); + let ty = this.structurally_resolve_type(expr.into(), ty); match ty.kind() { TyKind::FnDef(def, parameters) => { let fnptr_ty = Ty::new_fn_ptr( this.interner(), this.interner() .fn_sig(def) - .instantiate(this.interner(), parameters), + .instantiate(this.interner(), parameters) + .skip_norm_wip(), ); _ = this.coerce( - expr.into(), + expr, ty, fnptr_ty, AllowTwoPhase::No, @@ -992,7 +837,7 @@ impl<'db> InferenceContext<'_, 'db> { TyKind::Ref(_, base_ty, mutbl) => { let ptr_ty = Ty::new_ptr(this.interner(), base_ty, mutbl); _ = this.coerce( - expr.into(), + expr, ty, ptr_ty, AllowTwoPhase::No, @@ -1037,7 +882,6 @@ impl<'db> InferenceContext<'_, 'db> { } } }; - // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); self.write_expr_ty(tgt_expr, ty); if self.shallow_resolve(ty).is_never() @@ -1049,6 +893,301 @@ impl<'db> InferenceContext<'_, 'db> { ty } + fn infer_ref_expr( + &mut self, + rawness: Rawness, + mutbl: Mutability, + oprnd: ExprId, + expected: &Expectation<'db>, + expr: ExprId, + ) -> Ty<'db> { + let hint = expected.only_has_type(&mut self.table).map_or(Expectation::None, |ty| { + match self.table.resolve_vars_with_obligations(ty).kind() { + TyKind::Ref(_, ty, _) | TyKind::RawPtr(ty, _) => { + if self.is_syntactic_place_expr(oprnd) { + // Places may legitimately have unsized types. + // For example, dereferences of a wide pointer and + // the last field of a struct can be unsized. + Expectation::has_type(ty) + } else { + Expectation::rvalue_hint(self, ty) + } + } + _ => Expectation::None, + } + }); + let ty = self.infer_expr_inner(oprnd, &hint, ExprIsRead::No); + + match rawness { + Rawness::RawPtr => Ty::new_ptr(self.interner(), ty, mutbl), + Rawness::Ref => { + // Note: at this point, we cannot say what the best lifetime + // is to use for resulting pointer. We want to use the + // shortest lifetime possible so as to avoid spurious borrowck + // errors. Moreover, the longest lifetime will depend on the + // precise details of the value whose address is being taken + // (and how long it is valid), which we don't know yet until + // type inference is complete. + // + // Therefore, here we simply generate a region variable. The + // region inferencer will then select a suitable value. + // Finally, borrowck will infer the value of the region again, + // this time with enough precision to check that the value + // whose address was taken can actually be made to live as long + // as it needs to live. + let region = self.table.next_region_var(expr.into()); + Ty::new_ref(self.interner(), region, ty, mutbl) + } + } + } + + fn infer_await_expr(&mut self, expr: ExprId, awaitee: ExprId) -> Ty<'db> { + let awaitee_ty = self.infer_expr_no_expect(awaitee, ExprIsRead::Yes); + let (Some(into_future), Some(into_future_output)) = + (self.lang_items.IntoFuture, self.lang_items.IntoFutureOutput) + else { + return self.types.types.error; + }; + self.table.register_bound(awaitee_ty, into_future, ObligationCause::new(expr)); + // Do not eagerly normalize. + Ty::new_projection(self.interner(), into_future_output.into(), [awaitee_ty]) + } + + fn infer_record_expr( + &mut self, + expr: ExprId, + expected: &Expectation<'db>, + path: &Path, + fields: &[RecordLitField], + base_expr: RecordSpread, + ) -> Ty<'db> { + // Find the relevant variant + let (adt_ty, Some(variant)) = self.resolve_variant(expr.into(), path, false) else { + // FIXME: Emit an error. + for field in fields { + self.infer_expr_no_expect(field.expr, ExprIsRead::Yes); + } + + return self.types.types.error; + }; + self.write_variant_resolution(expr.into(), variant); + + // Prohibit struct expressions when non-exhaustive flag is set. + if self.has_applicable_non_exhaustive(variant.into()) { + self.push_diagnostic(InferenceDiagnostic::NonExhaustiveRecordExpr { expr }); + } + + self.check_record_expr_fields(adt_ty, expected, expr, variant, fields, base_expr); + + self.require_type_is_sized(adt_ty, expr.into()); + adt_ty + } + + fn check_record_expr_fields( + &mut self, + adt_ty: Ty<'db>, + expected: &Expectation<'db>, + expr: ExprId, + variant: VariantId, + hir_fields: &[RecordLitField], + base_expr: RecordSpread, + ) { + let interner = self.interner(); + + let adt_ty = self.table.try_structurally_resolve_type(expr.into(), adt_ty); + let adt_ty_hint = expected.only_has_type(&mut self.table).and_then(|expected| { + self.infcx() + .fudge_inference_if_ok(|| { + let mut ocx = ObligationCtxt::new(self.infcx()); + ocx.sup(&ObligationCause::new(expr), self.table.param_env, expected, adt_ty)?; + if !ocx.try_evaluate_obligations().is_empty() { + return Err(TypeError::Mismatch); + } + Ok(self.resolve_vars_if_possible(adt_ty)) + }) + .ok() + }); + if let Some(adt_ty_hint) = adt_ty_hint { + // re-link the variables that the fudging above can create. + _ = self.demand_eqtype(expr.into(), adt_ty_hint, adt_ty); + } + + let TyKind::Adt(adt, args) = adt_ty.kind() else { + never!("non-ADT passed to check_struct_expr_fields"); + return; + }; + let adt_id = adt.def_id(); + + let variant_fields = variant.fields(self.db); + let variant_field_tys = self.db.field_types(variant); + let variant_field_vis = VariantFields::field_visibilities(self.db, variant); + let mut remaining_fields = variant_fields + .fields() + .iter() + .map(|(i, field)| (field.name.clone(), i)) + .collect::<FxHashMap<_, _>>(); + + let mut seen_fields = FxHashMap::default(); + + // Type-check each field. + for field in hir_fields { + let name = &field.name; + let field_type = if let Some(i) = remaining_fields.remove(name) { + seen_fields.insert(name, i); + + if !self.resolver.is_visible(self.db, variant_field_vis[i]) { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: field.expr.into(), + private: Some(i), + variant, + }); + } + + variant_field_tys[i].get().instantiate(interner, args).skip_norm_wip() + } else { + if let Some(field_idx) = seen_fields.get(&name) { + self.push_diagnostic(InferenceDiagnostic::DuplicateField { + field: field.expr.into(), + variant, + }); + variant_field_tys[*field_idx].get().instantiate(interner, args).skip_norm_wip() + } else { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: field.expr.into(), + private: None, + variant, + }); + self.types.types.error + } + }; + + // Check that the expected field type is WF. Otherwise, we emit no use-site error + // in the case of coercions for non-WF fields, which leads to incorrect error + // tainting. See issue #126272. + self.table.register_wf_obligation(field_type.into(), ObligationCause::new(field.expr)); + + // Make sure to give a type to the field even if there's + // an error, so we can continue type-checking. + self.infer_expr_coerce(field.expr, &Expectation::has_type(field_type), ExprIsRead::Yes); + } + + // Make sure the programmer specified correct number of fields. + if matches!(adt_id, AdtId::UnionId(_)) && hir_fields.len() != 1 { + self.push_diagnostic(InferenceDiagnostic::UnionExprMustHaveExactlyOneField { expr }); + } + + match base_expr { + RecordSpread::FieldDefaults => { + let mut missing_mandatory_fields = Vec::new(); + let mut missing_optional_fields = Vec::new(); + for (field_idx, field) in variant_fields.fields().iter() { + if remaining_fields.remove(&field.name).is_some() { + if field.default_value.is_none() { + missing_mandatory_fields.push(field_idx); + } else { + missing_optional_fields.push(field_idx); + } + } + } + if !missing_mandatory_fields.is_empty() { + // FIXME: Emit an error: missing fields. + } + } + RecordSpread::Expr(base_expr) => { + // FIXME: We are currently creating two branches here in order to maintain + // consistency. But they should be merged as much as possible. + if self.features.type_changing_struct_update { + if matches!(adt_id, AdtId::StructId(_)) { + // Make some fresh generic parameters for our ADT type. + let fresh_args = self.table.fresh_args_for_item(expr.into(), adt_id.into()); + // We do subtyping on the FRU fields first, so we can + // learn exactly what types we expect the base expr + // needs constrained to be compatible with the struct + // type we expect from the expectation value. + for (field_idx, field) in variant_fields.fields().iter() { + let fru_ty = variant_field_tys[field_idx] + .get() + .instantiate(interner, fresh_args) + .skip_norm_wip(); + if remaining_fields.remove(&field.name).is_some() { + let target_ty = variant_field_tys[field_idx] + .get() + .instantiate(interner, args) + .skip_norm_wip(); + let cause = ObligationCause::new(expr); + match self.table.at(&cause).sup(target_ty, fru_ty) { + Ok(InferOk { obligations, value: () }) => { + self.table.register_predicates(obligations) + } + Err(_) => { + never!( + "subtyping remaining fields of type changing FRU \ + failed: {target_ty:?} != {fru_ty:?}: {:?}", + field.name, + ); + } + } + } + } + // The use of fresh args that we have subtyped against + // our base ADT type's fields allows us to guide inference + // along so that, e.g. + // ``` + // MyStruct<'a, F1, F2, const C: usize> { + // f: F1, + // // Other fields that reference `'a`, `F2`, and `C` + // } + // + // let x = MyStruct { + // f: 1usize, + // ..other_struct + // }; + // ``` + // will have the `other_struct` expression constrained to + // `MyStruct<'a, _, F2, C>`, as opposed to just `_`... + // This is important to allow coercions to happen in + // `other_struct` itself. See `coerce-in-base-expr.rs`. + let fresh_base_ty = Ty::new_adt(self.interner(), adt_id, fresh_args); + self.infer_expr_suptype_coerce_never( + base_expr, + &Expectation::has_type(self.resolve_vars_if_possible(fresh_base_ty)), + ExprIsRead::Yes, + ); + } else { + // Check the base_expr, regardless of a bad expected adt_ty, so we can get + // type errors on that expression, too. + self.infer_expr_no_expect(base_expr, ExprIsRead::Yes); + self.push_diagnostic( + InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr }, + ); + } + } else { + self.infer_expr_suptype_coerce_never( + base_expr, + &Expectation::has_type(adt_ty), + ExprIsRead::Yes, + ); + if !matches!(adt_id, AdtId::StructId(_)) { + self.push_diagnostic( + InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr }, + ); + } + } + } + RecordSpread::None => { + if !matches!(adt_id, AdtId::UnionId(_)) + && !remaining_fields.is_empty() + //~ non_exhaustive already reported, which will only happen for extern modules + && !self.has_applicable_non_exhaustive(adt_id.into()) + { + debug!(?remaining_fields); + + // FIXME: Emit an error: missing fields. + } + } + } + } + fn demand_scrutinee_type( &mut self, scrut: ExprId, @@ -1114,16 +1253,16 @@ impl<'db> InferenceContext<'_, 'db> { // ...but otherwise we want to use any supertype of the // scrutinee. This is sort of a workaround, see note (*) in // `check_pat` for some details. - let scrut_ty = self.table.next_ty_var(); + let scrut_ty = self.table.next_ty_var(scrut.into()); self.infer_expr_coerce_never(scrut, &Expectation::HasType(scrut_ty), scrutinee_is_read); scrut_ty } } fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty<'db> { - let g = self.resolver.update_to_inner_scope(self.db, self.owner, scope_id); + let g = self.resolver.update_to_inner_scope(self.db, self.store_owner, scope_id); let ty = match self.infer_path(path, id) { - Some(ty) => ty, + Some((_, ty)) => ty, None => { if path.mod_path().is_some_and(|mod_path| mod_path.is_ident() || mod_path.is_self()) { @@ -1149,7 +1288,7 @@ impl<'db> InferenceContext<'_, 'db> { }; let mut oprnd_t = self.infer_expr_inner(oprnd, expected_inner, ExprIsRead::Yes); - oprnd_t = self.table.structurally_resolve_type(oprnd_t); + oprnd_t = self.structurally_resolve_type(oprnd.into(), oprnd_t); match unop { UnaryOp::Deref => { if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { @@ -1176,63 +1315,83 @@ impl<'db> InferenceContext<'_, 'db> { } oprnd_t } - fn infer_expr_array(&mut self, array: &Array, expected: &Expectation<'db>) -> Ty<'db> { - let elem_ty = match expected - .to_option(&mut self.table) - .map(|t| self.table.try_structurally_resolve_type(t).kind()) - { - Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st, - _ => self.table.next_ty_var(), - }; - let krate = self.resolver.krate(); + fn infer_array_repeat_expr( + &mut self, + element: ExprId, + count: ExprId, + expected: &Expectation<'db>, + expr: ExprId, + ) -> Ty<'db> { + let interner = self.interner(); + let count_ct = self.create_body_anon_const(count, self.types.types.usize, true); + let count = self.table.try_structurally_resolve_const(count.into(), count_ct); + + let uty = match expected { + Expectation::HasType(uty) => uty.builtin_index(), + _ => None, + }; - let expected = Expectation::has_type(elem_ty); - let (elem_ty, len) = match array { - Array::ElementList { elements, .. } if elements.is_empty() => { - (elem_ty, consteval::usize_const(self.db, Some(0), krate)) + let t = match uty { + Some(uty) => { + self.infer_expr_coerce(element, &Expectation::has_type(uty), ExprIsRead::Yes); + uty } - Array::ElementList { elements, .. } => { - let mut coerce = CoerceMany::with_coercion_sites(elem_ty, elements); - for &expr in elements.iter() { - let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes); - coerce.coerce( - self, - &ObligationCause::new(), - expr, - cur_elem_ty, - ExprIsRead::Yes, - ); - } - ( - coerce.complete(self), - consteval::usize_const(self.db, Some(elements.len() as u128), krate), - ) + None => { + let ty = self.table.next_ty_var(element.into()); + self.infer_expr(element, &Expectation::has_type(ty), ExprIsRead::Yes); + ty } - &Array::Repeat { initializer, repeat } => { - self.infer_expr_coerce( - initializer, - &Expectation::has_type(elem_ty), - ExprIsRead::Yes, - ); - let usize = self.types.types.usize; - let len = match self.store[repeat] { - Expr::Underscore => { - self.write_expr_ty(repeat, usize); - self.table.next_const_var() - } - _ => { - self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes); - consteval::eval_to_const(repeat, self) - } - }; + }; + + // We defer checking whether the element type is `Copy` as it is possible to have + // an inference variable as a repeat count and it seems unlikely that `Copy` would + // have inference side effects required for type checking to succeed. + // FIXME: Do it here like rustc. + // self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count)); + + let ty = Ty::new_array_with_const_len(interner, t, count); + self.table.register_wf_obligation(ty.into(), ObligationCause::new(expr)); + ty + } - (elem_ty, len) + fn infer_array_elements_expr( + &mut self, + args: &[ExprId], + expected: &Expectation<'db>, + expr: ExprId, + ) -> Ty<'db> { + let element_ty = if !args.is_empty() { + let coerce_to = expected + .to_option(&self.table) + .and_then(|uty| { + self.table + .resolve_vars_with_obligations(uty) + .builtin_index() + // Avoid using the original type variable as the coerce_to type, as it may resolve + // during the first coercion instead of being the LUB type. + .filter(|t| !self.table.resolve_vars_with_obligations(*t).is_ty_var()) + }) + .unwrap_or_else(|| self.table.next_ty_var(expr.into())); + let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); + + for &e in args { + // FIXME: the element expectation should use + // `try_structurally_resolve_and_adjust_for_branches` just like in `if` and `match`. + // While that fixes nested coercion, it will break [some + // code like this](https://github.com/rust-lang/rust/pull/140283#issuecomment-2958776528). + // If we find a way to support recursive tuple coercion, this break can be avoided. + let e_ty = + self.infer_expr_inner(e, &Expectation::has_type(coerce_to), ExprIsRead::Yes); + let cause = ObligationCause::new(e); + coerce.coerce(self, &cause, e, e_ty, ExprIsRead::Yes); } + coerce.complete(self) + } else { + self.table.next_ty_var(expr.into()) }; - // Try to evaluate unevaluated constant, and insert variable if is not possible. - let len = self.table.insert_const_vars_shallow(len); - Ty::new_array_with_const_len(self.interner(), elem_ty, len) + let array_len = args.len() as u128; + Ty::new_array(self.interner(), element_ty, array_len) } pub(super) fn infer_return(&mut self, expr: ExprId) { @@ -1244,7 +1403,13 @@ impl<'db> InferenceContext<'_, 'db> { let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes); let mut coerce_many = self.return_coercion.take().unwrap(); - coerce_many.coerce(self, &ObligationCause::new(), expr, return_expr_ty, ExprIsRead::Yes); + coerce_many.coerce( + self, + &ObligationCause::new(expr), + expr, + return_expr_ty, + ExprIsRead::Yes, + ); self.return_coercion = Some(coerce_many); } @@ -1258,7 +1423,7 @@ impl<'db> InferenceContext<'_, 'db> { coerce.coerce_forced_unit( self, ret, - &ObligationCause::new(), + &ObligationCause::new(ret), true, ExprIsRead::Yes, ); @@ -1285,7 +1450,7 @@ impl<'db> InferenceContext<'_, 'db> { // NB: this should *not* coerce. // tail calls don't support any coercions except lifetimes ones (like `&'static u8 -> &'a u8`). - self.unify(call_expr_ty, ret_ty); + _ = self.demand_eqtype(expr.into(), call_expr_ty, ret_ty); } None => { // FIXME: diagnose `become` outside of functions @@ -1311,16 +1476,7 @@ impl<'db> InferenceContext<'_, 'db> { .unwrap_or_else(Expectation::none); let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp, ExprIsRead::Yes); - Ty::new_adt( - self.interner(), - box_id, - GenericArgs::fill_with_defaults( - self.interner(), - box_id.into(), - [inner_ty.into()], - |_, id, _| self.table.next_var_for_param(id), - ), - ) + Ty::new_box(self.interner(), inner_ty) } else { self.err_ty() } @@ -1334,8 +1490,8 @@ impl<'db> InferenceContext<'_, 'db> { label: Option<LabelId>, expected: &Expectation<'db>, ) -> Ty<'db> { - let coerce_ty = expected.coercion_target_type(&mut self.table); - let g = self.resolver.update_to_inner_scope(self.db, self.owner, expr); + let coerce_ty = expected.coercion_target_type(&mut self.table, expr.into()); + let g = self.resolver.update_to_inner_scope(self.db, self.store_owner, expr); let (break_ty, ty) = self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty), label, |this| { @@ -1345,7 +1501,7 @@ impl<'db> InferenceContext<'_, 'db> { let decl_ty = type_ref .as_ref() .map(|&tr| this.make_body_ty(tr)) - .unwrap_or_else(|| this.table.next_ty_var()); + .unwrap_or_else(|| this.table.next_ty_var((*pat).into())); let ty = if let Some(expr) = initializer { // If we have a subpattern that performs a read, we want to consider this @@ -1356,7 +1512,7 @@ impl<'db> InferenceContext<'_, 'db> { } else { ExprIsRead::No }; - let ty = if contains_explicit_ref_binding(this.store, *pat) { + let ty = if this.contains_explicit_ref_binding(*pat) { this.infer_expr( *expr, &Expectation::has_type(decl_ty), @@ -1374,11 +1530,11 @@ impl<'db> InferenceContext<'_, 'db> { decl_ty }; - let decl = DeclContext { - origin: DeclOrigin::LocalDecl { has_else: else_branch.is_some() }, - }; - - this.infer_top_pat(*pat, ty, Some(decl)); + this.infer_top_pat( + *pat, + ty, + PatOrigin::LetStmt { has_else: else_branch.is_some() }, + ); if let Some(expr) = else_branch { let previous_diverges = mem::replace(&mut this.diverges, Diverges::Maybe); @@ -1418,11 +1574,11 @@ impl<'db> InferenceContext<'_, 'db> { // `!`). if this.diverges.is_always() { // we don't even make an attempt at coercion - this.table.new_maybe_never_var() + this.table.new_maybe_never_var(expr.into()) } else if let Some(t) = expected.only_has_type(&mut this.table) { if this .coerce( - expr.into(), + expr, this.types.types.unit, t, AllowTwoPhase::No, @@ -1430,13 +1586,7 @@ impl<'db> InferenceContext<'_, 'db> { ) .is_err() { - this.result.type_mismatches.get_or_insert_default().insert( - expr.into(), - TypeMismatch { - expected: t.store(), - actual: this.types.types.unit.store(), - }, - ); + this.emit_type_mismatch(expr.into(), t, this.types.types.unit); } t } else { @@ -1451,11 +1601,12 @@ impl<'db> InferenceContext<'_, 'db> { fn lookup_field( &mut self, + field_expr: ExprId, receiver_ty: Ty<'db>, name: &Name, ) -> Option<(Ty<'db>, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> { let interner = self.interner(); - let mut autoderef = self.table.autoderef_with_tracking(receiver_ty); + let mut autoderef = self.table.autoderef_with_tracking(receiver_ty, field_expr.into()); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { let (field_id, parameters) = match derefed_ty.kind() { @@ -1474,7 +1625,7 @@ impl<'db> InferenceContext<'_, 'db> { }) }); } - TyKind::Adt(adt, parameters) => match adt.def_id().0 { + TyKind::Adt(adt, parameters) => match adt.def_id() { hir_def::AdtId::StructId(s) => { let local_id = s.fields(self.db).field(name)?; let field = FieldId { parent: s.into(), local_id }; @@ -1500,7 +1651,8 @@ impl<'db> InferenceContext<'_, 'db> { } let ty = self.db.field_types(field_id.parent)[field_id.local_id] .get() - .instantiate(interner, parameters); + .instantiate(interner, parameters) + .skip_norm_wip(); Some((Either::Left(field_id), ty)) }); @@ -1518,7 +1670,8 @@ impl<'db> InferenceContext<'_, 'db> { self.table.register_infer_ok(autoderef.adjust_steps_as_infer_ok()); let ty = self.db.field_types(field_id.parent)[field_id.local_id] .get() - .instantiate(self.interner(), subst); + .instantiate(self.interner(), subst) + .skip_norm_wip(); let ty = self.process_remote_user_written_ty(ty); (ty, Either::Left(field_id), adjustments, false) @@ -1535,7 +1688,7 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { // Field projections don't constitute reads. let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No); - let receiver_ty = self.table.structurally_resolve_type(receiver_ty); + let receiver_ty = self.structurally_resolve_type(receiver.into(), receiver_ty); if name.is_missing() { // Bail out early, don't even try to look up field. Also, we don't issue an unresolved @@ -1543,7 +1696,7 @@ impl<'db> InferenceContext<'_, 'db> { return self.err_ty(); } - match self.lookup_field(receiver_ty, name) { + match self.lookup_field(tgt_expr, receiver_ty, name) { Some((ty, field_id, adjustments, is_public)) => { self.write_expr_adj(receiver, adjustments.into_boxed_slice()); self.result.field_resolutions.insert(tgt_expr, field_id); @@ -1585,14 +1738,21 @@ impl<'db> InferenceContext<'_, 'db> { fn instantiate_erroneous_method(&mut self, def_id: FunctionId) -> MethodCallee<'db> { // FIXME: Using fresh infer vars for the method args isn't optimal, // we can do better by going thorough the full probe/confirm machinery. - let args = self.table.fresh_args_for_item(def_id.into()); - let sig = self.db.callable_item_signature(def_id.into()).instantiate(self.interner(), args); - let sig = - self.infcx().instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, sig); + let args = self.table.fresh_args_for_item(Span::Dummy, def_id.into()); + let sig = self + .db + .callable_item_signature(def_id.into()) + .instantiate(self.interner(), args) + .skip_norm_wip(); + let sig = self.infcx().instantiate_binder_with_fresh_vars( + Span::Dummy, + BoundRegionConversionTime::FnCall, + sig, + ); MethodCallee { def_id, args, sig } } - fn check_call( + fn infer_method_call_as_call( &mut self, tgt_expr: ExprId, args: &[ExprId], @@ -1603,7 +1763,14 @@ impl<'db> InferenceContext<'_, 'db> { is_varargs: bool, expected: &Expectation<'db>, ) -> Ty<'db> { - self.register_obligations_for_call(callee_ty); + if let TyKind::FnDef(def_id, args) = callee_ty.kind() { + let def_id = match def_id.0 { + CallableDefId::FunctionId(it) => it.into(), + CallableDefId::StructId(it) => it.into(), + CallableDefId::EnumVariantId(it) => it.loc(self.db).parent.into(), + }; + self.add_required_obligations_for_value_path(tgt_expr.into(), def_id, args); + } self.check_call_arguments( tgt_expr, @@ -1628,7 +1795,7 @@ impl<'db> InferenceContext<'_, 'db> { expected: &Expectation<'db>, ) -> Ty<'db> { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); - let receiver_ty = self.table.try_structurally_resolve_type(receiver_ty); + let receiver_ty = self.table.try_structurally_resolve_type(receiver.into(), receiver_ty); let resolved = self.lookup_method_including_private( receiver_ty, @@ -1650,30 +1817,31 @@ impl<'db> InferenceContext<'_, 'db> { // Failed to resolve, report diagnostic and try to resolve as call to field access or // assoc function Err(_) => { - let field_with_same_name_exists = match self.lookup_field(receiver_ty, method_name) - { - Some((ty, field_id, adjustments, _public)) => { - self.write_expr_adj(receiver, adjustments.into_boxed_slice()); - self.result.field_resolutions.insert(tgt_expr, field_id); - Some(ty) - } - None => None, - }; + let field_with_same_name_exists = + match self.lookup_field(tgt_expr, receiver_ty, method_name) { + Some((ty, field_id, adjustments, _public)) => { + self.write_expr_adj(receiver, adjustments.into_boxed_slice()); + self.result.field_resolutions.insert(tgt_expr, field_id); + Some(ty) + } + None => None, + }; - let assoc_func_with_same_name = self.with_method_resolution(|ctx| { - if !matches!( - receiver_ty.kind(), - TyKind::Infer(InferTy::TyVar(_)) | TyKind::Error(_) - ) { - ctx.probe_for_name( - method_resolution::Mode::Path, - method_name.clone(), - receiver_ty, - ) - } else { - Err(MethodError::ErrorReported) - } - }); + let assoc_func_with_same_name = + self.with_method_resolution(tgt_expr.into(), receiver.into(), |ctx| { + if !matches!( + receiver_ty.kind(), + TyKind::Infer(InferTy::TyVar(_)) | TyKind::Error(_) + ) { + ctx.probe_for_name( + method_resolution::Mode::Path, + method_name.clone(), + receiver_ty, + ) + } else { + Err(MethodError::ErrorReported) + } + }); let assoc_func_with_same_name = match assoc_func_with_same_name { Ok(method_resolution::Pick { item: CandidateId::FunctionId(def_id), .. @@ -1709,7 +1877,7 @@ impl<'db> InferenceContext<'_, 'db> { }), }; match recovered { - Some((callee_ty, sig, strip_first)) => self.check_call( + Some((callee_ty, sig, strip_first)) => self.infer_method_call_as_call( tgt_expr, args, callee_ty, @@ -1751,7 +1919,7 @@ impl<'db> InferenceContext<'_, 'db> { expected, args, &[], - sig.c_variadic, + sig.c_variadic(), TupleArgumentsFlag::DontTupleArguments, ); ret_ty @@ -1778,7 +1946,7 @@ impl<'db> InferenceContext<'_, 'db> { let formal_input_tys: Vec<_> = formal_input_tys .iter() .map(|&ty| { - let generalized_ty = self.table.next_ty_var(); + let generalized_ty = self.table.next_ty_var(call_expr.into()); let _ = self.demand_eqtype(call_expr.into(), ty, generalized_ty); generalized_ty }) @@ -1801,13 +1969,13 @@ impl<'db> InferenceContext<'_, 'db> { // return type (likely containing type variables if the function // is polymorphic) and the expected return type. // No argument expectations are produced if unification fails. - let origin = ObligationCause::new(); + let origin = ObligationCause::new(call_expr); ocx.sup(&origin, self.table.param_env, expected_output, formal_output)?; for &ty in &formal_input_tys { ocx.register_obligation(Obligation::new( self.interner(), - ObligationCause::new(), + ObligationCause::new(call_expr), self.table.param_env, ClauseKind::WellFormed(ty.into()), )); @@ -1826,38 +1994,39 @@ impl<'db> InferenceContext<'_, 'db> { .unwrap_or_default(); // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here - let (formal_input_tys, expected_input_tys) = - if tuple_arguments == TupleArgumentsFlag::TupleArguments { - let tuple_type = self.table.structurally_resolve_type(formal_input_tys[0]); - match tuple_type.kind() { - // We expected a tuple and got a tuple - TyKind::Tuple(arg_types) => { - // Argument length differs - if arg_types.len() != provided_args.len() { - // FIXME: Emit an error. - } - let expected_input_tys = match expected_input_tys { - Some(expected_input_tys) => match expected_input_tys.first() { - Some(ty) => match ty.kind() { - TyKind::Tuple(tys) => Some(tys.iter().collect()), - _ => None, - }, - None => None, - }, - None => None, - }; - (arg_types.iter().collect(), expected_input_tys) - } - _ => { - // Otherwise, there's a mismatch, so clear out what we're expecting, and set - // our input types to err_args so we don't blow up the error messages + let (formal_input_tys, expected_input_tys) = if tuple_arguments + == TupleArgumentsFlag::TupleArguments + { + let tuple_type = self.structurally_resolve_type(call_expr.into(), formal_input_tys[0]); + match tuple_type.kind() { + // We expected a tuple and got a tuple + TyKind::Tuple(arg_types) => { + // Argument length differs + if arg_types.len() != provided_args.len() { // FIXME: Emit an error. - (vec![self.types.types.error; provided_args.len()], None) } + let expected_input_tys = match expected_input_tys { + Some(expected_input_tys) => match expected_input_tys.first() { + Some(ty) => match ty.kind() { + TyKind::Tuple(tys) => Some(tys.iter().collect()), + _ => None, + }, + None => None, + }, + None => None, + }; + (arg_types.to_vec(), expected_input_tys) } - } else { - (formal_input_tys.to_vec(), expected_input_tys) - }; + _ => { + // Otherwise, there's a mismatch, so clear out what we're expecting, and set + // our input types to err_args so we don't blow up the error messages + // FIXME: Emit an error. + (vec![self.types.types.error; provided_args.len()], None) + } + } + } else { + (formal_input_tys, expected_input_tys) + }; // If there are no external expectations at the call site, just use the types from the function defn let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys { @@ -1916,13 +2085,7 @@ impl<'db> InferenceContext<'_, 'db> { let coerced_ty = this.table.resolve_vars_with_obligations(coerced_ty); let coerce_error = this - .coerce( - provided_arg.into(), - checked_ty, - coerced_ty, - AllowTwoPhase::Yes, - ExprIsRead::Yes, - ) + .coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, ExprIsRead::Yes) .err(); if coerce_error.is_some() { return Err((coerce_error, coerced_ty, checked_ty)); @@ -1933,7 +2096,7 @@ impl<'db> InferenceContext<'_, 'db> { let formal_ty_error = this .table .infer_ctxt - .at(&ObligationCause::new(), this.table.param_env) + .at(&ObligationCause::new(provided_arg), this.table.param_env) .eq(formal_input_ty, coerced_ty); // If neither check failed, the types are compatible @@ -1974,7 +2137,7 @@ impl<'db> InferenceContext<'_, 'db> { // closure wrapped in a block. // See <https://github.com/rust-lang/rust/issues/112225>. let is_closure = if let Expr::Closure { closure_kind, .. } = self.store[*arg] { - !matches!(closure_kind, ClosureKind::Coroutine(_)) + !matches!(closure_kind, ClosureKind::OldCoroutine(_)) } else { false }; @@ -1992,10 +2155,7 @@ impl<'db> InferenceContext<'_, 'db> { && args_count_matches { // Don't report type mismatches if there is a mismatch in args count. - self.result.type_mismatches.get_or_insert_default().insert( - (*arg).into(), - TypeMismatch { expected: expected.store(), actual: found.store() }, - ); + self.emit_type_mismatch((*arg).into(), expected, found); } } } @@ -2003,40 +2163,6 @@ impl<'db> InferenceContext<'_, 'db> { if !args_count_matches {} } - fn register_obligations_for_call(&mut self, callable_ty: Ty<'db>) { - let callable_ty = self.table.try_structurally_resolve_type(callable_ty); - if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind() { - let generic_predicates = GenericPredicates::query_all( - self.db, - GenericDefId::from_callable(self.db, fn_def.0), - ); - let param_env = self.table.param_env; - self.table.register_predicates(clauses_as_obligations( - generic_predicates.iter_instantiated(self.interner(), parameters.as_slice()), - ObligationCause::new(), - param_env, - )); - // add obligation for trait implementation, if this is a trait method - match fn_def.0 { - CallableDefId::FunctionId(f) => { - if let ItemContainerId::TraitId(trait_) = f.lookup(self.db).container { - // construct a TraitRef - let trait_params_len = generics(self.db, trait_.into()).len(); - let substs = - GenericArgs::new_from_slice(¶meters.as_slice()[..trait_params_len]); - self.table.register_predicate(Obligation::new( - self.interner(), - ObligationCause::new(), - self.table.param_env, - TraitRef::new_from_args(self.interner(), trait_.into(), substs), - )); - } - } - CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {} - } - } - } - pub(super) fn with_breakable_ctx<T>( &mut self, kind: BreakableKind, diff --git a/crates/hir-ty/src/infer/fallback.rs b/crates/hir-ty/src/infer/fallback.rs index c7669b346f..3744a434d2 100644 --- a/crates/hir-ty/src/infer/fallback.rs +++ b/crates/hir-ty/src/infer/fallback.rs @@ -1,6 +1,5 @@ //! Fallback of infer vars to `!` and `i32`/`f64`. -use intern::sym; use petgraph::{ Graph, visit::{Dfs, Walker}, @@ -76,11 +75,11 @@ impl<'db> InferenceContext<'_, 'db> { } fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior { - if self.krate().data(self.db).edition.at_least_2024() { + if self.edition.at_least_2024() { return DivergingFallbackBehavior::ToNever; } - if self.resolver.def_map().is_unstable_feature_enabled(&sym::never_type_fallback) { + if self.features.never_type_fallback { return DivergingFallbackBehavior::ContextDependent; } diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index b2369f6a87..c3b532638f 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -32,6 +32,7 @@ impl<'db> InferenceContext<'_, 'db> { }; if let Some(infer_ok) = Self::try_mutable_overloaded_place_op( &self.table, + tgt_expr, source_ty, None, PlaceOp::Deref, diff --git a/crates/hir-ty/src/infer/op.rs b/crates/hir-ty/src/infer/op.rs index 95d63ffb50..9119af9628 100644 --- a/crates/hir-ty/src/infer/op.rs +++ b/crates/hir-ty/src/infer/op.rs @@ -2,8 +2,7 @@ use std::collections::hash_map; -use hir_def::{GenericParamId, TraitId, hir::ExprId}; -use intern::{Symbol, sym}; +use hir_def::{FunctionId, GenericParamId, TraitId, hir::ExprId}; use rustc_ast_ir::Mutability; use rustc_type_ir::inherent::{IntoKind, Ty as _}; use syntax::ast::{ArithOp, BinaryOp, UnaryOp}; @@ -38,7 +37,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, category) { - self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category); + self.enforce_builtin_binop_types(expr, lhs_ty, rhs_ty, category); self.types.types.unit } else { return_ty @@ -107,7 +106,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { && is_builtin_binop(lhs_ty, rhs_ty, category) { let builtin_return_ty = - self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category); + self.enforce_builtin_binop_types(expr, lhs_ty, rhs_ty, category); _ = self.demand_eqtype(expr.into(), builtin_return_ty, return_ty); builtin_return_ty } else { @@ -119,6 +118,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { fn enforce_builtin_binop_types( &mut self, + expr: ExprId, lhs_ty: Ty<'db>, rhs_ty: Ty<'db>, category: BinOpCategory, @@ -131,8 +131,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { match category { BinOpCategory::Shortcircuit => { - self.demand_suptype(self.types.types.bool, lhs_ty); - self.demand_suptype(self.types.types.bool, rhs_ty); + _ = self.demand_suptype(expr.into(), self.types.types.bool, lhs_ty); + _ = self.demand_suptype(expr.into(), self.types.types.bool, rhs_ty); self.types.types.bool } @@ -143,13 +143,13 @@ impl<'a, 'db> InferenceContext<'a, 'db> { BinOpCategory::Math | BinOpCategory::Bitwise => { // both LHS and RHS and result will have the same type - self.demand_suptype(lhs_ty, rhs_ty); + _ = self.demand_suptype(expr.into(), lhs_ty, rhs_ty); lhs_ty } BinOpCategory::Comparison => { // both LHS and RHS and result will have the same type - self.demand_suptype(lhs_ty, rhs_ty); + _ = self.demand_suptype(expr.into(), lhs_ty, rhs_ty); self.types.types.bool } } @@ -179,7 +179,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. let lhs_ty = self.infer_expr_no_expect(lhs_expr, ExprIsRead::Yes); - let fresh_var = self.table.next_ty_var(); + let fresh_var = self.table.next_ty_var(lhs_expr.into()); self.demand_coerce(lhs_expr, lhs_ty, fresh_var, AllowTwoPhase::No, ExprIsRead::Yes) } }; @@ -191,8 +191,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // using this variable as the expected type, which sometimes lets // us do better coercions than we would be able to do otherwise, // particularly for things like `String + &String`. - let rhs_ty_var = self.table.next_ty_var(); + let rhs_ty_var = self.table.next_ty_var(rhs_expr.into()); let result = self.lookup_op_method( + expr, lhs_ty, Some((rhs_expr, rhs_ty_var)), self.lang_item_for_bin_op(op), @@ -264,7 +265,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { operand_ty: Ty<'db>, op: UnaryOp, ) -> Ty<'db> { - match self.lookup_op_method(operand_ty, None, self.lang_item_for_unop(op)) { + match self.lookup_op_method(ex, operand_ty, None, self.lang_item_for_unop(op)) { Ok(method) => { self.write_method_resolution(ex, method.def_id, method.args); method.sig.output() @@ -278,31 +279,32 @@ impl<'a, 'db> InferenceContext<'a, 'db> { fn lookup_op_method( &mut self, + expr: ExprId, lhs_ty: Ty<'db>, opt_rhs: Option<(ExprId, Ty<'db>)>, - (opname, trait_did): (Symbol, Option<TraitId>), + (op_method, trait_did): (Option<FunctionId>, Option<TraitId>), ) -> Result<MethodCallee<'db>, Vec<NextSolverError<'db>>> { - let Some(trait_did) = trait_did else { + let (Some(trait_did), Some(op_method)) = (trait_did, op_method) else { // Bail if the operator trait is not defined. return Err(vec![]); }; debug!( "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})", - lhs_ty, opname, trait_did + lhs_ty, op_method, trait_did ); let opt_rhs_ty = opt_rhs.map(|it| it.1); - let cause = ObligationCause::new(); + let cause = ObligationCause::new(expr); // We don't consider any other candidates if this lookup fails // so we can freely treat opaque types as inference variables here // to allow more code to compile. let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; let method = self.table.lookup_method_for_operator( - cause.clone(), - opname, + cause, trait_did, + op_method, lhs_ty, opt_rhs_ty, treat_opaques, @@ -357,20 +359,20 @@ impl<'a, 'db> InferenceContext<'a, 'db> { } } - fn lang_item_for_bin_op(&self, op: BinaryOp) -> (Symbol, Option<TraitId>) { - let (method_name, trait_lang_item) = + fn lang_item_for_bin_op(&self, op: BinaryOp) -> (Option<FunctionId>, Option<TraitId>) { + let (method, trait_lang_item) = crate::lang_items::lang_items_for_bin_op(self.lang_items, op) .expect("invalid operator provided"); - (method_name, trait_lang_item) + (method, trait_lang_item) } - fn lang_item_for_unop(&self, op: UnaryOp) -> (Symbol, Option<TraitId>) { - let (method_name, trait_lang_item) = match op { - UnaryOp::Not => (sym::not, self.lang_items.Not), - UnaryOp::Neg => (sym::neg, self.lang_items.Neg), + fn lang_item_for_unop(&self, op: UnaryOp) -> (Option<FunctionId>, Option<TraitId>) { + let (method, trait_lang_item) = match op { + UnaryOp::Not => (self.lang_items.Not_not, self.lang_items.Not), + UnaryOp::Neg => (self.lang_items.Neg_neg, self.lang_items.Neg), UnaryOp::Deref => panic!("Deref is not overloadable"), }; - (method_name, trait_lang_item) + (method, trait_lang_item) } } diff --git a/crates/hir-ty/src/infer/opaques.rs b/crates/hir-ty/src/infer/opaques.rs index a39288721b..63149deb82 100644 --- a/crates/hir-ty/src/infer/opaques.rs +++ b/crates/hir-ty/src/infer/opaques.rs @@ -4,6 +4,7 @@ use rustc_type_ir::{TypeVisitableExt, fold_regions}; use tracing::{debug, instrument}; use crate::{ + Span, infer::InferenceContext, next_solver::{ EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode, @@ -68,13 +69,13 @@ impl<'db> InferenceContext<'_, 'db> { mut opaque_types: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>, ) { for entry in opaque_types.iter_mut() { - *entry = self.table.infer_ctxt.resolve_vars_if_possible(*entry); + *entry = self.resolve_vars_if_possible(*entry); } debug!(?opaque_types); let interner = self.interner(); let TypingMode::Analysis { defining_opaque_types_and_generators } = - self.table.infer_ctxt.typing_mode() + self.table.infer_ctxt.typing_mode_raw() else { unreachable!(); }; @@ -107,8 +108,9 @@ impl<'db> InferenceContext<'_, 'db> { continue; } - let expected = - EarlyBinder::bind(ty.ty).instantiate(interner, opaque_type_key.args); + let expected = EarlyBinder::bind(ty.ty) + .instantiate(interner, opaque_type_key.args) + .skip_norm_wip(); _ = self.demand_eqtype_fixme_no_diag(expected, hidden_type.ty); } @@ -135,7 +137,8 @@ impl<'db> InferenceContext<'_, 'db> { return UsageKind::UnconstrainedHiddenType(hidden_type); } - let cause = ObligationCause::new(); + // FIXME: This should not use a dummy span. + let cause = ObligationCause::new(Span::Dummy); let at = self.table.infer_ctxt.at(&cause, self.table.param_env); let hidden_type = match at.deeply_normalize(hidden_type) { Ok(hidden_type) => hidden_type, diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 8033680dcc..f21438647c 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -1,647 +1,1545 @@ //! Type inference for patterns. -use std::{cmp, iter}; +use std::{ + cmp, + collections::hash_map::Entry::{Occupied, Vacant}, + iter, +}; use hir_def::{ - HasModule as _, - expr_store::{ExpressionStore, path::Path}, - hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId}, + AdtId, LocalFieldId, VariantId, + expr_store::path::Path, + hir::{ + BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId, + RecordFieldPat, + }, + resolver::ValueNs, signatures::VariantFields, }; -use hir_expand::name::Name; use rustc_ast_ir::Mutability; -use rustc_type_ir::inherent::{GenericArg as _, GenericArgs as _, IntoKind, Ty as _}; -use stdx::TupleExt; +use rustc_hash::FxHashMap; +use rustc_type_ir::{ + TypeVisitableExt as _, + inherent::{IntoKind as _, Ty as _}, +}; +use span::Edition; +use tracing::{debug, instrument, trace}; use crate::{ - DeclContext, DeclOrigin, InferenceDiagnostic, - consteval::{self, try_const_usize, usize_const}, + BindingMode, InferenceDiagnostic, Span, infer::{ - AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch, expr::ExprIsRead, + AllowTwoPhase, ByRef, Expectation, InferenceContext, PatAdjust, PatAdjustment, + expr::ExprIsRead, + }, + next_solver::{ + Const, TraitRef, Ty, TyKind, Tys, + infer::{ + InferOk, + traits::{Obligation, ObligationCause}, + }, }, - lower::lower_mutability, - next_solver::{GenericArgs, Ty, TyKind, Tys, infer::traits::ObligationCause}, + utils::EnumerateAndAdjustIterator, }; -impl<'db> InferenceContext<'_, 'db> { - /// Infers type for tuple struct pattern or its corresponding assignee expression. +impl ByRef { + #[must_use] + fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self { + if let ByRef::Yes(old_mutbl) = &mut self { + *old_mutbl = cmp::min(*old_mutbl, mutbl); + } + self + } +} + +impl BindingMode { + fn from_annotation(annotation: BindingAnnotation) -> BindingMode { + match annotation { + BindingAnnotation::Unannotated => BindingMode(ByRef::No, Mutability::Not), + BindingAnnotation::Mutable => BindingMode(ByRef::No, Mutability::Mut), + BindingAnnotation::Ref => BindingMode(ByRef::Yes(Mutability::Not), Mutability::Not), + BindingAnnotation::RefMut => BindingMode(ByRef::Yes(Mutability::Mut), Mutability::Not), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub(super) enum PatOrigin { + LetExpr, + LetStmt { has_else: bool }, + Param, + MatchArm, + DestructuringAssignment, +} + +impl PatOrigin { + fn default_binding_modes(self) -> bool { + self != PatOrigin::DestructuringAssignment + } +} + +#[derive(Copy, Clone)] +struct PatInfo { + binding_mode: ByRef, + max_ref_mutbl: MutblCap, + pat_origin: PatOrigin, +} + +/// Mode for adjusting the expected type and binding mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum AdjustMode { + /// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this + /// also peels smart pointer ADTs. + Peel { kind: PeelKind }, + /// Pass on the input binding mode and expected type. + Pass, +} + +/// Restrictions on what types to peel when adjusting the expected type and binding mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum PeelKind { + /// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference + /// any number of `&`/`&mut` references, plus a single smart pointer. + ExplicitDerefPat, + /// Implicitly peel references, and if `deref_patterns` is enabled, smart pointer ADTs. + Implicit { + /// The ADT the pattern is a constructor for, if applicable, so that we don't peel it. See + /// [`ResolvedPat`] for more information. + until_adt: Option<AdtId>, + /// The number of references at the head of the pattern's type, so we can leave that many + /// untouched. This is `1` for string literals, and `0` for most patterns. + pat_ref_layers: usize, + }, +} + +impl AdjustMode { + const fn peel_until_adt(opt_adt_def: Option<AdtId>) -> AdjustMode { + AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def, pat_ref_layers: 0 } } + } + const fn peel_all() -> AdjustMode { + AdjustMode::peel_until_adt(None) + } +} + +/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference. +/// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics, +/// we track this when typing patterns for two purposes: +/// +/// - For RFC 3627's Rule 3, when this would prevent us from binding with `ref mut`, we limit the +/// default binding mode to be by shared `ref` when it would otherwise be `ref mut`. +/// +/// - For RFC 3627's Rule 5, we allow `&` patterns to match against `&mut` references, treating them +/// as if they were shared references. Since the scrutinee is mutable in this case, the borrow +/// checker won't catch if we bind with `ref mut`, so we need to throw an error ourselves. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum MutblCap { + /// Mutability restricted to immutable. + Not, + + /// Mutability restricted to immutable, but only because of the pattern + /// (not the scrutinee type). /// - /// Ellipses found in the original pattern or expression must be filtered out. - pub(super) fn infer_tuple_struct_pat_like( + /// The contained span, if present, points to an `&` pattern + /// that is the reason for the restriction, + /// and which will be reported in a diagnostic. + WeaklyNot, + + /// No restriction on mutability + Mut, +} + +impl MutblCap { + #[must_use] + fn cap_to_weakly_not(self) -> Self { + match self { + MutblCap::Not => MutblCap::Not, + _ => MutblCap::WeaklyNot, + } + } + + #[must_use] + fn as_mutbl(self) -> Mutability { + match self { + MutblCap::Not | MutblCap::WeaklyNot => Mutability::Not, + MutblCap::Mut => Mutability::Mut, + } + } +} + +/// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references? +/// +/// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e. +/// from matching a reference type with a non-reference pattern. E.g. when `Some(x)` matches on +/// `&mut Option<&T>`, `x` gets type `&mut &T` and the outer `&mut` is considered "inherited". +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum InheritedRefMatchRule { + /// Reference patterns consume only the inherited reference if possible, regardless of whether + /// the underlying type being matched against is a reference type. If there is no inherited + /// reference, a reference will be consumed from the underlying type. + EatOuter, + /// Reference patterns consume only a reference from the underlying type if possible. If the + /// underlying type is not a reference type, the inherited reference will be consumed. + EatInner, + /// When the underlying type is a reference type, reference patterns consume both layers of + /// reference, i.e. they both reset the binding mode and consume the reference type. + EatBoth { + /// If `true`, an inherited reference will be considered when determining whether a reference + /// pattern matches a given type: + /// - If the underlying type is not a reference, a reference pattern may eat the inherited reference; + /// - If the underlying type is a reference, a reference pattern matches if it can eat either one + /// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the + /// underlying type is `&mut` or the inherited reference is `&mut`. + /// + /// If `false`, a reference pattern is only matched against the underlying type. + /// This is `false` for stable Rust and `true` for both the `ref_pat_eat_one_layer_2024` and + /// `ref_pat_eat_one_layer_2024_structural` feature gates. + consider_inherited_ref: bool, + }, +} + +/// When checking patterns containing paths, we need to know the path's resolution to determine +/// whether to apply match ergonomics and implicitly dereference the scrutinee. For instance, when +/// the `deref_patterns` feature is enabled and we're matching against a scrutinee of type +/// `Cow<'a, Option<u8>>`, we insert an implicit dereference to allow the pattern `Some(_)` to type, +/// but we must not dereference it when checking the pattern `Cow::Borrowed(_)`. +/// +/// `ResolvedPat` contains the information from resolution needed to determine match ergonomics +/// adjustments, and to finish checking the pattern once we know its adjusted type. +#[derive(Clone, Copy, Debug)] +struct ResolvedPat<'db> { + /// The type of the pattern, to be checked against the type of the scrutinee after peeling. This + /// is also used to avoid peeling the scrutinee's constructors (see the `Cow` example above). + ty: Ty<'db>, + kind: ResolvedPatKind, +} + +#[derive(Clone, Copy, Debug)] +enum ResolvedPatKind { + Path { res: ValueNs }, + Struct { variant: VariantId }, + TupleStruct { variant: VariantId }, +} + +impl<'db> ResolvedPat<'db> { + fn adjust_mode(&self) -> AdjustMode { + if let ResolvedPatKind::Path { res, .. } = self.kind + && matches!(res, ValueNs::ConstId(_)) + { + // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. + // Peeling the reference types too early will cause type checking failures. + // Although it would be possible to *also* peel the types of the constants too. + AdjustMode::Pass + } else { + // The remaining possible resolutions for path, struct, and tuple struct patterns are + // ADT constructors. As such, we may peel references freely, but we must not peel the + // ADT itself from the scrutinee if it's a smart pointer. + AdjustMode::peel_until_adt(self.ty.as_adt().map(|(adt, _)| adt)) + } + } +} + +impl<'a, 'db> InferenceContext<'a, 'db> { + /// Experimental pattern feature: after matching against a shared reference, do we limit the + /// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`? + /// This corresponds to Rule 3 of RFC 3627. + fn downgrade_mut_inside_shared(&self) -> bool { + // NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior + // across all editions, this may be removed. + self.features.ref_pat_eat_one_layer_2024_structural + } + + /// Experimental pattern feature: when do reference patterns match against inherited references? + /// This corresponds to variations on Rule 4 of RFC 3627. + fn ref_pat_matches_inherited_ref(&self, edition: Edition) -> InheritedRefMatchRule { + // NB: The particular rule used here is likely to differ across editions, so calls to this + // may need to become edition checks after match ergonomics stabilize. + if edition.at_least_2024() { + if self.features.ref_pat_eat_one_layer_2024 { + InheritedRefMatchRule::EatOuter + } else if self.features.ref_pat_eat_one_layer_2024_structural { + InheritedRefMatchRule::EatInner + } else { + // Currently, matching against an inherited ref on edition 2024 is an error. + // Use `EatBoth` as a fallback to be similar to stable Rust. + InheritedRefMatchRule::EatBoth { consider_inherited_ref: false } + } + } else { + InheritedRefMatchRule::EatBoth { + consider_inherited_ref: self.features.ref_pat_eat_one_layer_2024 + || self.features.ref_pat_eat_one_layer_2024_structural, + } + } + } + + /// Experimental pattern feature: do `&` patterns match against `&mut` references, treating them + /// as if they were shared references? This corresponds to Rule 5 of RFC 3627. + fn ref_pat_matches_mut_ref(&self) -> bool { + // NB: RFC 3627 proposes stabilizing Rule 5 in all editions. If we adopt the same behavior + // across all editions, this may be removed. + self.features.ref_pat_eat_one_layer_2024 + || self.features.ref_pat_eat_one_layer_2024_structural + } + + /// Type check the given top level pattern against the `expected` type. + /// + /// If a `Some(span)` is provided and `origin_expr` holds, + /// then the `span` represents the scrutinee's span. + /// The scrutinee is found in e.g. `match scrutinee { ... }` and `let pat = scrutinee;`. + /// + /// Otherwise, `Some(span)` represents the span of a type expression + /// which originated the `expected` type. + pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: Ty<'db>, pat_origin: PatOrigin) { + let pat_info = + PatInfo { binding_mode: ByRef::No, max_ref_mutbl: MutblCap::Mut, pat_origin }; + self.infer_pat(pat, expected, pat_info); + } + + /// Type check the given `pat` against the `expected` type + /// with the provided `binding_mode` (default binding mode). + /// + /// Outside of this module, `check_pat_top` should always be used. + /// Conversely, inside this module, `check_pat_top` should never be used. + #[instrument(level = "debug", skip(self, pat_info))] + fn infer_pat(&mut self, pat_id: PatId, expected: Ty<'db>, pat_info: PatInfo) { + // For patterns containing paths, we need the path's resolution to determine whether to + // implicitly dereference the scrutinee before matching. + let pat = &self.store[pat_id]; + let opt_path_res = match pat { + Pat::Path(path) => Some(self.resolve_pat_path(pat_id, path)), + Pat::Record { path, .. } => Some(self.resolve_record_pat(pat_id, path)), + Pat::TupleStruct { path, .. } => Some(self.resolve_tuple_struct_pat(pat_id, path)), + _ => None, + }; + let adjust_mode = self.calc_adjust_mode(pat_id, pat, opt_path_res); + let ty = self.infer_pat_inner(pat_id, opt_path_res, adjust_mode, expected, pat_info); + let ty = self.insert_type_vars_shallow(ty); + self.write_pat_ty(pat_id, ty); + + // If we implicitly inserted overloaded dereferences before matching check the pattern to + // see if the dereferenced types need `DerefMut` bounds. + if let Some(derefed_tys) = self.result.pat_adjustment(pat_id) + && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref) + { + let infer_ok = self.register_deref_mut_bounds_if_needed( + pat_id, + pat_id, + derefed_tys.iter().filter_map(|adjust| match adjust.kind { + PatAdjust::OverloadedDeref => Some(adjust.source.as_ref()), + PatAdjust::BuiltinDeref => None, + }), + ); + self.table.register_infer_ok(infer_ok); + } + + // (note_1): In most of the cases where (note_1) is referenced + // (literals and constants being the exception), we relate types + // using strict equality, even though subtyping would be sufficient. + // There are a few reasons for this, some of which are fairly subtle + // and which cost me (nmatsakis) an hour or two debugging to remember, + // so I thought I'd write them down this time. + // + // 1. There is no loss of expressiveness here, though it does + // cause some inconvenience. What we are saying is that the type + // of `x` becomes *exactly* what is expected. This can cause unnecessary + // errors in some cases, such as this one: + // + // ``` + // fn foo<'x>(x: &'x i32) { + // let a = 1; + // let mut z = x; + // z = &a; + // } + // ``` + // + // The reason we might get an error is that `z` might be + // assigned a type like `&'x i32`, and then we would have + // a problem when we try to assign `&a` to `z`, because + // the lifetime of `&a` (i.e., the enclosing block) is + // shorter than `'x`. + // + // HOWEVER, this code works fine. The reason is that the + // expected type here is whatever type the user wrote, not + // the initializer's type. In this case the user wrote + // nothing, so we are going to create a type variable `Z`. + // Then we will assign the type of the initializer (`&'x i32`) + // as a subtype of `Z`: `&'x i32 <: Z`. And hence we + // will instantiate `Z` as a type `&'0 i32` where `'0` is + // a fresh region variable, with the constraint that `'x : '0`. + // So basically we're all set. + // + // Note that there are two tests to check that this remains true + // (`regions-reassign-{match,let}-bound-pointer.rs`). + // + // 2. An outdated issue related to the old HIR borrowck. See the test + // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, + } + + // Helper to avoid resolving the same path pattern several times. + fn infer_pat_inner( &mut self, - path: Option<&Path>, + pat: PatId, + opt_path_res: Option<Result<ResolvedPat<'db>, ()>>, + adjust_mode: AdjustMode, expected: Ty<'db>, - default_bm: BindingMode, - id: PatId, - ellipsis: Option<u32>, - subs: &[PatId], - decl: Option<DeclContext>, + pat_info: PatInfo, ) -> Ty<'db> { - let (ty, def) = self.resolve_variant(id.into(), path, true); - let var_data = def.map(|it| it.fields(self.db)); - if let Some(variant) = def { - self.write_variant_resolution(id.into(), variant); - } - if let Some(var) = &var_data { - let cmp = if ellipsis.is_some() { usize::gt } else { usize::ne }; - - if cmp(&subs.len(), &var.fields().len()) { - self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount { - pat: id.into(), - expected: var.fields().len(), - found: subs.len(), - }); - } + #[cfg(debug_assertions)] + if matches!(pat_info.binding_mode, ByRef::Yes(Mutability::Mut)) + && pat_info.max_ref_mutbl != MutblCap::Mut + && self.downgrade_mut_inside_shared() + { + panic!("Pattern mutability cap violated!"); } - self.unify(ty, expected); + // Resolve type if needed. + let expected = if let AdjustMode::Peel { .. } = adjust_mode + && pat_info.pat_origin.default_binding_modes() + { + self.table.try_structurally_resolve_type(pat.into(), expected) + } else { + expected + }; + + match self.store[pat] { + // Peel off a `&` or `&mut`from the scrutinee type. See the examples in + // `tests/ui/rfcs/rfc-2005-default-binding-mode`. + _ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode + && pat_info.pat_origin.default_binding_modes() + && let TyKind::Ref(_, inner_ty, inner_mutability) = expected.kind() + && self.should_peel_ref(peel_kind, expected) => + { + debug!("inspecting {:?}", expected); + + debug!("current discriminant is Ref, inserting implicit deref"); + // Preserve the reference type. We'll need it later during THIR lowering. + self.result.pat_adjustments.entry(pat).or_default().push(PatAdjustment { + kind: PatAdjust::BuiltinDeref, + source: expected.store(), + }); - match def { - _ if subs.is_empty() => {} - Some(def) => { - let field_types = self.db.field_types(def); - let variant_data = def.fields(self.db); - let visibilities = VariantFields::field_visibilities(self.db, def); + // Use the old pat info to keep `current_depth` to its old value. + let new_pat_info = self.adjust_pat_info(inner_mutability, pat_info); - let (pre, post) = match ellipsis { - Some(idx) => subs.split_at(idx as usize), - None => (subs, &[][..]), + // Recurse with the new expected type. + self.infer_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info) + } + // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the + // examples in `tests/ui/pattern/deref_patterns/`. + _ if self.features.deref_patterns + && let AdjustMode::Peel { kind: peel_kind } = adjust_mode + && pat_info.pat_origin.default_binding_modes() + && self.should_peel_smart_pointer(peel_kind, expected) => + { + debug!("scrutinee ty {expected:?} is a smart pointer, inserting pin deref"); + + // The scrutinee is a smart pointer; implicitly dereference it. This adds a + // requirement that `expected: DerefPure`. + let inner_ty = self.deref_pat_target(pat, expected); + // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any + // `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`. + + self.check_deref_pattern( + pat, + opt_path_res, + adjust_mode, + expected, + inner_ty, + PatAdjust::OverloadedDeref, + pat_info, + ) + } + Pat::Missing => self.types.types.error, + Pat::Wild | Pat::Rest => expected, + // We allow any type here; we ensure that the type is uninhabited during match checking. + // Pat::Never => expected, + Pat::Path(_) => { + let ty = match opt_path_res.unwrap() { + Ok(ref pr) => self.infer_pat_path(pat, pr, expected), + Err(()) => self.types.types.error, }; - let post_idx_offset = field_types.iter().count().saturating_sub(post.len()); - - let pre_iter = pre.iter().enumerate(); - 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) { - let expected_ty = { - match variant_data.field(&Name::new_tuple_field(i)) { - Some(local_id) => { - if !visibilities[local_id] - .is_visible_from(self.db, self.resolver.module()) - { - // FIXME(DIAGNOSE): private tuple field - } - let f = field_types[local_id].get(); - let expected_ty = match substs { - Some(substs) => f.instantiate(self.interner(), substs), - None => f.instantiate(self.interner(), &[]), - }; - self.process_remote_user_written_ty(expected_ty) - } - None => self.err_ty(), + self.write_pat_ty(pat, ty); + ty + } + Pat::Lit(expr) => self.infer_lit_pat(expr, expected), + Pat::Range { start: lhs, end: rhs, .. } => { + self.infer_range_pat(pat, lhs, rhs, expected) + } + Pat::Bind { id: var_id, subpat } => { + self.infer_bind_pat(pat, var_id, subpat, expected, pat_info) + } + Pat::TupleStruct { args: ref subpats, ellipsis: ddpos, .. } => match opt_path_res + .unwrap() + { + Ok(ResolvedPat { ty, kind: ResolvedPatKind::TupleStruct { variant } }) => self + .infer_tuple_struct_pat(pat, subpats, ddpos, ty, variant, expected, pat_info), + Err(()) => { + let ty_err = self.types.types.error; + for &subpat in subpats { + self.infer_pat(subpat, ty_err, pat_info); + } + ty_err + } + Ok(pr) => panic!("tuple struct pattern resolved to {pr:?}"), + }, + Pat::Record { args: ref fields, ellipsis: has_rest_pat, .. } => { + match opt_path_res.unwrap() { + Ok(ResolvedPat { ty, kind: ResolvedPatKind::Struct { variant } }) => self + .infer_record_pat( + pat, + fields, + has_rest_pat, + ty, + variant, + expected, + pat_info, + ), + Err(()) => { + let ty_err = self.types.types.error; + for field in fields { + self.infer_pat(field.pat, ty_err, pat_info); } - }; - - self.infer_pat(subpat, expected_ty, default_bm, decl); + ty_err + } + Ok(pr) => panic!("struct pattern resolved to {pr:?}"), } } - None => { - let err_ty = self.err_ty(); - for &inner in subs { - self.infer_pat(inner, err_ty, default_bm, decl); + // Pat::Guard(pat, cond) => { + // self.infer_pat(pat, expected, pat_info); + // self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {}); + // expected + // } + Pat::Or(ref pats) => { + for &pat in pats { + self.infer_pat(pat, expected, pat_info); } + expected + } + Pat::Tuple { args: ref elements, ellipsis: ddpos } => { + self.infer_tuple_pat(pat, elements, ddpos, expected, pat_info) + } + Pat::Box { inner } => self.infer_box_pat(pat, inner, expected, pat_info), + Pat::Deref { inner } => self.infer_deref_pat(pat, inner, expected, pat_info), + // Pat::Deref(inner) => self.infer_deref_pat(pat.span, inner, expected, pat_info), + Pat::Ref { pat: inner, mutability: mutbl } => self.infer_ref_pat( + pat, + inner, + if mutbl.is_mut() { Mutability::Mut } else { Mutability::Not }, + expected, + pat_info, + ), + Pat::Slice { prefix: ref before, slice, suffix: ref after } => { + self.infer_slice_pat(pat, before, slice, after, expected, pat_info) + } + Pat::Expr(expr) => self.infer_destructuring_assignment_expr(expr, expected), + Pat::ConstBlock(expr) => { + self.infer_expr(expr, &Expectation::has_type(expected), ExprIsRead::Yes) } } + } - ty + fn adjust_pat_info(&self, inner_mutability: Mutability, pat_info: PatInfo) -> PatInfo { + let mut binding_mode = match pat_info.binding_mode { + // If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const` + // or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or + // `&pin mut`). + ByRef::No => ByRef::Yes(inner_mutability), + ByRef::Yes(mutability) => { + let mutability = match mutability { + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). + Mutability::Mut => inner_mutability, + // Once a `ref`, always a `ref`. + // This is because a `& &mut` cannot mutate the underlying value. + Mutability::Not => Mutability::Not, + }; + ByRef::Yes(mutability) + } + }; + + let PatInfo { mut max_ref_mutbl, .. } = pat_info; + if self.downgrade_mut_inside_shared() { + binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl()); + } + match binding_mode { + ByRef::Yes(Mutability::Not) => max_ref_mutbl = MutblCap::Not, + _ => {} + } + debug!("default binding mode is now {:?}", binding_mode); + PatInfo { binding_mode, max_ref_mutbl, ..pat_info } } - /// Infers type for record pattern or its corresponding assignee expression. - pub(super) fn infer_record_pat_like( + fn check_deref_pattern( &mut self, - path: Option<&Path>, + pat: PatId, + opt_path_res: Option<Result<ResolvedPat<'db>, ()>>, + adjust_mode: AdjustMode, expected: Ty<'db>, - default_bm: BindingMode, - id: PatId, - subs: impl ExactSizeIterator<Item = (Name, PatId)>, - decl: Option<DeclContext>, + mut inner_ty: Ty<'db>, + pat_adjust_kind: PatAdjust, + pat_info: PatInfo, ) -> Ty<'db> { - let (ty, def) = self.resolve_variant(id.into(), path, false); - if let Some(variant) = def { - self.write_variant_resolution(id.into(), variant); - } - - self.unify(ty, expected); - - match def { - _ if subs.len() == 0 => {} - Some(def) => { - let field_types = self.db.field_types(def); - let variant_data = def.fields(self.db); - let visibilities = VariantFields::field_visibilities(self.db, def); - - let substs = ty.as_adt().map(TupleExt::tail); - - for (name, inner) in subs { - let expected_ty = { - match variant_data.field(&name) { - Some(local_id) => { - if !visibilities[local_id] - .is_visible_from(self.db, self.resolver.module()) - { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - field: inner.into(), - private: Some(local_id), - variant: def, - }); - } - let f = field_types[local_id].get(); - let expected_ty = match substs { - Some(substs) => f.instantiate(self.interner(), substs), - None => f.instantiate(self.interner(), &[]), - }; - self.process_remote_user_written_ty(expected_ty) - } - None => { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - field: inner.into(), - private: None, - variant: def, - }); - self.err_ty() - } - } - }; + debug_assert!( + !matches!(pat_adjust_kind, PatAdjust::BuiltinDeref), + "unexpected deref pattern for builtin reference type {expected:?}", + ); + + let pat_adjustments = self.result.pat_adjustments.entry(pat).or_default(); + // We may reach the recursion limit if a user matches on a type `T` satisfying + // `T: Deref<Target = T>`; error gracefully in this case. + // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move + // this check out of this branch. Alternatively, this loop could be implemented with + // autoderef and this check removed. For now though, don't break code compiling on + // stable with lots of `&`s and a low recursion limit, if anyone's done that. + if pat_adjustments.len() < self.resolver.top_level_def_map().recursion_limit() as usize { + // Preserve the smart pointer type for THIR lowering and closure upvar analysis. + pat_adjustments.push(PatAdjustment { kind: pat_adjust_kind, source: expected.store() }); + } else { + // FIXME: Emit an error. + inner_ty = self.types.types.error; + } - self.infer_pat(inner, expected_ty, default_bm, decl); + // Recurse, using the old pat info to keep `current_depth` to its old value. + // Peeling smart pointers does not update the default binding mode. + self.infer_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, pat_info) + } + + /// How should the binding mode and expected type be adjusted? + /// + /// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`. + fn calc_adjust_mode( + &mut self, + pat_id: PatId, + pat: &Pat, + opt_path_res: Option<Result<ResolvedPat<'db>, ()>>, + ) -> AdjustMode { + match pat { + // Type checking these product-like types successfully always require + // that the expected type be of those types and not reference types. + Pat::Tuple { .. } | Pat::Range { .. } | Pat::Slice { .. } => AdjustMode::peel_all(), + // When checking an explicit deref pattern, only peel reference types. + // FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box + // patterns may want `PeelKind::Implicit`, stopping on encountering a box. + Pat::Box { .. } | Pat::Deref { .. } => { + AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat } + } + // A never pattern behaves somewhat like a literal or unit variant. + // Pat::Never => AdjustMode::peel_all(), + // For patterns with paths, how we peel the scrutinee depends on the path's resolution. + Pat::Record { .. } + | Pat::TupleStruct { .. } + | Pat::Path(_) => { + // If there was an error resolving the path, default to peeling everything. + opt_path_res.unwrap().map_or(AdjustMode::peel_all(), |pr| pr.adjust_mode()) + } + + // String and byte-string literals result in types `&str` and `&[u8]` respectively. + // All other literals result in non-reference types. + // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}` unless + // `deref_patterns` is enabled. + &Pat::Lit(expr) | &Pat::ConstBlock(expr) => { + let lit_ty = self.infer_expr_pat_unadjusted(expr); + // Call `resolve_vars_if_possible` here for inline const blocks. + let lit_ty = self.infcx().resolve_vars_if_possible(lit_ty); + // If `deref_patterns` is enabled, allow `if let "foo" = &&"foo" {}`. + if self.features.deref_patterns { + let mut peeled_ty = lit_ty; + let mut pat_ref_layers = 0; + while let TyKind::Ref(_, inner_ty, mutbl) = + self.table.try_structurally_resolve_type(pat_id.into(), peeled_ty).kind() + { + // We rely on references at the head of constants being immutable. + debug_assert!(mutbl.is_not()); + pat_ref_layers += 1; + peeled_ty = inner_ty; + } + AdjustMode::Peel { + kind: PeelKind::Implicit { until_adt: None, pat_ref_layers }, + } + } else { + if lit_ty.is_ref() { AdjustMode::Pass } else { AdjustMode::peel_all() } } } - None => { - let err_ty = self.err_ty(); - for (_, inner) in subs { - self.infer_pat(inner, err_ty, default_bm, decl); + + // Ref patterns are complicated, we handle them in `check_pat_ref`. + Pat::Ref { .. } + // No need to do anything on a missing pattern. + | Pat::Missing + // A `_`/`..` pattern works with any expected type, so there's no need to do anything. + | Pat::Wild | Pat::Rest + // Bindings also work with whatever the expected type is, + // and moreover if we peel references off, that will give us the wrong binding type. + // Also, we can have a subpattern `binding @ pat`. + // Each side of the `@` should be treated independently (like with OR-patterns). + | Pat::Bind { .. } + // `Pat::Expr(_)` inside assignments becomes a binding in rustc, therefore should be + // the same as `Pat::Bind`. + | Pat::Expr(_) + // An OR-pattern just propagates to each individual alternative. + // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`. + // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`. + | Pat::Or(_) + // Like or-patterns, guard patterns just propagate to their subpatterns. + /* | Pat::Guard(..) */ => AdjustMode::Pass, + } + } + + /// Assuming `expected` is a reference type, determine whether to peel it before matching. + fn should_peel_ref(&self, peel_kind: PeelKind, mut expected: Ty<'db>) -> bool { + debug_assert!(expected.is_ref()); + let pat_ref_layers = match peel_kind { + PeelKind::ExplicitDerefPat => 0, + PeelKind::Implicit { pat_ref_layers, .. } => pat_ref_layers, + }; + + // Most patterns don't have reference types, so we'll want to peel all references from the + // scrutinee before matching. To optimize for the common case, return early. + if pat_ref_layers == 0 { + return true; + } + debug_assert!( + self.features.deref_patterns, + "Peeling for patterns with reference types is gated by `deref_patterns`." + ); + + // If the pattern has as many or more layers of reference as the expected type, we can match + // without peeling more, unless we find a smart pointer or `&mut` that we also need to peel. + // We don't treat `&` and `&mut` as interchangeable, but by peeling `&mut`s before matching, + // we can still, e.g., match on a `&mut str` with a string literal pattern. This is because + // string literal patterns may be used where `str` is expected. + let mut expected_ref_layers = 0; + while let TyKind::Ref(_, inner_ty, mutbl) = expected.kind() { + if mutbl.is_mut() { + // Mutable references can't be in the final value of constants, thus they can't be + // at the head of their types, thus we should always peel `&mut`. + return true; + } + expected_ref_layers += 1; + expected = inner_ty; + } + pat_ref_layers < expected_ref_layers || self.should_peel_smart_pointer(peel_kind, expected) + } + + /// Determine whether `expected` is a smart pointer type that should be peeled before matching. + fn should_peel_smart_pointer(&self, peel_kind: PeelKind, expected: Ty<'db>) -> bool { + // Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case. + if let PeelKind::Implicit { until_adt, .. } = peel_kind + // For simplicity, only apply overloaded derefs if `expected` is a known ADT. + // FIXME(deref_patterns): we'll get better diagnostics for users trying to + // implicitly deref generics if we allow them here, but primitives, tuples, and + // inference vars definitely should be stopped. Figure out what makes most sense. + && let TyKind::Adt(scrutinee_adt, _) = expected.kind() + // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if + // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern. + && until_adt != Some(scrutinee_adt.def_id()) + // At this point, the pattern isn't able to match `expected` without peeling. Check + // that it implements `Deref` before assuming it's a smart pointer, to get a normal + // type error instead of a missing impl error if not. This only checks for `Deref`, + // not `DerefPure`: we require that too, but we want a trait error if it's missing. + && let Some(deref_trait) = self.lang_items.Deref + && self.infcx().type_implements_trait(deref_trait, [expected], self.table.param_env).may_apply() + { + true + } else { + false + } + } + + fn infer_expr_pat_unadjusted(&mut self, expr: ExprId) -> Ty<'db> { + self.infer_expr_no_expect(expr, ExprIsRead::Yes) + } + + fn infer_lit_pat(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { + let literal = match &self.store[expr] { + Expr::Literal(literal) => literal, + _ => panic!("expected a literal"), + }; + + // We've already computed the type above (when checking for a non-ref pat), + // so avoid computing it again. + let ty = self.expr_ty(expr); + + // Byte string patterns behave the same way as array patterns + // They can denote both statically and dynamically-sized byte arrays. + // Additionally, when `deref_patterns` is enabled, byte string literal patterns may have + // types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec<u8>`. + let mut pat_ty = ty; + if matches!(literal, Literal::ByteString(_)) { + let expected = self.structurally_resolve_type(expr.into(), expected); + match expected.kind() { + // Allow `b"...": &[u8]` + TyKind::Ref(_, inner_ty, _) + if self + .table + .try_structurally_resolve_type(expr.into(), inner_ty) + .is_slice() => + { + trace!(?expr, "polymorphic byte string lit"); + pat_ty = self.types.types.static_u8_slice; } + // Allow `b"...": [u8; 3]` for `deref_patterns` + TyKind::Array(..) if self.features.deref_patterns => { + pat_ty = match ty.kind() { + TyKind::Ref(_, inner_ty, _) => inner_ty, + _ => panic!("found byte string literal with non-ref type {ty:?}"), + } + } + // Allow `b"...": [u8]` for `deref_patterns` + TyKind::Slice(..) if self.features.deref_patterns => { + pat_ty = self.types.types.u8_slice; + } + // Otherwise, `b"...": &[u8; 3]` + _ => {} } } - ty + // When `deref_patterns` is enabled, in order to allow `deref!("..."): String`, we allow + // string literal patterns to have type `str`. This is accounted for when lowering to MIR. + if self.features.deref_patterns + && matches!(literal, Literal::String(_)) + && self.table.try_structurally_resolve_type(expr.into(), expected).is_str() + { + pat_ty = self.types.types.str; + } + + // Somewhat surprising: in this case, the subtyping relation goes the + // opposite way as the other cases. Actually what we really want is not + // a subtyping relation at all but rather that there exists a LUB + // (so that they can be compared). However, in practice, constants are + // always scalars or strings. For scalars subtyping is irrelevant, + // and for strings `ty` is type is `&'static str`, so if we say that + // + // &'static str <: expected + // + // then that's equivalent to there existing a LUB. + _ = self.demand_suptype(expr.into(), expected, pat_ty); + + pat_ty } - /// Infers type for tuple pattern or its corresponding assignee expression. - /// - /// Ellipses found in the original pattern or expression must be filtered out. - pub(super) fn infer_tuple_pat_like( + fn infer_range_pat( &mut self, pat: PatId, + lhs_expr: Option<ExprId>, + rhs_expr: Option<ExprId>, expected: Ty<'db>, - default_bm: BindingMode, - ellipsis: Option<u32>, - elements: &[PatId], - decl: Option<DeclContext>, ) -> Ty<'db> { - let mut expected_len = elements.len(); - if ellipsis.is_some() { - // Require known type only when `..` is present. - if let TyKind::Tuple(tys) = self.table.structurally_resolve_type(expected).kind() { - expected_len = tys.len(); + let mut calc_side = |opt_expr: Option<ExprId>| match opt_expr { + None => None, + Some(expr) => { + let ty = self.infer_expr_pat_unadjusted(expr); + // Check that the end-point is possibly of numeric or char type. + // The early check here is not for correctness, but rather better + // diagnostics (e.g. when `&str` is being matched, `expected` will + // be peeled to `str` while ty here is still `&str`, if we don't + // err early here, a rather confusing unification error will be + // emitted instead). + let ty = self.table.try_structurally_resolve_type(expr.into(), ty); + let fail = + !(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error()); + Some((fail, ty, expr)) } + }; + let mut lhs = calc_side(lhs_expr); + let mut rhs = calc_side(rhs_expr); + + if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { + // There exists a side that didn't meet our criteria that the end-point + // be of a numeric or char type, as checked in `calc_side` above. + // FIXME: Emit an error. + return self.types.types.error; } - let max_len = cmp::max(expected_len, elements.len()); - let element_tys_iter = (0..max_len).map(|_| self.table.next_ty_var()); - let element_tys = Tys::new_from_iter(self.interner(), element_tys_iter); - let pat_ty = Ty::new(self.interner(), TyKind::Tuple(element_tys)); - if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() - && let TyKind::Tuple(expected) = expected.kind() - { - // Equate expected type with the infer vars, for better diagnostics. - for (expected, elem_ty) in iter::zip(expected, element_tys) { - _ = self - .table - .at(&ObligationCause::dummy()) - .eq(expected, elem_ty) - .map(|infer_ok| self.table.register_infer_ok(infer_ok)); - } - } - let (before_ellipsis, after_ellipsis) = match ellipsis { - Some(ellipsis) => { - let element_tys = element_tys.as_slice(); - // Don't check patterns twice. - let from_end_start = cmp::max( - element_tys.len().saturating_sub(elements.len() - ellipsis as usize), - ellipsis as usize, - ); - ( - element_tys.get(..ellipsis as usize).unwrap_or(element_tys), - element_tys.get(from_end_start..).unwrap_or_default(), - ) + // Unify each side with `expected`. + // Subtyping doesn't matter here, as the value is some kind of scalar. + let mut demand_eqtype = |x: &mut _| { + if let Some((_, x_ty, x_expr)) = *x { + _ = self.demand_eqtype(ExprOrPatId::from(x_expr), expected, x_ty); + } + }; + demand_eqtype(&mut lhs); + demand_eqtype(&mut rhs); + + if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { + return self.types.types.error; + } + + // Find the unified type and check if it's of numeric or char type again. + // This check is needed if both sides are inference variables. + // We require types to be resolved here so that we emit inference failure + // rather than "_ is not a char or numeric". + let ty = self.structurally_resolve_type( + lhs_expr.or(rhs_expr).map(ExprOrPatId::ExprId).unwrap_or(pat.into()), + expected, + ); + if !(ty.is_numeric() || ty.is_char() || ty.references_error()) { + // FIXME: Emit an error. + return self.types.types.error; + } + ty + } + + fn infer_bind_pat( + &mut self, + pat: PatId, + var_id: BindingId, + sub: Option<PatId>, + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + let PatInfo { binding_mode: def_br, .. } = pat_info; + let binding_data = &self.store[var_id]; + + // Determine the binding mode... + let user_bind_annot = BindingMode::from_annotation(binding_data.mode); + let bm = match user_bind_annot { + BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_) = def_br => { + // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and + // using other experimental matching features compatible with it. + if self.edition.at_least_2024() + && (self.features.ref_pat_eat_one_layer_2024 + || self.features.ref_pat_eat_one_layer_2024_structural) + { + if !self.features.mut_ref { + // FIXME: Emit an error: binding cannot be both mutable and by-reference. + } + + BindingMode(def_br, Mutability::Mut) + } else { + // `mut` resets the binding mode on edition <= 2021 + BindingMode(ByRef::No, Mutability::Mut) + } } - None => (element_tys.as_slice(), &[][..]), + BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), + BindingMode(ByRef::Yes(_), _) => user_bind_annot, }; - for (&elem, &elem_ty) in iter::zip(elements, before_ellipsis.iter().chain(after_ellipsis)) { - self.infer_pat(elem, elem_ty, default_bm, decl); + + if matches!(bm.0, ByRef::Yes(Mutability::Mut)) + && let MutblCap::WeaklyNot = pat_info.max_ref_mutbl + { + // FIXME: Emit an error: cannot borrow as mutable inside an `&` pattern. } - if let Some(uncovered) = elements.get(element_tys.len()..) { - for &elem in uncovered { - self.infer_pat(elem, self.types.types.error, default_bm, decl); + + // ...and store it in a side table: + self.result.binding_modes.insert(pat, bm); + + debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat, bm); + + let local_ty = match bm.0 { + ByRef::Yes(mutbl) => { + // If the binding is like `ref x | ref mut x`, + // then `x` is assigned a value of type `&M T` where M is the + // mutability and T is the expected type. + // + // Under pin ergonomics, if the binding is like `ref pin const|mut x`, + // then `x` is assigned a value of type `&pin M T` where M is the + // mutability and T is the expected type. + // + // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` + // is required. However, we use equality, which is stronger. + // See (note_1) for an explanation. + self.new_ref_ty(pat.into(), mutbl, expected) } + // Otherwise, the type of x is the expected type `T`. + ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). + }; + + // We have a concrete type for the local, so we do not need to taint it and hide follow up errors *using* the local. + if let Some(existing_local_ty) = self.result.type_of_binding.get(var_id) { + // If there are multiple arms, make sure they all agree on + // what the type of the binding `x` ought to be. + _ = self.demand_eqtype(pat.into(), existing_local_ty.as_ref(), local_ty); + } else { + self.write_binding_ty(var_id, local_ty); + } + + if let Some(p) = sub { + self.infer_pat(p, expected, pat_info); + } + + local_ty + } + + fn check_dereferenceable(&self, expected: Ty<'db>, inner: PatId) -> Result<(), ()> { + if let Pat::Bind { .. } = self.store[inner] + && let Some(pointee_ty) = self.shallow_resolve(expected).builtin_deref(true) + && let TyKind::Dynamic(..) = pointee_ty.kind() + { + // This is "x = dyn SomeTrait" being reduced from + // "let &x = &dyn SomeTrait" or "let box x = Box<dyn SomeTrait>", an error. + // FIXME: Emit an error. rustc emits this message: + const _CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ +This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ +pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ +this type has no compile-time size. Therefore, all accesses to trait types must be through \ +pointers. If you encounter this error you should try to avoid dereferencing the pointer. + +You can read more about trait objects in the Trait Objects section of the Reference: \ +https://doc.rust-lang.org/reference/types.html#trait-objects"; } + Ok(()) + } + + fn resolve_record_pat(&mut self, pat: PatId, path: &Path) -> Result<ResolvedPat<'db>, ()> { + // Resolve the path and check the definition for errors. + let (pat_ty, Some(variant)) = self.resolve_variant(pat.into(), path, false) else { + return Err(()); + }; + self.write_variant_resolution(pat.into(), variant); + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Struct { variant } }) + } + + fn infer_record_pat( + &mut self, + pat: PatId, + fields: &[RecordFieldPat], + has_rest_pat: bool, + pat_ty: Ty<'db>, + variant: VariantId, + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + // Type-check the path. + let _ = self.demand_eqtype(pat.into(), expected, pat_ty); + + // Type-check subpatterns. + self.check_record_pat_fields(pat_ty, pat, variant, fields, has_rest_pat, pat_info); pat_ty } - /// The resolver needs to be updated to the surrounding expression when inside assignment - /// (because there, `Pat::Path` can refer to a variable). - pub(super) fn infer_top_pat( + fn resolve_pat_path(&mut self, pat: PatId, path: &Path) -> Result<ResolvedPat<'db>, ()> { + let (res, pat_ty) = self.infer_path(path, pat.into()).ok_or(())?; + match res { + ValueNs::FunctionId(_) + | ValueNs::GenericParam(_) + | ValueNs::ImplSelf(_) + | ValueNs::LocalBinding(_) + | ValueNs::StaticId(_) => { + // FIXME: Emit an error. + return Err(()); + } + ValueNs::ConstId(_) | ValueNs::EnumVariantId(_) | ValueNs::StructId(_) => {} // OK + } + + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res } }) + } + + fn infer_pat_path( &mut self, pat: PatId, + resolved: &ResolvedPat<'db>, expected: Ty<'db>, - decl: Option<DeclContext>, - ) { - self.infer_pat(pat, expected, BindingMode::default(), decl); + ) -> Ty<'db> { + _ = self.demand_suptype(pat.into(), expected, resolved.ty); + resolved.ty } - fn infer_pat( + fn resolve_tuple_struct_pat( &mut self, pat: PatId, + path: &Path, + ) -> Result<ResolvedPat<'db>, ()> { + // Resolve the path and check the definition for errors. + let (pat_ty, Some(variant)) = self.resolve_variant(pat.into(), path, true) else { + return Err(()); + }; + self.write_variant_resolution(pat.into(), variant); + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::TupleStruct { variant } }) + } + + fn infer_tuple_struct_pat( + &mut self, + pat: PatId, + subpats: &[PatId], + ddpos: Option<u32>, + pat_ty: Ty<'db>, + variant: VariantId, expected: Ty<'db>, - mut default_bm: BindingMode, - decl: Option<DeclContext>, + pat_info: PatInfo, ) -> Ty<'db> { - let mut expected = self.table.structurally_resolve_type(expected); - - if matches!(&self.store[pat], Pat::Ref { .. }) || self.inside_assignment { - cov_mark::hit!(match_ergonomics_ref); - // When you encounter a `&pat` pattern, reset to Move. - // This is so that `w` is by value: `let (_, &w) = &(1, &2);` - // Destructuring assignments also reset the binding mode and - // don't do match ergonomics. - default_bm = BindingMode::Move; - } else if self.is_non_ref_pat(self.store, pat) { - let mut pat_adjustments = Vec::new(); - while let TyKind::Ref(_lifetime, inner, mutability) = expected.kind() { - pat_adjustments.push(expected.store()); - expected = self.table.try_structurally_resolve_type(inner); - default_bm = match default_bm { - BindingMode::Move => BindingMode::Ref(mutability), - BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not), - BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability), + let interner = self.interner(); + + // Type-check the tuple struct pattern against the expected type. + let had_err = self.demand_eqtype(pat.into(), expected, pat_ty); + + let variant_fields = variant.fields(self.db); + let variant_field_tys = self.db.field_types(variant); + let TyKind::Adt(_, args) = pat_ty.kind() else { + panic!("unexpected pattern type {:?}", pat_ty); + }; + // Type-check subpatterns. + if subpats.len() == variant_fields.len() + || subpats.len() < variant_fields.len() && ddpos.is_some() + { + for (i, &subpat) in subpats.iter().enumerate_and_adjust(variant_fields.len(), ddpos) { + let field_id = LocalFieldId::from_raw(la_arena::RawIdx::from_u32(i as u32)); + let field_ty = + variant_field_tys[field_id].get().instantiate(interner, args).skip_norm_wip(); + self.infer_pat(subpat, field_ty, pat_info); + } + if let Err(()) = had_err { + for &pat in subpats { + self.infer_pat(pat, self.types.types.error, pat_info); } + return self.types.types.error; } + } else { + self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount { + pat, + expected: variant_fields.len(), + found: subpats.len(), + }); - if !pat_adjustments.is_empty() { - pat_adjustments.shrink_to_fit(); - self.result.pat_adjustments.insert(pat, pat_adjustments); + for (i, &pat) in subpats.iter().enumerate() { + let field_id = LocalFieldId::from_raw(la_arena::RawIdx::from_u32(i as u32)); + let expected = match variant_field_tys.get(field_id) { + Some(field_ty) => field_ty.get().instantiate(interner, args).skip_norm_wip(), + None => self.types.types.error, + }; + self.infer_pat(pat, expected, pat_info); } } + pat_ty + } - // Lose mutability. - let default_bm = default_bm; - let expected = expected; - - let ty = match &self.store[pat] { - Pat::Tuple { args, ellipsis } => { - self.infer_tuple_pat_like(pat, expected, default_bm, *ellipsis, args, decl) + fn infer_tuple_pat( + &mut self, + pat: PatId, + elements: &[PatId], + ddpos: Option<u32>, + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + let interner = self.interner(); + let mut expected_len = elements.len(); + if ddpos.is_some() { + // Require known type only when `..` is present. + if let TyKind::Tuple(tys) = self.structurally_resolve_type(pat.into(), expected).kind() + { + expected_len = tys.len(); } - Pat::Or(pats) => { - for pat in pats.iter() { - self.infer_pat(*pat, expected, default_bm, decl); + } + let max_len = cmp::max(expected_len, elements.len()); + + let element_tys_iter = (0..max_len).map(|i| { + self.table.next_ty_var(elements.get(i).copied().map(Span::PatId).unwrap_or(Span::Dummy)) + }); + let element_tys = Tys::new_from_iter(interner, element_tys_iter); + let pat_ty = Ty::new(interner, TyKind::Tuple(element_tys)); + if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() { + let expected = if let TyKind::Tuple(tys) = + self.table.try_structurally_resolve_type(Span::Dummy, expected).kind() + { + for (expected_var, found) in iter::zip(element_tys, tys) { + // Constrain the infer var so that the type mismatch error message, which contains it, + // will be better. + _ = self.demand_eqtype(pat.into(), expected_var, found); } - expected - } - &Pat::Ref { pat, mutability } => { - self.infer_ref_pat(pat, lower_mutability(mutability), expected, default_bm, decl) + tys + } else { + self.types.empty.tys + }; + let expected = expected.iter().chain(iter::repeat(self.types.types.error)); + Ty::new_tup_from_iter( + interner, + iter::zip(expected, elements).map(|(expected, &elem)| { + self.infer_pat(elem, expected, pat_info); + self.result.type_of_pat_with_adjust(elem) + }), + ) + } else { + for (i, &elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { + self.infer_pat(elem, element_tys[i], pat_info); } - Pat::TupleStruct { path: p, args: subpats, ellipsis } => self - .infer_tuple_struct_pat_like( - p.as_deref(), - expected, - default_bm, - pat, - *ellipsis, - subpats, - decl, - ), - Pat::Record { path: p, args: fields, ellipsis: _ } => { - let subs = fields.iter().map(|f| (f.name.clone(), f.pat)); - self.infer_record_pat_like(p.as_deref(), expected, default_bm, pat, subs, decl) - } - Pat::Path(path) => { - let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty()); - let ty_inserted_vars = self.insert_type_vars_shallow(ty); - match self.coerce( - pat.into(), - expected, - ty_inserted_vars, - AllowTwoPhase::No, - ExprIsRead::No, - ) { - Ok(coerced_ty) => { - self.write_pat_ty(pat, coerced_ty); - return self.pat_ty_after_adjustment(pat); - } - Err(_) => { - self.result.type_mismatches.get_or_insert_default().insert( - pat.into(), - TypeMismatch { - expected: expected.store(), - actual: ty_inserted_vars.store(), - }, - ); - self.write_pat_ty(pat, ty); - // We return `expected` to prevent cascading errors. I guess an alternative is to - // not emit type mismatches for error types and emit an error type here. - return expected; - } + pat_ty + } + } + + fn check_record_pat_fields( + &mut self, + adt_ty: Ty<'db>, + _pat: PatId, + variant: VariantId, + fields: &[RecordFieldPat], + has_rest_pat: bool, + pat_info: PatInfo, + ) { + let interner = self.interner(); + + let TyKind::Adt(_, args) = adt_ty.kind() else { + panic!("struct pattern is not an ADT"); + }; + + // Index the struct fields' types. + let variant_fields = variant.fields(self.db); + let field_map = variant_fields + .fields() + .iter() + .map(|(i, field)| (field.name.clone(), i)) + .collect::<FxHashMap<_, _>>(); + let variant_field_tys = self.db.field_types(variant); + let variant_fields_vis = VariantFields::field_visibilities(self.db, variant); + + // Keep track of which fields have already appeared in the pattern. + let mut used_fields = FxHashMap::default(); + + let mut inexistent_fields = vec![]; + // Typecheck each field. + for (field_idx, field) in fields.iter().enumerate() { + match used_fields.entry(field.name.clone()) { + Occupied(_occupied) => { + self.push_diagnostic(InferenceDiagnostic::DuplicateField { + field: field.pat.into(), + variant, + }); } - } - Pat::Bind { id, subpat } => { - return self.infer_bind_pat(pat, *id, default_bm, *subpat, expected, decl); - } - Pat::Slice { prefix, slice, suffix } => { - self.infer_slice_pat(expected, prefix, *slice, suffix, default_bm, decl) - } - Pat::Wild => expected, - Pat::Range { start, end, range_type: _ } => { - if let Some(start) = *start { - let start_ty = self.infer_expr(start, &Expectation::None, ExprIsRead::Yes); - _ = self.demand_eqtype(start.into(), expected, start_ty); + Vacant(vacant) => { + vacant.insert(field_idx); } - if let Some(end) = *end { - let end_ty = self.infer_expr(end, &Expectation::None, ExprIsRead::Yes); - _ = self.demand_eqtype(end.into(), expected, end_ty); + }; + let field_idx = field_map.get(&field.name).copied(); + let field_ty = match field_idx { + Some(field_idx) => { + if !self.resolver.is_visible(self.db, variant_fields_vis[field_idx]) { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: field.pat.into(), + private: Some(field_idx), + variant, + }); + } + + variant_field_tys[field_idx].get().instantiate(interner, args).skip_norm_wip() } - expected - } - &Pat::Lit(expr) => { - // Don't emit type mismatches again, the expression lowering already did that. - let ty = self.infer_lit_pat(expr, expected); - self.write_pat_ty(pat, ty); - return self.pat_ty_after_adjustment(pat); - } - Pat::Box { inner } => match self.resolve_boxed_box() { - Some(box_adt) => { - let (inner_ty, alloc_ty) = match expected.as_adt() { - Some((adt, subst)) if adt == box_adt => { - (subst.type_at(0), subst.as_slice().get(1).and_then(|a| a.as_type())) - } - _ => (self.types.types.error, None), - }; - - let inner_ty = self.infer_pat(*inner, inner_ty, default_bm, decl); - Ty::new_adt( - self.interner(), - box_adt, - GenericArgs::fill_with_defaults( - self.interner(), - box_adt.into(), - iter::once(inner_ty.into()).chain(alloc_ty.map(Into::into)), - |_, id, _| self.table.next_var_for_param(id), - ), - ) + None => { + inexistent_fields.push(field); + self.types.types.error } - None => self.err_ty(), - }, - Pat::ConstBlock(expr) => { - let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false); - let result = - self.infer_expr(*expr, &Expectation::has_type(expected), ExprIsRead::Yes); - self.inside_assignment = old_inside_assign; - result - } - Pat::Expr(expr) => { - let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false); - // LHS of assignment doesn't constitute reads. - let expr_is_read = ExprIsRead::No; - let result = - self.infer_expr_coerce(*expr, &Expectation::has_type(expected), expr_is_read); - // We are returning early to avoid the unifiability check below. - let lhs_ty = self.insert_type_vars_shallow(result); - let ty = match self.coerce( - (*expr).into(), - expected, - lhs_ty, - AllowTwoPhase::No, - expr_is_read, - ) { - Ok(ty) => ty, - Err(_) => { - self.result.type_mismatches.get_or_insert_default().insert( - pat.into(), - TypeMismatch { expected: expected.store(), actual: lhs_ty.store() }, - ); - // `rhs_ty` is returned so no further type mismatches are - // reported because of this mismatch. - expected - } - }; - self.write_pat_ty(pat, ty); - self.inside_assignment = old_inside_assign; - return ty; - } - Pat::Missing => self.err_ty(), - }; - // use a new type variable if we got error type here - let ty = self.insert_type_vars_shallow(ty); - // FIXME: This never check is odd, but required with out we do inference right now - if !expected.is_never() && !self.unify(ty, expected) { - self.result.type_mismatches.get_or_insert_default().insert( - pat.into(), - TypeMismatch { expected: expected.store(), actual: ty.store() }, - ); + }; + + self.infer_pat(field.pat, field_ty, pat_info); + } + + let unmentioned_fields = variant_fields + .fields() + .iter() + .filter(|(_, field)| !used_fields.contains_key(&field.name)) + .collect::<Vec<_>>(); + + for inexistent_field in inexistent_fields { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: inexistent_field.pat.into(), + private: None, + variant, + }); } - self.write_pat_ty(pat, ty); - self.pat_ty_after_adjustment(pat) - } - fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty<'db> { - self.result - .pat_adjustments - .get(&pat) - .and_then(|it| it.last()) - .unwrap_or_else(|| &self.result.type_of_pat[pat]) - .as_ref() + // Require `..` if struct has non_exhaustive attribute. + let non_exhaustive = self.has_applicable_non_exhaustive(variant.into()); + if non_exhaustive && !has_rest_pat { + // FIXME: Emit an error. + } + + // Report an error if an incorrect number of fields was specified. + if matches!(variant, VariantId::UnionId(_)) { + if fields.len() != 1 { + // FIXME: Emit an error, unions can't have more than one field. + } + if has_rest_pat { + // FIXME: Emit an error, unions can't have a rest pat. + } + } else if !unmentioned_fields.is_empty() && !has_rest_pat { + // FIXME: Emit an error. + } } - fn infer_ref_pat( + fn infer_box_pat( &mut self, - inner_pat: PatId, - mutability: Mutability, + pat: PatId, + inner: PatId, expected: Ty<'db>, - default_bm: BindingMode, - decl: Option<DeclContext>, + pat_info: PatInfo, ) -> Ty<'db> { - let (expectation_type, expectation_lt) = match expected.kind() { - TyKind::Ref(lifetime, inner_ty, _exp_mut) => (inner_ty, lifetime), - _ => { - let inner_ty = self.table.next_ty_var(); - let inner_lt = self.table.next_region_var(); - let ref_ty = Ty::new_ref(self.interner(), inner_lt, inner_ty, mutability); - // Unification failure will be reported by the caller. - self.unify(ref_ty, expected); - (inner_ty, inner_lt) - } - }; - let subty = self.infer_pat(inner_pat, expectation_type, default_bm, decl); - Ty::new_ref(self.interner(), expectation_lt, subty, mutability) + let interner = self.interner(); + let (box_ty, inner_ty) = self + .check_dereferenceable(expected, inner) + .map(|()| { + // Here, `demand::subtype` is good enough, but I don't + // think any errors can be introduced by using `demand::eqtype`. + let inner_ty = self.table.next_ty_var(inner.into()); + let box_ty = Ty::new_box(interner, inner_ty); + _ = self.demand_eqtype(pat.into(), expected, box_ty); + (box_ty, inner_ty) + }) + .unwrap_or_else(|()| { + let err = self.types.types.error; + (err, err) + }); + self.infer_pat(inner, inner_ty, pat_info); + box_ty } - fn infer_bind_pat( + fn infer_deref_pat( &mut self, pat: PatId, - binding: BindingId, - default_bm: BindingMode, - subpat: Option<PatId>, + inner: PatId, expected: Ty<'db>, - decl: Option<DeclContext>, + pat_info: PatInfo, ) -> Ty<'db> { - let Binding { mode, .. } = self.store[binding]; - let mode = if mode == BindingAnnotation::Unannotated { - default_bm - } else { - BindingMode::convert(mode) - }; - self.result.binding_modes.insert(pat, mode); + let target_ty = self.deref_pat_target(pat, expected); + self.infer_pat(inner, target_ty, pat_info); + let infer_ok = self.register_deref_mut_bounds_if_needed(pat, inner, [expected]); + self.table.register_infer_ok(infer_ok); + expected + } - let inner_ty = match subpat { - Some(subpat) => self.infer_pat(subpat, expected, default_bm, decl), - None => expected, + fn deref_pat_target(&mut self, pat: PatId, source_ty: Ty<'db>) -> Ty<'db> { + let (Some(deref_pure), Some(deref_target)) = + (self.lang_items.DerefPure, self.lang_items.DerefTarget) + else { + return self.types.types.error; }; - let inner_ty = self.insert_type_vars_shallow(inner_ty); + // Register a `DerefPure` bound, which is required by all `deref!()` pats. + let interner = self.interner(); + self.table.register_bound(source_ty, deref_pure, ObligationCause::new(pat)); + // The expected type for the deref pat's inner pattern is `<expected as Deref>::Target`. + let target_ty = Ty::new_projection(interner, deref_target.into(), [source_ty]); + self.table.try_structurally_resolve_type(pat.into(), target_ty) + } - let bound_ty = match mode { - BindingMode::Ref(mutability) => { - let inner_lt = self.table.next_region_var(); - Ty::new_ref(self.interner(), inner_lt, expected, mutability) + /// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut` + /// bindings, which would require `DerefMut` to be emitted in MIR building instead of just + /// `Deref`. We do this *after* checking the inner pattern, since we want to make sure to + /// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs. + fn register_deref_mut_bounds_if_needed( + &self, + pat: PatId, + inner: PatId, + derefed_tys: impl IntoIterator<Item = Ty<'db>>, + ) -> InferOk<'db, ()> { + let mut infer_ok = InferOk { value: (), obligations: Vec::new() }; + if self.pat_has_ref_mut_binding(inner) { + let Some(deref_mut) = self.lang_items.DerefMut else { return infer_ok }; + let interner = self.interner(); + for mutably_derefed_ty in derefed_tys { + infer_ok.obligations.push(Obligation::new( + interner, + ObligationCause::new(pat), + self.table.param_env, + TraitRef::new(interner, deref_mut.into(), [mutably_derefed_ty]), + )); } - BindingMode::Move => expected, - }; - self.write_pat_ty(pat, inner_ty); - self.write_binding_ty(binding, bound_ty); - inner_ty + } + infer_ok } - fn infer_slice_pat( + /// Does the pattern recursively contain a `ref mut` binding in it? + /// + /// This is used to determined whether a `deref` pattern should emit a `Deref` + /// or `DerefMut` call for its pattern scrutinee. + /// + /// This is computed from the typeck results since we want to make + /// sure to apply any match-ergonomics adjustments, which we cannot + /// determine from the HIR alone. + pub(super) fn pat_has_ref_mut_binding(&self, pat: PatId) -> bool { + let mut has_ref_mut = false; + self.store.walk_pats(pat, &mut |pat| { + if let Some(BindingMode(ByRef::Yes(Mutability::Mut), _)) = + self.result.binding_modes.get(pat) + { + has_ref_mut = true; + } + }); + has_ref_mut + } + + // Precondition: Pat is Ref(inner) + fn infer_ref_pat( &mut self, - expected: Ty<'db>, - prefix: &[PatId], - slice: Option<PatId>, - suffix: &[PatId], - default_bm: BindingMode, - decl: Option<DeclContext>, + pat: PatId, + inner: PatId, + pat_mutbl: Mutability, + mut expected: Ty<'db>, + mut pat_info: PatInfo, ) -> Ty<'db> { - let expected = self.table.structurally_resolve_type(expected); - - // If `expected` is an infer ty, we try to equate it to an array if the given pattern - // allows it. See issue #16609 - if self.pat_is_irrefutable(decl) - && expected.is_ty_var() - && let Some(resolved_array_ty) = - self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice) - { - self.unify(expected, resolved_array_ty); + let ref_pat_matches_mut_ref = self.ref_pat_matches_mut_ref(); + if ref_pat_matches_mut_ref && pat_mutbl == Mutability::Not { + // If `&` patterns can match against mutable reference types (RFC 3627, Rule 5), we need + // to prevent subpatterns from binding with `ref mut`. Subpatterns of a shared reference + // pattern should have read-only access to the scrutinee, and the borrow checker won't + // catch it in this case. + pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(); } - let expected = self.table.try_structurally_resolve_type(expected); - let elem_ty = match expected.kind() { - TyKind::Array(st, _) | TyKind::Slice(st) => st, - _ => self.err_ty(), - }; + expected = self.table.try_structurally_resolve_type(pat.into(), expected); + // Determine whether we're consuming an inherited reference and resetting the default + // binding mode, based on edition and enabled experimental features. + if let ByRef::Yes(inh_mut) = pat_info.binding_mode { + match self.ref_pat_matches_inherited_ref(self.edition) { + InheritedRefMatchRule::EatOuter => { + // ref pattern attempts to consume inherited reference + if pat_mutbl > inh_mut { + // Tried to match inherited `ref` with `&mut` + // NB: This assumes that `&` patterns can match against mutable references + // (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E + // but not Rule 5, we'll need to check that here. + debug_assert!(ref_pat_matches_mut_ref); + // FIXME: Emit an error. + } - for &pat_id in prefix.iter().chain(suffix.iter()) { - self.infer_pat(pat_id, elem_ty, default_bm, decl); - } - - if let Some(slice_pat_id) = slice { - let rest_pat_ty = match expected.kind() { - TyKind::Array(_, length) => { - let len = try_const_usize(self.db, length); - let len = - len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128)); - Ty::new_array_with_const_len( - self.interner(), - elem_ty, - usize_const(self.db, len, self.resolver.krate()), - ) + pat_info.binding_mode = ByRef::No; + self.result.skipped_ref_pats.insert(pat); + self.infer_pat(inner, expected, pat_info); + return expected; } - _ => Ty::new_slice(self.interner(), elem_ty), - }; - self.infer_pat(slice_pat_id, rest_pat_ty, default_bm, decl); - } + InheritedRefMatchRule::EatInner => { + if let TyKind::Ref(_, _, r_mutbl) = expected.kind() + && pat_mutbl <= r_mutbl + { + // Match against the reference type; don't consume the inherited ref. + // NB: The check for compatible pattern and ref type mutability assumes that + // `&` patterns can match against mutable references (RFC 3627, Rule 5). If + // we implement a pattern typing ruleset with Rule 4 (including the fallback + // to matching the inherited ref when the inner ref can't match) but not + // Rule 5, we'll need to check that here. + debug_assert!(ref_pat_matches_mut_ref); + // NB: For RFC 3627's Rule 3, we limit the default binding mode's ref + // mutability to `pat_info.max_ref_mutbl`. If we implement a pattern typing + // ruleset with Rule 4 but not Rule 3, we'll need to check that here. + debug_assert!(self.downgrade_mut_inside_shared()); + let mutbl_cap = cmp::min(r_mutbl, pat_info.max_ref_mutbl.as_mutbl()); + pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(mutbl_cap); + } else { + // The reference pattern can't match against the expected type, so try + // matching against the inherited ref instead. + if pat_mutbl > inh_mut { + // We can't match an inherited shared reference with `&mut`. + // NB: This assumes that `&` patterns can match against mutable + // references (RFC 3627, Rule 5). If we implement a pattern typing + // ruleset with Rule 4 but not Rule 5, we'll need to check that here. + // FIXME(ref_pat_eat_one_layer_2024_structural): If we already tried + // matching the real reference, the error message should explain that + // falling back to the inherited reference didn't work. This should be + // the same error as the old-Edition version below. + debug_assert!(ref_pat_matches_mut_ref); + // FIXME: Emit an error. + } + + pat_info.binding_mode = ByRef::No; + self.result.skipped_ref_pats.insert(pat); + self.infer_pat(inner, expected, pat_info); + return expected; + } + } + InheritedRefMatchRule::EatBoth { consider_inherited_ref: true } => { + // Reset binding mode on old editions + pat_info.binding_mode = ByRef::No; + + if let TyKind::Ref(_, inner_ty, _) = expected.kind() { + // Consume both the inherited and inner references. + if pat_mutbl.is_mut() && inh_mut.is_mut() { + // As a special case, a `&mut` reference pattern will be able to match + // against a reference type of any mutability if the inherited ref is + // mutable. Since this allows us to match against a shared reference + // type, we refer to this as "falling back" to matching the inherited + // reference, though we consume the real reference as well. We handle + // this here to avoid adding this case to the common logic below. + self.infer_pat(inner, inner_ty, pat_info); + return expected; + } else { + // Otherwise, use the common logic below for matching the inner + // reference type. + // FIXME(ref_pat_eat_one_layer_2024_structural): If this results in a + // mutability mismatch, the error message should explain that falling + // back to the inherited reference didn't work. This should be the same + // error as the Edition 2024 version above. + } + } else { + // The expected type isn't a reference type, so only match against the + // inherited reference. + if pat_mutbl > inh_mut { + // We can't match a lone inherited shared reference with `&mut`. + // FIXME: Emit an error. + } - match expected.kind() { - TyKind::Array(_, const_) => { - Ty::new_array_with_const_len(self.interner(), elem_ty, const_) + self.result.skipped_ref_pats.insert(pat); + self.infer_pat(inner, expected, pat_info); + return expected; + } + } + InheritedRefMatchRule::EatBoth { consider_inherited_ref: false } => { + // Reset binding mode on stable Rust. This will be a type error below if + // `expected` is not a reference type. + pat_info.binding_mode = ByRef::No; + } } - _ => Ty::new_slice(self.interner(), elem_ty), } - } - fn infer_lit_pat(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { - // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`. - if let Expr::Literal(Literal::ByteString(_)) = self.store[expr] - && let TyKind::Ref(_, inner, _) = expected.kind() - { - let inner = self.table.try_structurally_resolve_type(inner); - if matches!(inner.kind(), TyKind::Slice(_)) { - let elem_ty = self.types.types.u8; - let slice_ty = Ty::new_slice(self.interner(), elem_ty); - let ty = Ty::new_ref( - self.interner(), - self.types.regions.statik, - slice_ty, - Mutability::Not, - ); - self.write_expr_ty(expr, ty); - return ty; + let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, inner) { + Ok(()) => { + // `demand::subtype` would be good enough, but using `eqtype` turns + // out to be equally general. See (note_1) for details. + + // Take region, inner-type from expected type if we can, + // to avoid creating needless variables. This also helps with + // the bad interactions of the given hack detailed in (note_1). + debug!("check_pat_ref: expected={:?}", expected); + match expected.as_reference() { + Some((r_ty, _, r_mutbl)) + if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl) + || r_mutbl == pat_mutbl) => + { + if r_mutbl == Mutability::Not { + pat_info.max_ref_mutbl = MutblCap::Not; + } + + (expected, r_ty) + } + _ => { + let inner_ty = self.table.next_ty_var(inner.into()); + let ref_ty = self.new_ref_ty(inner.into(), pat_mutbl, inner_ty); + debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); + _ = self.demand_eqtype(pat.into(), expected, ref_ty); + + (ref_ty, inner_ty) + } + } } - } + Err(()) => { + let err = self.types.types.error; + (err, err) + } + }; - self.infer_expr(expr, &Expectation::has_type(expected), ExprIsRead::Yes) + self.infer_pat(inner, inner_ty, pat_info); + ref_ty } - fn is_non_ref_pat(&mut self, store: &hir_def::expr_store::ExpressionStore, pat: PatId) -> bool { - match &store[pat] { - Pat::Tuple { .. } - | Pat::TupleStruct { .. } - | Pat::Record { .. } - | Pat::Range { .. } - | Pat::Slice { .. } => true, - Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(store, *p)), - Pat::Path(path) => { - // A const is a reference pattern, but other value ns things aren't (see #16131). - let resolved = self.resolve_value_path_inner(path, pat.into(), true); - resolved.is_some_and(|it| !matches!(it.0, hir_def::resolver::ValueNs::ConstId(_))) - } - Pat::ConstBlock(..) => false, - Pat::Lit(expr) => !matches!( - store[*expr], - Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) - ), - Pat::Wild - | Pat::Bind { .. } - | Pat::Ref { .. } - | Pat::Box { .. } - | Pat::Missing - | Pat::Expr(_) => false, - } + /// Create a reference or pinned reference type with a fresh region variable. + fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'db>) -> Ty<'db> { + let region = self.table.next_region_var(span); + Ty::new_ref(self.interner(), region, ty, mutbl) } fn try_resolve_slice_ty_to_array_ty( - &mut self, + &self, before: &[PatId], - suffix: &[PatId], slice: Option<PatId>, + pat: PatId, ) -> Option<Ty<'db>> { if slice.is_some() { return None; } - let len = before.len() + suffix.len(); - let size = consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db)); + let interner = self.interner(); + let len = before.len(); + let inner_ty = self.table.next_ty_var(pat.into()); - let elem_ty = self.table.next_ty_var(); - let array_ty = Ty::new_array_with_const_len(self.interner(), elem_ty, size); - Some(array_ty) + Some(Ty::new_array(interner, inner_ty, len.try_into().unwrap())) } - /// Used to determine whether we can infer the expected type in the slice pattern to be of type array. + /// Used to determines whether we can infer the expected type in the slice pattern to be of type array. /// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable /// patterns we wouldn't e.g. report ambiguity in the following situation: /// /// ```ignore(rust) - /// struct Zeroes; + /// struct Zeroes; /// const ARR: [usize; 2] = [0; 2]; /// const ARR2: [usize; 2] = [2; 2]; /// @@ -666,15 +1564,160 @@ impl<'db> InferenceContext<'_, 'db> { /// /// If we're in an irrefutable pattern we prefer the array impl candidate given that /// the slice impl candidate would be rejected anyway (if no ambiguity existed). - fn pat_is_irrefutable(&self, decl_ctxt: Option<DeclContext>) -> bool { - matches!(decl_ctxt, Some(DeclContext { origin: DeclOrigin::LocalDecl { has_else: false } })) + fn pat_is_irrefutable(&self, pat_origin: PatOrigin) -> bool { + match pat_origin { + PatOrigin::LetExpr | PatOrigin::MatchArm => false, + PatOrigin::LetStmt { has_else } => !has_else, + PatOrigin::DestructuringAssignment | PatOrigin::Param => true, + } } -} -pub(super) fn contains_explicit_ref_binding(store: &ExpressionStore, pat_id: PatId) -> bool { - let mut res = false; - store.walk_pats(pat_id, &mut |pat| { - res |= matches!(store[pat], Pat::Bind { id, .. } if matches!(store[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)); - }); - res + /// Type check a slice pattern. + /// + /// Syntactically, these look like `[pat_0, ..., pat_n]`. + /// Semantically, we are type checking a pattern with structure: + /// ```ignore (not-rust) + /// [before_0, ..., before_n, (slice, after_0, ... after_n)?] + /// ``` + /// The type of `slice`, if it is present, depends on the `expected` type. + /// If `slice` is missing, then so is `after_i`. + /// If `slice` is present, it can still represent 0 elements. + fn infer_slice_pat( + &mut self, + pat: PatId, + before: &[PatId], + slice: Option<PatId>, + after: &[PatId], + expected: Ty<'db>, + pat_info: PatInfo, + ) -> Ty<'db> { + let expected = self.table.try_structurally_resolve_type(pat.into(), expected); + + // If the pattern is irrefutable and `expected` is an infer ty, we try to equate it + // to an array if the given pattern allows it. See issue #76342 + if self.pat_is_irrefutable(pat_info.pat_origin) + && expected.is_ty_var() + && let Some(resolved_arr_ty) = self.try_resolve_slice_ty_to_array_ty(before, slice, pat) + { + debug!(?resolved_arr_ty); + let _ = self.demand_eqtype(pat.into(), expected, resolved_arr_ty); + } + + let expected = self.structurally_resolve_type(pat.into(), expected); + debug!(?expected); + + let (element_ty, opt_slice_ty, inferred) = match expected.kind() { + // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. + TyKind::Array(element_ty, len) => { + let min = before.len() as u64 + after.len() as u64; + let (opt_slice_ty, expected) = + self.check_array_pat_len(pat, element_ty, expected, slice, len, min.into()); + // `opt_slice_ty.is_none()` => `slice.is_none()`. + // Note, though, that opt_slice_ty could be `Some(error_ty)`. + assert!(opt_slice_ty.is_some() || slice.is_none()); + (element_ty, opt_slice_ty, expected) + } + TyKind::Slice(element_ty) => (element_ty, Some(expected), expected), + // The expected type must be an array or slice, but was neither, so error. + _ => { + self.push_diagnostic(InferenceDiagnostic::ExpectedArrayOrSlicePat { + pat, + found: expected.store(), + }); + let err = self.types.types.error; + (err, Some(err), err) + } + }; + + // Type check all the patterns before `slice`. + for &elt in before { + self.infer_pat(elt, element_ty, pat_info); + } + // Type check the `slice`, if present, against its expected type. + if let Some(slice) = slice { + self.infer_pat(slice, opt_slice_ty.unwrap(), pat_info); + } + // Type check the elements after `slice`, if present. + for &elt in after { + self.infer_pat(elt, element_ty, pat_info); + } + inferred + } + + /// Type check the length of an array pattern. + /// + /// Returns both the type of the variable length pattern (or `None`), and the potentially + /// inferred array type. We only return `None` for the slice type if `slice.is_none()`. + fn check_array_pat_len( + &mut self, + pat: PatId, + element_ty: Ty<'db>, + arr_ty: Ty<'db>, + slice: Option<PatId>, + len: Const<'db>, + min_len: u128, + ) -> (Option<Ty<'db>>, Ty<'db>) { + let len = crate::consteval::try_const_usize(self.db, len); + + if let Some(len) = len { + // Now we know the length... + if slice.is_none() { + // ...and since there is no variable-length pattern, + // we require an exact match between the number of elements + // in the array pattern and as provided by the matched type. + if min_len == len { + return (None, arr_ty); + } + + self.push_diagnostic(InferenceDiagnostic::MismatchedArrayPatLen { + pat, + expected: len, + found: min_len, + has_rest: false, + }); + } else if let Some(pat_len) = len.checked_sub(min_len) { + // The variable-length pattern was there, + // so it has an array type with the remaining elements left as its size... + return (Some(Ty::new_array(self.interner(), element_ty, pat_len)), arr_ty); + } else { + // ...however, in this case, there were no remaining elements. + // That is, the slice pattern requires more than the array type offers. + self.push_diagnostic(InferenceDiagnostic::MismatchedArrayPatLen { + pat, + expected: len, + found: min_len, + has_rest: true, + }); + } + } else if slice.is_none() { + // We have a pattern with a fixed length, + // which we can use to infer the length of the array. + let updated_arr_ty = Ty::new_array(self.interner(), element_ty, min_len); + _ = self.demand_eqtype(pat.into(), updated_arr_ty, arr_ty); + return (None, updated_arr_ty); + } else { + // We have a variable-length pattern and don't know the array length. + // This happens if we have e.g., + // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. + // FIXME: Emit an error: cannot pattern-match on an array without a fixed length. + }; + + // If we get here, we must have emitted an error. + (Some(self.types.types.error), arr_ty) + } + + fn infer_destructuring_assignment_expr(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> { + // LHS of assignment doesn't constitute reads. + let expr_is_read = ExprIsRead::No; + let lhs_ty = self.infer_expr_inner(expr, &Expectation::has_type(expected), expr_is_read); + match self.coerce(expr, expected, lhs_ty, AllowTwoPhase::No, expr_is_read) { + Ok(ty) => ty, + Err(_) => { + self.emit_type_mismatch(expr.into(), expected, lhs_ty); + // `rhs_ty` is returned so no further type mismatches are + // reported because of this mismatch. + expected + } + } + } } diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 3cadc8e933..704f15cc86 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -11,14 +11,14 @@ use rustc_type_ir::inherent::{SliceLike, Ty as _}; use stdx::never; use crate::{ - InferenceDiagnostic, ValueTyDefId, - generics::generics, - infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, + InferenceDiagnostic, Span, ValueTyDefId, + infer::{ + InferenceTyLoweringVarsCtx, diagnostics::InferenceTyLoweringContext as TyLoweringContext, + }, lower::{GenericPredicates, LifetimeElisionKind}, method_resolution::{self, CandidateId, MethodError}, next_solver::{ - GenericArg, GenericArgs, TraitRef, Ty, - infer::traits::{Obligation, ObligationCause}, + GenericArg, GenericArgs, TraitRef, Ty, Unnormalized, infer::traits::ObligationCause, util::clauses_as_obligations, }, }; @@ -26,29 +26,36 @@ use crate::{ use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; impl<'db> InferenceContext<'_, 'db> { - pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty<'db>> { - let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? { - ValuePathResolution::GenericDef(value_def, generic_def, substs) => { - (value_def, generic_def, substs) - } - ValuePathResolution::NonGeneric(ty) => return Some(ty), - }; + pub(super) fn infer_path( + &mut self, + path: &Path, + id: ExprOrPatId, + ) -> Option<(ValueNs, Ty<'db>)> { + let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; + + let (value_def, generic_def, substs) = + match self.resolve_value_path(path, id, value, self_subst)? { + ValuePathResolution::GenericDef(value_def, generic_def, substs) => { + (value_def, generic_def, substs) + } + ValuePathResolution::NonGeneric(ty) => return Some((value, ty)), + }; let args = self.insert_type_vars(substs); - self.add_required_obligations_for_value_path(generic_def, args); + self.add_required_obligations_for_value_path(id, generic_def, args); - let ty = self.db.value_ty(value_def)?.instantiate(self.interner(), args); + let ty = self.db.value_ty(value_def)?.instantiate(self.interner(), args).skip_norm_wip(); let ty = self.process_remote_user_written_ty(ty); - Some(ty) + Some((value, ty)) } fn resolve_value_path( &mut self, path: &Path, id: ExprOrPatId, + value: ValueNs, + self_subst: Option<GenericArgs<'db>>, ) -> Option<ValuePathResolution<'db>> { - let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; - let value_def: ValueTyDefId = match value { ValueNs::FunctionId(it) => it.into(), ValueNs::ConstId(it) => it.into(), @@ -73,7 +80,7 @@ impl<'db> InferenceContext<'_, 'db> { }; } ValueNs::ImplSelf(impl_id) => { - let ty = self.db.impl_self_ty(impl_id).instantiate_identity(); + let ty = self.db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); return if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { Some(ValuePathResolution::GenericDef( struct_id.into(), @@ -86,7 +93,7 @@ impl<'db> InferenceContext<'_, 'db> { }; } ValueNs::GenericParam(it) => { - return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty_ns(it))); + return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it))); } }; @@ -112,7 +119,7 @@ impl<'db> InferenceContext<'_, 'db> { if let Some(last_segment) = last_segment { path_ctx.set_current_segment(last_segment) } - path_ctx.substs_from_path(value_def, true, false) + path_ctx.substs_from_path(value_def, true, false, id.into()) }) }; @@ -134,14 +141,23 @@ impl<'db> InferenceContext<'_, 'db> { no_diagnostics: bool, ) -> Option<(ValueNs, Option<GenericArgs<'db>>)> { // Don't use `self.make_ty()` here as we need `orig_ns`. + let mut vars_ctx = InferenceTyLoweringVarsCtx { + table: &mut self.table, + type_of_type_placeholder: &mut self.result.type_of_type_placeholder, + }; let mut ctx = TyLoweringContext::new( self.db, &self.resolver, self.store, &self.diagnostics, InferenceTyDiagnosticSource::Body, + self.store_owner, self.generic_def, + &self.generics, LifetimeElisionKind::Infer, + self.allow_using_generic_params, + Some(&mut vars_ctx), + &self.defined_anon_consts, ); let mut path_ctx = if no_diagnostics { ctx.at_path_forget_diagnostics(path) @@ -152,10 +168,10 @@ impl<'db> InferenceContext<'_, 'db> { let last = path.segments().last()?; let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref); - let ty = self.table.process_user_written_ty(ty); + let ty = path_ctx.expect_table().process_user_written_ty(ty); path_ctx.ignore_last_segment(); - let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true); + let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true, id.into()); drop_ctx(ctx, no_diagnostics); let ty = self.table.process_user_written_ty(ty); self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? @@ -182,9 +198,13 @@ impl<'db> InferenceContext<'_, 'db> { let (resolution, substs) = match (def, is_before_last) { (TypeNs::TraitId(trait_), true) => { - let self_ty = self.table.next_ty_var(); - let trait_ref = - path_ctx.lower_trait_ref_from_resolved_path(trait_, self_ty, true); + let self_ty = path_ctx.expect_table().next_ty_var(id.into()); + let trait_ref = path_ctx.lower_trait_ref_from_resolved_path( + trait_, + self_ty, + true, + id.into(), + ); drop_ctx(ctx, no_diagnostics); self.resolve_trait_assoc_item(trait_ref, last_segment, id) } @@ -194,7 +214,7 @@ impl<'db> InferenceContext<'_, 'db> { // should resolve to an associated type of that trait (e.g. `<T // as Iterator>::Item::default`) path_ctx.ignore_last_segment(); - let (ty, _) = path_ctx.lower_partly_resolved_path(def, true); + let (ty, _) = path_ctx.lower_partly_resolved_path(def, true, id.into()); drop_ctx(ctx, no_diagnostics); if ty.is_ty_error() { return None; @@ -219,8 +239,9 @@ impl<'db> InferenceContext<'_, 'db> { } } - fn add_required_obligations_for_value_path( + pub(super) fn add_required_obligations_for_value_path( &mut self, + node: ExprOrPatId, def: GenericDefId, subst: GenericArgs<'db>, ) { @@ -228,29 +249,12 @@ impl<'db> InferenceContext<'_, 'db> { let predicates = GenericPredicates::query_all(self.db, def); let param_env = self.table.param_env; self.table.register_predicates(clauses_as_obligations( - predicates.iter_instantiated(interner, subst.as_slice()), - ObligationCause::new(), + predicates + .iter_instantiated(interner, subst.as_slice()) + .map(Unnormalized::skip_norm_wip), + ObligationCause::new(node), param_env, )); - - // We need to add `Self: Trait` obligation when `def` is a trait assoc item. - let container = match def { - GenericDefId::FunctionId(id) => id.lookup(self.db).container, - GenericDefId::ConstId(id) => id.lookup(self.db).container, - _ => return, - }; - - if let ItemContainerId::TraitId(trait_) = container { - let parent_len = generics(self.db, def).parent_generics().map_or(0, |g| g.len_self()); - let parent_subst = GenericArgs::new_from_slice(&subst.as_slice()[..parent_len]); - let trait_ref = TraitRef::new_from_args(interner, trait_.into(), parent_subst); - self.table.register_predicate(Obligation::new( - interner, - ObligationCause::new(), - param_env, - trait_ref, - )); - } } fn resolve_trait_assoc_item( @@ -304,7 +308,7 @@ impl<'db> InferenceContext<'_, 'db> { return Some(result); } - let res = self.with_method_resolution(|ctx| { + let res = self.with_method_resolution(Span::Dummy, Span::Dummy, |ctx| { ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty) }); let (item, visible) = match res { @@ -324,28 +328,23 @@ impl<'db> InferenceContext<'_, 'db> { }; let substs = match container { ItemContainerId::ImplId(impl_id) => { - let impl_substs = self.table.fresh_args_for_item(impl_id.into()); - let impl_self_ty = - self.db.impl_self_ty(impl_id).instantiate(self.interner(), impl_substs); - self.unify(impl_self_ty, ty); + let impl_substs = self.table.fresh_args_for_item(id.into(), impl_id.into()); + let impl_self_ty = self + .db + .impl_self_ty(impl_id) + .instantiate(self.interner(), impl_substs) + .skip_norm_wip(); + _ = self.demand_eqtype(id, impl_self_ty, ty); impl_substs } ItemContainerId::TraitId(trait_) => { // we're picking this method - let args = GenericArgs::fill_rest( + GenericArgs::fill_rest( self.interner(), trait_.into(), [ty.into()], - |_, id, _| self.table.next_var_for_param(id), - ); - let trait_ref = TraitRef::new_from_args(self.interner(), trait_.into(), args); - self.table.register_predicate(Obligation::new( - self.interner(), - ObligationCause::new(), - self.table.param_env, - trait_ref, - )); - args + |_, param, _| self.table.var_for_def(param, id.into()), + ) } ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { never!("assoc item contained in module/extern block"); @@ -370,7 +369,7 @@ impl<'db> InferenceContext<'_, 'db> { name: &Name, id: ExprOrPatId, ) -> Option<(ValueNs, GenericArgs<'db>)> { - let ty = self.table.try_structurally_resolve_type(ty); + let ty = self.table.try_structurally_resolve_type(id.into(), ty); let (enum_id, subst) = match ty.as_adt() { Some((AdtId::EnumId(e), subst)) => (e, subst), _ => return None, diff --git a/crates/hir-ty/src/infer/place_op.rs b/crates/hir-ty/src/infer/place_op.rs index 1298b38097..bbf047b8ba 100644 --- a/crates/hir-ty/src/infer/place_op.rs +++ b/crates/hir-ty/src/infer/place_op.rs @@ -1,7 +1,6 @@ //! Inference of *place operators*: deref and indexing (operators that create places, as opposed to values). use hir_def::hir::ExprId; -use intern::sym; use rustc_ast_ir::Mutability; use rustc_type_ir::inherent::{IntoKind, Ty as _}; use tracing::debug; @@ -29,9 +28,10 @@ pub(super) enum PlaceOp { impl<'a, 'db> InferenceContext<'a, 'db> { pub(super) fn try_overloaded_deref( &self, + expr: ExprId, base_ty: Ty<'db>, ) -> Option<InferOk<'db, MethodCallee<'db>>> { - self.try_overloaded_place_op(base_ty, None, PlaceOp::Deref) + self.try_overloaded_place_op(expr, base_ty, None, PlaceOp::Deref) } /// For the overloaded place expressions (`*x`, `x[3]`), the trait @@ -57,7 +57,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { return Some(ty); } - let ok = self.try_overloaded_deref(oprnd_ty)?; + let ok = self.try_overloaded_deref(expr, oprnd_ty)?; let method = self.table.register_infer_ok(ok); if let TyKind::Ref(_, _, Mutability::Not) = method.sig.inputs_and_output.inputs()[0].kind() { @@ -81,6 +81,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { &mut self, expr: ExprId, base_expr: ExprId, + index_expr: ExprId, base_ty: Ty<'db>, idx_ty: Ty<'db>, ) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> { @@ -88,10 +89,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // autoderef that normal method probing does. They could likely be // consolidated. - let mut autoderef = InferenceContextAutoderef::new_from_inference_context(self, base_ty); + let mut autoderef = + InferenceContextAutoderef::new_from_inference_context(self, base_ty, base_expr.into()); let mut result = None; while result.is_none() && autoderef.next().is_some() { - result = Self::try_index_step(expr, base_expr, &mut autoderef, idx_ty); + result = Self::try_index_step(expr, base_expr, index_expr, &mut autoderef, idx_ty); } result } @@ -104,11 +106,12 @@ impl<'a, 'db> InferenceContext<'a, 'db> { fn try_index_step( expr: ExprId, base_expr: ExprId, + index_expr: ExprId, autoderef: &mut InferenceContextAutoderef<'_, 'a, 'db>, index_ty: Ty<'db>, ) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> { let ty = autoderef.final_ty(); - let adjusted_ty = autoderef.ctx().table.structurally_resolve_type(ty); + let adjusted_ty = autoderef.ctx().structurally_resolve_type(base_expr.into(), ty); debug!( "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ index_ty={:?})", @@ -123,7 +126,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let ctx = autoderef.ctx(); ctx.table.register_predicate(Obligation::new( ctx.interner(), - ObligationCause::new(), + ObligationCause::new(base_expr), ctx.table.param_env, ClauseKind::ConstArgHasType(ct, ctx.types.types.usize), )); @@ -136,9 +139,13 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // If some lookup succeeds, write callee into table and extract index/element // type from the method signature. // If some lookup succeeded, install method in table - let input_ty = autoderef.ctx().table.next_ty_var(); - let method = - autoderef.ctx().try_overloaded_place_op(self_ty, Some(input_ty), PlaceOp::Index); + let input_ty = autoderef.ctx().table.next_ty_var(index_expr.into()); + let method = autoderef.ctx().try_overloaded_place_op( + expr, + self_ty, + Some(input_ty), + PlaceOp::Index, + ); if let Some(result) = method { debug!("try_index_step: success, using overloaded indexing"); @@ -180,15 +187,16 @@ impl<'a, 'db> InferenceContext<'a, 'db> { /// `convert_place_derefs_to_mutable`. pub(super) fn try_overloaded_place_op( &self, + expr: ExprId, base_ty: Ty<'db>, opt_rhs_ty: Option<Ty<'db>>, op: PlaceOp, ) -> Option<InferOk<'db, MethodCallee<'db>>> { debug!("try_overloaded_place_op({:?},{:?})", base_ty, op); - let (Some(imm_tr), imm_op) = (match op { - PlaceOp::Deref => (self.lang_items.Deref, sym::deref), - PlaceOp::Index => (self.lang_items.Index, sym::index), + let (Some(imm_tr), Some(imm_op)) = (match op { + PlaceOp::Deref => (self.lang_items.Deref, self.lang_items.Deref_deref), + PlaceOp::Index => (self.lang_items.Index, self.lang_items.Index_index), }) else { // Bail if `Deref` or `Index` isn't defined. return None; @@ -198,9 +206,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // opaque types as rigid here to support `impl Deref<Target = impl Index<usize>>`. let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; self.table.lookup_method_for_operator( - ObligationCause::new(), - imm_op, + ObligationCause::new(expr), imm_tr, + imm_op, base_ty, opt_rhs_ty, treat_opaques, @@ -209,6 +217,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { pub(super) fn try_mutable_overloaded_place_op( table: &InferenceTable<'db>, + expr: ExprId, base_ty: Ty<'db>, opt_rhs_ty: Option<Ty<'db>>, op: PlaceOp, @@ -216,9 +225,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { debug!("try_mutable_overloaded_place_op({:?},{:?})", base_ty, op); let lang_items = table.interner().lang_items(); - let (Some(mut_tr), mut_op) = (match op { - PlaceOp::Deref => (lang_items.DerefMut, sym::deref_mut), - PlaceOp::Index => (lang_items.IndexMut, sym::index_mut), + let (Some(mut_tr), Some(mut_op)) = (match op { + PlaceOp::Deref => (lang_items.DerefMut, lang_items.DerefMut_deref_mut), + PlaceOp::Index => (lang_items.IndexMut, lang_items.IndexMut_index_mut), }) else { // Bail if `DerefMut` or `IndexMut` isn't defined. return None; @@ -230,9 +239,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // of the opaque. let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; table.lookup_method_for_operator( - ObligationCause::new(), - mut_op, + ObligationCause::new(expr), mut_tr, + mut_op, base_ty, opt_rhs_ty, treat_opaques, @@ -276,7 +285,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { )) } }; - let method = Self::try_mutable_overloaded_place_op(&self.table, base_ty, arg_ty, op); + let method = Self::try_mutable_overloaded_place_op(&self.table, expr, base_ty, arg_ty, op); let method = match method { Some(ok) => self.table.register_infer_ok(ok), // Couldn't find the mutable variant of the place op, keep the diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index b0f916b8c0..f9ad76b0c1 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -3,35 +3,35 @@ use std::fmt; use base_db::Crate; -use hir_def::{AdtId, ExpressionStoreOwnerId, GenericParamId, TraitId}; +use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId}; use rustc_hash::FxHashSet; use rustc_type_ir::{ - TyVid, TypeFoldable, TypeVisitableExt, UpcastFrom, + TyVid, TypeFoldable, TypeVisitableExt, inherent::{Const as _, GenericArg as _, IntoKind, Ty as _}, solve::Certainty, }; use smallvec::SmallVec; +use thin_vec::ThinVec; use crate::{ + InferenceDiagnostic, Span, db::HirDatabase, next_solver::{ - Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Goal, - ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind, - TypingMode, + Canonical, ClauseKind, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, + GenericArgs, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, + TyKind, TypingMode, fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ - DbInternerInferExt, InferCtxt, InferOk, InferResult, - at::{At, ToTrace}, + DbInternerInferExt, InferCtxt, InferOk, + at::At, snapshot::CombinedSnapshot, traits::{Obligation, ObligationCause, PredicateObligation}, }, inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}, obligation_ctxt::ObligationCtxt, }, - traits::{ - NextTraitSolveResult, ParamEnvAndCrate, next_trait_solve_canonical_in_ctxt, - next_trait_solve_in_ctxt, - }, + solver_errors::SolverDiagnostic, + traits::ParamEnvAndCrate, }; struct NestedObligationsForSelfTy<'a, 'db> { @@ -44,6 +44,10 @@ struct NestedObligationsForSelfTy<'a, 'db> { impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { type Result = (); + fn span(&self) -> Span { + self.root_cause.span() + } + fn config(&self) -> InspectConfig { // Using an intentionally low depth to minimize the chance of future // breaking changes in case we adapt the approach later on. This also @@ -63,7 +67,7 @@ impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty) { self.obligations_for_self_ty.push(Obligation::new( db, - self.root_cause.clone(), + *self.root_cause, goal.param_env, goal.predicate, )); @@ -115,7 +119,7 @@ fn could_unify_impl<'db>( let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); let cause = ObligationCause::dummy(); let at = infcx.at(&cause, env.param_env); - let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(tys); + let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(Span::Dummy, tys); let mut ctxt = ObligationCtxt::new(&infcx); let can_unify = at .eq(ty1_with_vars, ty2_with_vars) @@ -124,18 +128,13 @@ fn could_unify_impl<'db>( can_unify && select(&mut ctxt).is_empty() } -#[derive(Clone)] pub(crate) struct InferenceTable<'db> { pub(crate) db: &'db dyn HirDatabase, pub(crate) param_env: ParamEnv<'db>, pub(crate) infer_ctxt: InferCtxt<'db>, pub(super) fulfillment_cx: FulfillmentCtxt<'db>, pub(super) diverging_type_vars: FxHashSet<Ty<'db>>, -} - -pub(crate) struct InferenceTableSnapshot<'db> { - ctxt_snapshot: CombinedSnapshot, - obligations: FulfillmentCtxt<'db>, + pub(super) trait_errors: Vec<NextSolverError<'db>>, } impl<'db> InferenceTable<'db> { @@ -145,14 +144,10 @@ impl<'db> InferenceTable<'db> { db: &'db dyn HirDatabase, trait_env: ParamEnv<'db>, krate: Crate, - owner: Option<ExpressionStoreOwnerId>, + owner: ExpressionStoreOwnerId, ) -> Self { let interner = DbInterner::new_with(db, krate); - let typing_mode = match owner { - Some(owner) => TypingMode::typeck_for_body(interner, owner.into()), - // IDE things wants to reveal opaque types. - None => TypingMode::PostAnalysis, - }; + let typing_mode = TypingMode::typeck_for_body(interner, owner.into()); let infer_ctxt = interner.infer_ctxt().build(typing_mode); InferenceTable { db, @@ -160,6 +155,7 @@ impl<'db> InferenceTable<'db> { fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt), infer_ctxt, diverging_type_vars: FxHashSet::default(), + trait_errors: Vec::new(), } } @@ -172,6 +168,10 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.type_is_copy_modulo_regions(self.param_env, ty) } + pub(crate) fn type_is_sized_modulo_regions(&self, ty: Ty<'db>) -> bool { + self.infer_ctxt.type_is_sized_modulo_regions(self.param_env, ty) + } + pub(crate) fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'db>) -> bool { self.infer_ctxt.type_is_use_cloned_modulo_regions(self.param_env, ty) } @@ -253,29 +253,12 @@ impl<'db> InferenceTable<'db> { self.diverging_type_vars.insert(ty); } - pub(crate) fn canonicalize<T>(&mut self, t: T) -> rustc_type_ir::Canonical<DbInterner<'db>, T> - where - T: TypeFoldable<DbInterner<'db>>, - { - // try to resolve obligations before canonicalizing, since this might - // result in new knowledge about variables - self.select_obligations_where_possible(); - self.infer_ctxt.canonicalize_response(t) - } - - pub(crate) fn normalize_alias_ty(&mut self, alias: Ty<'db>) -> Ty<'db> { - self.infer_ctxt - .at(&ObligationCause::new(), self.param_env) - .structurally_normalize_ty(alias, &mut self.fulfillment_cx) - .unwrap_or(alias) - } - - pub(crate) fn next_ty_var(&self) -> Ty<'db> { - self.infer_ctxt.next_ty_var() + pub(crate) fn next_ty_var(&self, span: Span) -> Ty<'db> { + self.infer_ctxt.next_ty_var(span) } - pub(crate) fn next_const_var(&self) -> Const<'db> { - self.infer_ctxt.next_const_var() + pub(crate) fn next_const_var(&self, span: Span) -> Const<'db> { + self.infer_ctxt.next_const_var(span) } pub(crate) fn next_int_var(&self) -> Ty<'db> { @@ -286,42 +269,18 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.next_float_var() } - pub(crate) fn new_maybe_never_var(&mut self) -> Ty<'db> { - let var = self.next_ty_var(); + pub(crate) fn new_maybe_never_var(&mut self, span: Span) -> Ty<'db> { + let var = self.next_ty_var(span); self.set_diverging(var); var } - pub(crate) fn next_region_var(&self) -> Region<'db> { - self.infer_ctxt.next_region_var() - } - - pub(crate) fn next_var_for_param(&self, id: GenericParamId) -> GenericArg<'db> { - self.infer_ctxt.next_var_for_param(id) - } - - pub(crate) fn resolve_completely<T>(&mut self, value: T) -> T - where - T: TypeFoldable<DbInterner<'db>>, - { - let value = self.infer_ctxt.resolve_vars_if_possible(value); - - let mut goals = vec![]; - - // FIXME(next-solver): Handle `goals`. - - value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals)) + pub(crate) fn next_region_var(&self, span: Span) -> Region<'db> { + self.infer_ctxt.next_region_var(span) } - /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify<T: ToTrace<'db>>(&mut self, ty1: T, ty2: T) -> bool { - self.try_unify(ty1, ty2).map(|infer_ok| self.register_infer_ok(infer_ok)).is_ok() - } - - /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the - /// caller needs to deal with them. - pub(crate) fn try_unify<T: ToTrace<'db>>(&mut self, t1: T, t2: T) -> InferResult<'db, ()> { - self.at(&ObligationCause::new()).eq(t1, t2) + pub(crate) fn var_for_def(&self, id: GenericParamId, span: Span) -> GenericArg<'db> { + self.infer_ctxt.var_for_def(id, span) } pub(crate) fn at<'a>(&'a self, cause: &'a ObligationCause) -> At<'a, 'db> { @@ -332,6 +291,10 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.shallow_resolve(ty) } + pub(crate) fn resolve_vars_if_possible<T: TypeFoldable<DbInterner<'db>>>(&self, t: T) -> T { + self.infer_ctxt.resolve_vars_if_possible(t) + } + pub(crate) fn resolve_vars_with_obligations<T>(&mut self, t: T) -> T where T: rustc_type_ir::TypeFoldable<DbInterner<'db>>, @@ -351,8 +314,8 @@ impl<'db> InferenceTable<'db> { } /// Create a `GenericArgs` full of infer vars for `def`. - pub(crate) fn fresh_args_for_item(&self, def: SolverDefId) -> GenericArgs<'db> { - self.infer_ctxt.fresh_args_for_item(def) + pub(crate) fn fresh_args_for_item(&self, span: Span, def: SolverDefId) -> GenericArgs<'db> { + self.infer_ctxt.fresh_args_for_item(span, def) } /// Try to resolve `ty` to a structural type, normalizing aliases. @@ -360,36 +323,55 @@ impl<'db> InferenceTable<'db> { /// In case there is still ambiguity, the returned type may be an inference /// variable. This is different from `structurally_resolve_type` which errors /// in this case. - pub(crate) fn try_structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> { + pub(crate) fn try_structurally_resolve_type(&mut self, span: Span, ty: Ty<'db>) -> Ty<'db> { if let TyKind::Alias(..) = ty.kind() { let result = self .infer_ctxt - .at(&ObligationCause::misc(), self.param_env) + .at(&ObligationCause::new(span), self.param_env) .structurally_normalize_ty(ty, &mut self.fulfillment_cx); match result { Ok(normalized_ty) => normalized_ty, - Err(_errors) => Ty::new_error(self.interner(), ErrorGuaranteed), + Err(errors) => { + self.trait_errors.extend(errors); + Ty::new_error(self.interner(), ErrorGuaranteed) + } } } else { self.resolve_vars_with_obligations(ty) } } - pub(crate) fn structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> { - self.try_structurally_resolve_type(ty) - // FIXME: Err if it still contain infer vars. + pub(crate) fn try_structurally_resolve_const( + &mut self, + sp: Span, + ct: Const<'db>, + ) -> Const<'db> { + let ct = self.resolve_vars_with_obligations(ct); + + if let ConstKind::Unevaluated(..) = ct.kind() { + let result = self + .infer_ctxt + .at(&ObligationCause::new(sp), self.param_env) + .structurally_normalize_const(ct, &mut self.fulfillment_cx); + match result { + Ok(normalized_ct) => normalized_ct, + Err(errors) => { + self.trait_errors.extend(errors); + Const::new_error(self.interner(), ErrorGuaranteed) + } + } + } else { + ct + } } - pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'db> { - let ctxt_snapshot = self.infer_ctxt.start_snapshot(); - let obligations = self.fulfillment_cx.clone(); - InferenceTableSnapshot { ctxt_snapshot, obligations } + pub(crate) fn snapshot(&mut self) -> CombinedSnapshot { + self.infer_ctxt.start_snapshot() } #[tracing::instrument(skip_all)] - pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'db>) { - self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); - self.fulfillment_cx = snapshot.obligations; + pub(crate) fn rollback_to(&mut self, snapshot: CombinedSnapshot) { + self.infer_ctxt.rollback_to(snapshot); } pub(crate) fn commit_if_ok<T, E>( @@ -399,51 +381,12 @@ impl<'db> InferenceTable<'db> { let snapshot = self.snapshot(); let result = f(self); match result { - Ok(_) => {} - Err(_) => { - self.rollback_to(snapshot); - } + Ok(_) => self.infer_ctxt.commit_from(snapshot), + Err(_) => self.rollback_to(snapshot), } result } - /// Checks an obligation without registering it. Useful mostly to check - /// whether a trait *might* be implemented before deciding to 'lock in' the - /// choice (during e.g. method resolution or deref). - #[tracing::instrument(level = "debug", skip(self))] - pub(crate) fn try_obligation(&mut self, predicate: Predicate<'db>) -> NextTraitSolveResult { - let goal = Goal { param_env: self.param_env, predicate }; - let canonicalized = self.canonicalize(goal); - - next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized) - } - - pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) { - let goal = Goal { param_env: self.param_env, predicate }; - self.register_obligation_in_env(goal) - } - - #[tracing::instrument(level = "debug", skip(self))] - fn register_obligation_in_env(&mut self, goal: Goal<'db, Predicate<'db>>) { - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); - tracing::debug!(?result); - match result { - Ok((_, Certainty::Yes)) => {} - Err(rustc_type_ir::solve::NoSolution) => {} - Ok((_, Certainty::Maybe { .. })) => { - self.fulfillment_cx.register_predicate_obligation( - &self.infer_ctxt, - Obligation::new( - self.interner(), - ObligationCause::new(), - goal.param_env, - goal.predicate, - ), - ); - } - } - } - pub(crate) fn register_bound(&mut self, ty: Ty<'db>, def_id: TraitId, cause: ObligationCause) { if !ty.references_non_lt_error() { let trait_ref = TraitRef::new(self.interner(), def_id.into(), [ty]); @@ -463,7 +406,8 @@ impl<'db> InferenceTable<'db> { } pub(crate) fn select_obligations_where_possible(&mut self) { - self.fulfillment_cx.try_evaluate_obligations(&self.infer_ctxt); + let errors = self.fulfillment_cx.try_evaluate_obligations(&self.infer_ctxt); + self.trait_errors.extend(errors); } pub(super) fn register_predicate(&mut self, obligation: PredicateObligation<'db>) { @@ -478,9 +422,7 @@ impl<'db> InferenceTable<'db> { where I: IntoIterator<Item = PredicateObligation<'db>>, { - obligations.into_iter().for_each(|obligation| { - self.register_predicate(obligation); - }); + self.fulfillment_cx.register_predicate_obligations(&self.infer_ctxt, obligations); } /// checking later, during regionck, that `arg` is well-formed. @@ -494,9 +436,9 @@ impl<'db> InferenceTable<'db> { } /// Registers obligations that all `args` are well-formed. - pub(crate) fn add_wf_bounds(&mut self, args: GenericArgs<'db>) { + pub(crate) fn add_wf_bounds(&mut self, span: Span, args: GenericArgs<'db>) { for term in args.iter().filter_map(|it| it.as_term()) { - self.register_wf_obligation(term, ObligationCause::new()); + self.register_wf_obligation(term, ObligationCause::new(span)); } } @@ -507,15 +449,9 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.insert_type_vars(ty) } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. - pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { - if ty.is_ty_error() { self.next_ty_var() } else { ty } - } - /// Whenever you lower a user-written type, you should call this. pub(crate) fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { self.process_remote_user_written_ty(ty) - // FIXME: Register a well-formed obligation. } /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, @@ -525,77 +461,17 @@ impl<'db> InferenceTable<'db> { // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495: // Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs // to normalize before inspecting the `TyKind`. - // FIXME(next-solver): We should not deeply normalize here, only shallowly. - self.try_structurally_resolve_type(ty) - } - - /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. - pub(super) fn insert_const_vars_shallow(&mut self, c: Const<'db>) -> Const<'db> { - if c.is_ct_error() { self.next_const_var() } else { c } - } - - /// Check if given type is `Sized` or not - pub(crate) fn is_sized(&mut self, ty: Ty<'db>) -> bool { - fn short_circuit_trivial_tys(ty: Ty<'_>) -> Option<bool> { - match ty.kind() { - TyKind::Bool - | TyKind::Char - | TyKind::Int(_) - | TyKind::Uint(_) - | TyKind::Float(_) - | TyKind::Ref(..) - | TyKind::RawPtr(..) - | TyKind::Never - | TyKind::FnDef(..) - | TyKind::Array(..) - | TyKind::FnPtr(..) => Some(true), - TyKind::Slice(..) | TyKind::Str | TyKind::Dynamic(..) => Some(false), - _ => None, - } - } - - let mut ty = ty; - ty = self.try_structurally_resolve_type(ty); - if let Some(sized) = short_circuit_trivial_tys(ty) { - return sized; - } - - { - let mut structs = SmallVec::<[_; 8]>::new(); - // Must use a loop here and not recursion because otherwise users will conduct completely - // artificial examples of structs that have themselves as the tail field and complain r-a crashes. - while let Some((AdtId::StructId(id), subst)) = ty.as_adt() { - let struct_data = id.fields(self.db); - if let Some((last_field, _)) = struct_data.fields().iter().next_back() { - let last_field_ty = self.db.field_types(id.into())[last_field] - .get() - .instantiate(self.interner(), subst); - if structs.contains(&ty) { - // A struct recursively contains itself as a tail field somewhere. - return true; // Don't overload the users with too many errors. - } - structs.push(ty); - // Structs can have DST as its last field and such cases are not handled - // as unsized by the chalk, so we do this manually. - ty = last_field_ty; - ty = self.try_structurally_resolve_type(ty); - if let Some(sized) = short_circuit_trivial_tys(ty) { - return sized; - } - } else { - break; - }; - } - } + self.try_structurally_resolve_type(Span::Dummy, ty) + } - let Some(sized) = self.interner().lang_items().Sized else { - return false; - }; - let sized_pred = Predicate::upcast_from( - TraitRef::new(self.interner(), sized.into(), [ty]), - self.interner(), - ); - self.try_obligation(sized_pred).certain() + fn emit_trait_errors(&mut self, diagnostics: &mut ThinVec<InferenceDiagnostic>) { + diagnostics.extend(std::mem::take(&mut self.trait_errors).into_iter().filter_map( + |error| { + let error = error.into_fulfillment_error(&self.infer_ctxt); + SolverDiagnostic::from_fulfillment_error(&error) + .map(InferenceDiagnostic::SolverDiagnostic) + }, + )); } } @@ -608,20 +484,229 @@ impl fmt::Debug for InferenceTable<'_> { } } -mod resolve_completely { - use rustc_type_ir::{DebruijnIndex, Flags, TypeFolder, TypeSuperFoldable}; +pub(super) mod resolve_completely { + use rustc_hash::FxHashSet; + use rustc_type_ir::{ + DebruijnIndex, Flags, InferConst, InferTy, TypeFlags, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, inherent::IntoKind, + }; + use stdx::never; + use thin_vec::ThinVec; use crate::{ + InferenceDiagnostic, Span, infer::unify::InferenceTable, next_solver::{ - Const, DbInterner, Goal, Predicate, Region, Term, Ty, + Const, ConstKind, DbInterner, DefaultAny, GenericArg, Goal, Predicate, Region, Term, + TermKind, Ty, TyKind, infer::{resolve::ReplaceInferWithError, traits::ObligationCause}, normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals, }, }; + pub(crate) struct WriteBackCtxt<'db> { + table: InferenceTable<'db>, + diagnostics: ThinVec<InferenceDiagnostic>, + has_errors: bool, + spans_emitted_type_must_be_known_for: FxHashSet<Span>, + types: &'db DefaultAny<'db>, + } + + impl<'db> WriteBackCtxt<'db> { + pub(crate) fn new( + table: InferenceTable<'db>, + diagnostics: ThinVec<InferenceDiagnostic>, + vars_emitted_type_must_be_known_for: FxHashSet<Term<'db>>, + ) -> Self { + let spans_emitted_type_must_be_known_for = vars_emitted_type_must_be_known_for + .into_iter() + .filter_map(|term| match term.kind() { + TermKind::Ty(ty) => match ty.kind() { + TyKind::Infer(InferTy::TyVar(vid)) => { + Some(table.infer_ctxt.type_var_span(vid)) + } + _ => None, + }, + TermKind::Const(ct) => match ct.kind() { + ConstKind::Infer(InferConst::Var(vid)) => { + table.infer_ctxt.const_var_span(vid) + } + _ => None, + }, + }) + .collect(); + + Self { + types: table.interner().default_types(), + table, + diagnostics, + has_errors: false, + spans_emitted_type_must_be_known_for, + } + } + + pub(crate) fn resolve_completely<T>(&mut self, value_ref: &mut T) + where + T: TypeFoldable<DbInterner<'db>>, + { + self.resolve_completely_with_default(value_ref, value_ref.clone()); + } + + pub(crate) fn resolve_completely_with_default<T>(&mut self, value_ref: &mut T, default: T) + where + T: TypeFoldable<DbInterner<'db>>, + { + let value = std::mem::replace(value_ref, default); + + let value = self.table.resolve_vars_if_possible(value); + + let mut goals = vec![]; + + // FIXME(next-solver): Handle `goals`. + + *value_ref = value.fold_with(&mut Resolver::new(self, true, &mut goals)); + } + + pub(crate) fn resolve_diagnostics(mut self) -> (ThinVec<InferenceDiagnostic>, bool) { + let has_errors = self.has_errors; + + self.table.emit_trait_errors(&mut self.diagnostics); + + // Ignore diagnostics made from resolving diagnostics. + let mut diagnostics = std::mem::take(&mut self.diagnostics); + diagnostics.retain_mut(|diagnostic| { + self.resolve_completely(diagnostic); + + if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } + | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } + | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } + | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic + && ty.as_ref().references_non_lt_error() + { + false + } else { + true + } + }); + diagnostics.shrink_to_fit(); + + (diagnostics, has_errors) + } + } + + struct DiagnoseInferVars<'a, 'db> { + ctx: &'a mut WriteBackCtxt<'db>, + top_term: Term<'db>, + } + + impl<'db> DiagnoseInferVars<'_, 'db> { + const TYPE_FLAGS: TypeFlags = TypeFlags::HAS_INFER.union(TypeFlags::HAS_NON_REGION_ERROR); + + fn err_on_span(&mut self, span: Span) { + if !self.ctx.spans_emitted_type_must_be_known_for.insert(span) { + // Suppress duplicate diagnostics. + return; + } + + if span.is_dummy() { + return; + } + + // We have to be careful not to insert infer vars here, as we won't resolve this new diagnostic. + let top_term = self.top_term.fold_with(&mut ReplaceInferWithError::new(self.cx())); + self.ctx.diagnostics.push(InferenceDiagnostic::TypeMustBeKnown { + at_point: span, + top_term: Some(GenericArg::from(top_term).store()), + }); + } + } + + impl<'db> TypeFolder<DbInterner<'db>> for DiagnoseInferVars<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.ctx.table.interner() + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + if !t.has_type_flags(Self::TYPE_FLAGS) { + return t; + } + + match t.kind() { + TyKind::Error(_) => { + self.ctx.has_errors = true; + t + } + TyKind::Infer(infer_ty) => match infer_ty { + InferTy::TyVar(vid) => { + self.err_on_span(self.ctx.table.infer_ctxt.type_var_span(vid)); + self.ctx.has_errors = true; + self.ctx.types.types.error + } + InferTy::IntVar(_) => { + never!("fallback should have resolved all int vars"); + self.ctx.types.types.i32 + } + InferTy::FloatVar(_) => { + never!("fallback should have resolved all float vars"); + self.ctx.types.types.f64 + } + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => { + never!("should not have fresh infer vars outside of caching"); + self.ctx.has_errors = true; + self.ctx.types.types.error + } + }, + _ => t.super_fold_with(self), + } + } + + fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { + if !c.has_type_flags(Self::TYPE_FLAGS) { + return c; + } + + match c.kind() { + ConstKind::Error(_) => { + self.ctx.has_errors = true; + c + } + ConstKind::Infer(infer_ct) => match infer_ct { + InferConst::Var(vid) => { + if let Some(span) = self.ctx.table.infer_ctxt.const_var_span(vid) { + self.err_on_span(span); + } + self.ctx.has_errors = true; + self.ctx.types.consts.error + } + InferConst::Fresh(_) => { + never!("should not have fresh infer vars outside of caching"); + self.ctx.has_errors = true; + self.ctx.types.consts.error + } + }, + _ => c.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> { + if !p.has_type_flags(Self::TYPE_FLAGS) { + return p; + } + p.super_fold_with(self) + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if r.is_var() { + // For now, we don't error on regions. + self.ctx.types.regions.error + } else { + r + } + } + } + pub(super) struct Resolver<'a, 'db> { - ctx: &'a mut InferenceTable<'db>, + ctx: &'a mut WriteBackCtxt<'db>, /// Whether we should normalize, disabled when resolving predicates. should_normalize: bool, nested_goals: &'a mut Vec<Goal<'db, Predicate<'db>>>, @@ -629,7 +714,7 @@ mod resolve_completely { impl<'a, 'db> Resolver<'a, 'db> { pub(super) fn new( - ctx: &'a mut InferenceTable<'db>, + ctx: &'a mut WriteBackCtxt<'db>, should_normalize: bool, nested_goals: &'a mut Vec<Goal<'db, Predicate<'db>>>, ) -> Resolver<'a, 'db> { @@ -645,8 +730,9 @@ mod resolve_completely { T: Into<Term<'db>> + TypeSuperFoldable<DbInterner<'db>> + Copy, { let value = if self.should_normalize { - let cause = ObligationCause::new(); - let at = self.ctx.at(&cause); + // FIXME: This should not use a dummy span. + let cause = ObligationCause::new(Span::Dummy); + let at = self.ctx.table.at(&cause); let universes = vec![None; outer_exclusive_binder(value).as_usize()]; match deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( at, value, universes, @@ -655,8 +741,8 @@ mod resolve_completely { self.nested_goals.extend(goals); value } - Err(_errors) => { - // FIXME: Report the error. + Err(errors) => { + self.ctx.table.trait_errors.extend(errors); value } } @@ -664,17 +750,17 @@ mod resolve_completely { value }; - value.fold_with(&mut ReplaceInferWithError::new(self.ctx.interner())) + value.fold_with(&mut DiagnoseInferVars { ctx: self.ctx, top_term: value.into() }) } } - impl<'cx, 'db> TypeFolder<DbInterner<'db>> for Resolver<'cx, 'db> { + impl<'db> TypeFolder<DbInterner<'db>> for Resolver<'_, 'db> { fn cx(&self) -> DbInterner<'db> { - self.ctx.interner() + self.ctx.table.interner() } fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if r.is_var() { Region::error(self.ctx.interner()) } else { r } + if r.is_var() { self.ctx.types.regions.error } else { r } } fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs index 74d66123ea..0070d14f37 100644 --- a/crates/hir-ty/src/inhabitedness.rs +++ b/crates/hir-ty/src/inhabitedness.rs @@ -5,10 +5,7 @@ use hir_def::{ AdtId, EnumVariantId, ModuleId, VariantId, signatures::VariantFields, visibility::Visibility, }; use rustc_hash::FxHashSet; -use rustc_type_ir::{ - TypeSuperVisitable, TypeVisitable, TypeVisitor, - inherent::{AdtDef, IntoKind}, -}; +use rustc_type_ir::{TypeSuperVisitable, TypeVisitable, TypeVisitor, inherent::IntoKind}; use crate::{ consteval::try_const_usize, @@ -85,7 +82,7 @@ impl<'db> TypeVisitor<DbInterner<'db>> for UninhabitedFrom<'_, 'db> { } let r = match ty.kind() { - TyKind::Adt(adt, subst) => self.visit_adt(adt.def_id().0, subst), + TyKind::Adt(adt, subst) => self.visit_adt(adt.def_id(), subst), TyKind::Never => BREAK_VISIBLY_UNINHABITED, TyKind::Tuple(..) => ty.super_visit_with(self), TyKind::Array(item_ty, len) => match try_const_usize(self.infcx.interner.db, len) { @@ -172,7 +169,7 @@ impl<'a, 'db> UninhabitedFrom<'a, 'db> { subst: GenericArgs<'db>, ) -> ControlFlow<VisiblyUninhabited> { if vis.is_none_or(|it| it.is_visible_from(self.db(), self.target_mod)) { - let ty = ty.instantiate(self.interner(), subst); + let ty = ty.instantiate(self.interner(), subst).skip_norm_wip(); ty.visit_with(self) } else { CONTINUE_OPAQUELY_INHABITED diff --git a/crates/hir-ty/src/lang_items.rs b/crates/hir-ty/src/lang_items.rs index ae53276f56..19d2d29c9e 100644 --- a/crates/hir-ty/src/lang_items.rs +++ b/crates/hir-ty/src/lang_items.rs @@ -1,64 +1,52 @@ //! Functions to detect special lang items -use hir_def::{ - AdtId, TraitId, - lang_item::LangItems, - signatures::{StructFlags, StructSignature}, -}; -use intern::{Symbol, sym}; - -use crate::db::HirDatabase; - -pub fn is_box(db: &dyn HirDatabase, adt: AdtId) -> bool { - let AdtId::StructId(id) = adt else { return false }; - StructSignature::of(db, id).flags.contains(StructFlags::IS_BOX) -} +use hir_def::{FunctionId, TraitId, lang_item::LangItems}; pub fn lang_items_for_bin_op( lang_items: &LangItems, op: syntax::ast::BinaryOp, -) -> Option<(Symbol, Option<TraitId>)> { +) -> Option<(Option<FunctionId>, Option<TraitId>)> { use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering}; Some(match op { BinaryOp::LogicOp(_) => return None, BinaryOp::ArithOp(aop) => match aop { - ArithOp::Add => (sym::add, lang_items.Add), - ArithOp::Mul => (sym::mul, lang_items.Mul), - ArithOp::Sub => (sym::sub, lang_items.Sub), - ArithOp::Div => (sym::div, lang_items.Div), - ArithOp::Rem => (sym::rem, lang_items.Rem), - ArithOp::Shl => (sym::shl, lang_items.Shl), - ArithOp::Shr => (sym::shr, lang_items.Shr), - ArithOp::BitXor => (sym::bitxor, lang_items.BitXor), - ArithOp::BitOr => (sym::bitor, lang_items.BitOr), - ArithOp::BitAnd => (sym::bitand, lang_items.BitAnd), + ArithOp::Add => (lang_items.Add_add, lang_items.Add), + ArithOp::Mul => (lang_items.Mul_mul, lang_items.Mul), + ArithOp::Sub => (lang_items.Sub_sub, lang_items.Sub), + ArithOp::Div => (lang_items.Div_div, lang_items.Div), + ArithOp::Rem => (lang_items.Rem_rem, lang_items.Rem), + ArithOp::Shl => (lang_items.Shl_shl, lang_items.Shl), + ArithOp::Shr => (lang_items.Shr_shr, lang_items.Shr), + ArithOp::BitXor => (lang_items.BitXor_bitxor, lang_items.BitXor), + ArithOp::BitOr => (lang_items.BitOr_bitor, lang_items.BitOr), + ArithOp::BitAnd => (lang_items.BitAnd_bitand, lang_items.BitAnd), }, BinaryOp::Assignment { op: Some(aop) } => match aop { - ArithOp::Add => (sym::add_assign, lang_items.AddAssign), - ArithOp::Mul => (sym::mul_assign, lang_items.MulAssign), - ArithOp::Sub => (sym::sub_assign, lang_items.SubAssign), - ArithOp::Div => (sym::div_assign, lang_items.DivAssign), - ArithOp::Rem => (sym::rem_assign, lang_items.RemAssign), - ArithOp::Shl => (sym::shl_assign, lang_items.ShlAssign), - ArithOp::Shr => (sym::shr_assign, lang_items.ShrAssign), - ArithOp::BitXor => (sym::bitxor_assign, lang_items.BitXorAssign), - ArithOp::BitOr => (sym::bitor_assign, lang_items.BitOrAssign), - ArithOp::BitAnd => (sym::bitand_assign, lang_items.BitAndAssign), + ArithOp::Add => (lang_items.AddAssign_add_assign, lang_items.AddAssign), + ArithOp::Mul => (lang_items.MulAssign_mul_assign, lang_items.MulAssign), + ArithOp::Sub => (lang_items.SubAssign_sub_assign, lang_items.SubAssign), + ArithOp::Div => (lang_items.DivAssign_div_assign, lang_items.DivAssign), + ArithOp::Rem => (lang_items.RemAssign_rem_assign, lang_items.RemAssign), + ArithOp::Shl => (lang_items.ShlAssign_shl_assign, lang_items.ShlAssign), + ArithOp::Shr => (lang_items.ShrAssign_shr_assign, lang_items.ShrAssign), + ArithOp::BitXor => (lang_items.BitXorAssign_bitxor_assign, lang_items.BitXorAssign), + ArithOp::BitOr => (lang_items.BitOrAssign_bitor_assign, lang_items.BitOrAssign), + ArithOp::BitAnd => (lang_items.BitAndAssign_bitand_assign, lang_items.BitAndAssign), }, BinaryOp::CmpOp(cop) => match cop { - CmpOp::Eq { negated: false } => (sym::eq, lang_items.PartialEq), - CmpOp::Eq { negated: true } => (sym::ne, lang_items.PartialEq), + CmpOp::Eq { negated: false } => (lang_items.PartialEq_eq, lang_items.PartialEq), + CmpOp::Eq { negated: true } => (lang_items.PartialEq_ne, lang_items.PartialEq), CmpOp::Ord { ordering: Ordering::Less, strict: false } => { - (sym::le, lang_items.PartialOrd) + (lang_items.PartialOrd_le, lang_items.PartialOrd) } CmpOp::Ord { ordering: Ordering::Less, strict: true } => { - (sym::lt, lang_items.PartialOrd) + (lang_items.PartialOrd_lt, lang_items.PartialOrd) } CmpOp::Ord { ordering: Ordering::Greater, strict: false } => { - (sym::ge, lang_items.PartialOrd) + (lang_items.PartialOrd_ge, lang_items.PartialOrd) } CmpOp::Ord { ordering: Ordering::Greater, strict: true } => { - (sym::gt, lang_items.PartialOrd) + (lang_items.PartialOrd_gt, lang_items.PartialOrd) } }, BinaryOp::Assignment { op: None } => return None, diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 798c62c192..3e569076ad 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -142,10 +142,10 @@ fn layout_of_simd_ty<'db>( // where T is a primitive scalar (integer/float/pointer). let fields = db.field_types(id.into()); let mut fields = fields.iter(); - let Some(TyKind::Array(e_ty, e_len)) = fields - .next() - .filter(|_| fields.next().is_none()) - .map(|f| (*f.1).get().instantiate(DbInterner::new_no_crate(db), args).kind()) + let Some(TyKind::Array(e_ty, e_len)) = + fields.next().filter(|_| fields.next().is_none()).map(|f| { + (*f.1).get().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip().kind() + }) else { return Err(LayoutError::InvalidSimdType); }; @@ -167,7 +167,7 @@ pub fn layout_of_ty_query( let Ok(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); }; - let dl = &*target; + let dl = target; let cx = LayoutCx::new(dl); let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis); let cause = ObligationCause::dummy(); @@ -177,7 +177,7 @@ pub fn layout_of_ty_query( .unwrap_or(ty.as_ref()); let result = match ty.kind() { TyKind::Adt(def, args) => { - match def.inner().id { + match def.def_id() { hir_def::AdtId::StructId(s) => { let repr = AttrFlags::repr(db, s.into()).unwrap_or_default(); if repr.simd() { @@ -187,13 +187,13 @@ pub fn layout_of_ty_query( repr.packed(), &args, trait_env.as_ref(), - &target, + target, ); } } _ => {} } - return db.layout_of_adt(def.inner().id, args.store(), trait_env); + return db.layout_of_adt(def.def_id(), args.store(), trait_env); } TyKind::Bool => Layout::scalar( dl, @@ -374,7 +374,7 @@ pub(crate) fn layout_of_ty_cycle_result( fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> { match pointee.kind() { TyKind::Adt(def, args) => { - let struct_id = match def.inner().id { + let struct_id = match def.def_id() { AdtId::StructId(id) => id, _ => return pointee, }; @@ -405,7 +405,7 @@ fn field_ty<'a>( fd: LocalFieldId, args: GenericArgs<'a>, ) -> Ty<'a> { - db.field_types(def)[fd].get().instantiate(DbInterner::new_no_crate(db), args) + db.field_types(def)[fd].get().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip() } fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 6090ddfd45..b7e1697059 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs @@ -1,6 +1,6 @@ //! Compute the binary representation of structs, unions and enums -use std::{cmp, ops::Bound}; +use std::cmp; use hir_def::{ AdtId, VariantId, @@ -29,7 +29,7 @@ pub fn layout_of_adt_query( let Ok(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); }; - let dl = &*target; + let dl = target; let cx = LayoutCx::new(dl); let handle_variant = |def: VariantId, var: &VariantFields| { var.fields() @@ -79,7 +79,6 @@ pub fn layout_of_adt_query( &variants, matches!(def, AdtId::EnumId(..)), is_special_no_niche, - layout_scalar_valid_range(db, def), |min, max| repr_discr(dl, &repr, min, max).unwrap_or((Integer::I8, false)), variants.iter_enumerated().filter_map(|(id, _)| { let AdtId::EnumId(e) = def else { return None }; @@ -107,15 +106,6 @@ pub(crate) fn layout_of_adt_cycle_result( Err(LayoutError::RecursiveTypeWithoutIndirection) } -fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) { - let range = AttrFlags::rustc_layout_scalar_valid_range(db, def); - let get = |value| match value { - Some(it) => Bound::Included(it), - None => Bound::Unbounded, - }; - (get(range.start), get(range.end)) -} - /// Finds the appropriate Integer type and signedness for the given /// signed discriminant range and `#[repr]` attribute. /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but diff --git a/crates/hir-ty/src/layout/target.rs b/crates/hir-ty/src/layout/target.rs index 1752b56b0f..26fa73e76b 100644 --- a/crates/hir-ty/src/layout/target.rs +++ b/crates/hir-ty/src/layout/target.rs @@ -3,17 +3,17 @@ use base_db::{Crate, target::TargetLoadError}; use hir_def::layout::TargetDataLayout; use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutError}; -use triomphe::Arc; use crate::db::HirDatabase; +#[salsa_macros::tracked(returns(ref))] pub fn target_data_layout_query( db: &dyn HirDatabase, krate: Crate, -) -> Result<Arc<TargetDataLayout>, TargetLoadError> { +) -> Result<TargetDataLayout, TargetLoadError> { match &krate.workspace_data(db).target { Ok(target) => match TargetDataLayout::parse_from_llvm_datalayout_string(&target.data_layout, AddressSpace::ZERO) { - Ok(it) => Ok(Arc::new(it)), + Ok(it) => Ok(it), Err(e) => { Err(match e { TargetDataLayoutError::InvalidAddressSpace { addr_space, cause, err } => { diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index 484ecebba5..bc18f05790 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -90,7 +90,7 @@ fn eval_goal( adt_id, GenericArgs::identity_for_item(interner, adt_id.into()), ), - Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity(), + Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity().skip_norm_wip(), }; let param_env = db.trait_environment( match adt_or_type_alias_id { @@ -529,6 +529,7 @@ fn tuple_ptr_with_dst_tail() { } #[test] +#[ignore = "FIXME: We need to have proper pattern types"] fn non_zero_and_non_null() { size_and_align! { minicore: non_zero, non_null, option; @@ -565,8 +566,6 @@ fn const_eval_simple() { } #[test] -// FIXME -#[should_panic] fn const_eval_complex() { size_and_align! { struct Goal([i32; 2 + 2]); diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index d004b5e3ef..91e3b85aa1 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -26,6 +26,7 @@ extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; extern crate self as hir_ty; pub mod builtin_derive; +mod generics; mod infer; mod inhabitedness; mod lower; @@ -44,12 +45,12 @@ pub mod diagnostics; pub mod display; pub mod drop; pub mod dyn_compatibility; -pub mod generics; pub mod lang_items; pub mod layout; pub mod method_resolution; pub mod mir; pub mod primitive; +pub mod solver_errors; pub mod traits; pub mod upvars; @@ -61,42 +62,55 @@ mod tests; use std::{hash::Hash, ops::ControlFlow}; use hir_def::{ - CallableDefId, ExpressionStoreOwnerId, GenericDefId, TypeAliasId, TypeOrConstParamId, - TypeParamId, resolver::TypeNs, type_ref::Rawness, + CallableDefId, ConstId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, FunctionId, + GenericDefId, HasModule, LifetimeParamId, ModuleId, StaticId, TypeAliasId, TypeOrConstParamId, + TypeParamId, + db::DefDatabase, + expr_store::{Body, ExpressionStore}, + hir::{BindingId, ExprId, ExprOrPatId, PatId}, + resolver::{HasResolver, Resolver, TypeNs}, + type_ref::{Rawness, TypeRefId}, }; use hir_expand::name::Name; use indexmap::{IndexMap, map::Entry}; -use intern::{Symbol, sym}; use macros::GenericTypeVisitable; use mir::{MirEvalError, VTableMap}; +use rustc_abi::ExternAbi; use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use rustc_type_ir::{ - BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, UpcastFrom, + BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, inherent::{IntoKind, Ty as _}, }; +use stdx::impl_from; use syntax::ast::{ConstArg, make}; use traits::FnTrait; use crate::{ - db::HirDatabase, - display::{DisplayTarget, HirDisplay}, - infer::unify::InferenceTable, + db::{AnonConstId, HirDatabase}, + display::HirDisplay, lower::SupertraitsInfo, next_solver::{ AliasTy, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical, - CanonicalVarKind, CanonicalVarKinds, ClauseKind, Const, ConstKind, DbInterner, FnSig, - GenericArgs, PolyFnSig, Predicate, Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi, + CanonicalVarKind, CanonicalVarKinds, ClauseKind, Const, ConstKind, DbInterner, GenericArgs, + PolyFnSig, Region, RegionKind, TraitRef, Ty, TyKind, TypingMode, + abi::Safety, + infer::{ + DbInternerInferExt, + traits::{Obligation, ObligationCause}, + }, + obligation_ctxt::ObligationCtxt, }, }; pub use autoderef::autoderef; pub use infer::{ - Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, + Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, cast::CastError, could_coerce, could_unify, could_unify_deeply, infer_query_with_inspect, }; pub use lower::{ - GenericPredicates, ImplTraits, LifetimeElisionKind, TyDefId, TyLoweringContext, ValueTyDefId, + GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, LifetimeElisionKind, + TyDefId, TyLoweringContext, TyLoweringInferVarsCtx, TyLoweringResult, ValueTyDefId, diagnostics::*, }; pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db}; @@ -201,139 +215,12 @@ impl<'db> MemoryMap<'db> { } /// 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 type_or_const_param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> u32 { generics::generics(db, id.parent).type_or_const_param_idx(id) } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum FnAbi { - Aapcs, - AapcsUnwind, - AvrInterrupt, - AvrNonBlockingInterrupt, - C, - CCmseNonsecureCall, - CCmseNonsecureEntry, - CDecl, - CDeclUnwind, - CUnwind, - Efiapi, - Fastcall, - FastcallUnwind, - Msp430Interrupt, - PtxKernel, - RiscvInterruptM, - RiscvInterruptS, - Rust, - RustCall, - RustCold, - RustIntrinsic, - Stdcall, - StdcallUnwind, - System, - SystemUnwind, - Sysv64, - Sysv64Unwind, - Thiscall, - ThiscallUnwind, - Unadjusted, - Vectorcall, - VectorcallUnwind, - Wasm, - Win64, - Win64Unwind, - X86Interrupt, - RustPreserveNone, - Unknown, -} - -impl FnAbi { - #[rustfmt::skip] - pub fn from_symbol(s: &Symbol) -> FnAbi { - match s { - s if *s == sym::aapcs_dash_unwind => FnAbi::AapcsUnwind, - s if *s == sym::aapcs => FnAbi::Aapcs, - s if *s == sym::avr_dash_interrupt => FnAbi::AvrInterrupt, - s if *s == sym::avr_dash_non_dash_blocking_dash_interrupt => FnAbi::AvrNonBlockingInterrupt, - s if *s == sym::C_dash_cmse_dash_nonsecure_dash_call => FnAbi::CCmseNonsecureCall, - s if *s == sym::C_dash_cmse_dash_nonsecure_dash_entry => FnAbi::CCmseNonsecureEntry, - s if *s == sym::C_dash_unwind => FnAbi::CUnwind, - s if *s == sym::C => FnAbi::C, - s if *s == sym::cdecl_dash_unwind => FnAbi::CDeclUnwind, - s if *s == sym::cdecl => FnAbi::CDecl, - s if *s == sym::efiapi => FnAbi::Efiapi, - s if *s == sym::fastcall_dash_unwind => FnAbi::FastcallUnwind, - s if *s == sym::fastcall => FnAbi::Fastcall, - s if *s == sym::msp430_dash_interrupt => FnAbi::Msp430Interrupt, - s if *s == sym::ptx_dash_kernel => FnAbi::PtxKernel, - s if *s == sym::riscv_dash_interrupt_dash_m => FnAbi::RiscvInterruptM, - s if *s == sym::riscv_dash_interrupt_dash_s => FnAbi::RiscvInterruptS, - s if *s == sym::rust_dash_call => FnAbi::RustCall, - s if *s == sym::rust_dash_cold => FnAbi::RustCold, - s if *s == sym::rust_dash_preserve_dash_none => FnAbi::RustPreserveNone, - s if *s == sym::rust_dash_intrinsic => FnAbi::RustIntrinsic, - s if *s == sym::Rust => FnAbi::Rust, - s if *s == sym::stdcall_dash_unwind => FnAbi::StdcallUnwind, - s if *s == sym::stdcall => FnAbi::Stdcall, - s if *s == sym::system_dash_unwind => FnAbi::SystemUnwind, - s if *s == sym::system => FnAbi::System, - s if *s == sym::sysv64_dash_unwind => FnAbi::Sysv64Unwind, - s if *s == sym::sysv64 => FnAbi::Sysv64, - s if *s == sym::thiscall_dash_unwind => FnAbi::ThiscallUnwind, - s if *s == sym::thiscall => FnAbi::Thiscall, - s if *s == sym::unadjusted => FnAbi::Unadjusted, - s if *s == sym::vectorcall_dash_unwind => FnAbi::VectorcallUnwind, - s if *s == sym::vectorcall => FnAbi::Vectorcall, - s if *s == sym::wasm => FnAbi::Wasm, - s if *s == sym::win64_dash_unwind => FnAbi::Win64Unwind, - s if *s == sym::win64 => FnAbi::Win64, - s if *s == sym::x86_dash_interrupt => FnAbi::X86Interrupt, - _ => FnAbi::Unknown, - } - } - - pub fn as_str(self) -> &'static str { - match self { - FnAbi::Aapcs => "aapcs", - FnAbi::AapcsUnwind => "aapcs-unwind", - FnAbi::AvrInterrupt => "avr-interrupt", - FnAbi::AvrNonBlockingInterrupt => "avr-non-blocking-interrupt", - FnAbi::C => "C", - FnAbi::CCmseNonsecureCall => "C-cmse-nonsecure-call", - FnAbi::CCmseNonsecureEntry => "C-cmse-nonsecure-entry", - FnAbi::CDecl => "C-decl", - FnAbi::CDeclUnwind => "cdecl-unwind", - FnAbi::CUnwind => "C-unwind", - FnAbi::Efiapi => "efiapi", - FnAbi::Fastcall => "fastcall", - FnAbi::FastcallUnwind => "fastcall-unwind", - FnAbi::Msp430Interrupt => "msp430-interrupt", - FnAbi::PtxKernel => "ptx-kernel", - FnAbi::RiscvInterruptM => "riscv-interrupt-m", - FnAbi::RiscvInterruptS => "riscv-interrupt-s", - FnAbi::Rust => "Rust", - FnAbi::RustCall => "rust-call", - FnAbi::RustCold => "rust-cold", - FnAbi::RustPreserveNone => "rust-preserve-none", - FnAbi::RustIntrinsic => "rust-intrinsic", - FnAbi::Stdcall => "stdcall", - FnAbi::StdcallUnwind => "stdcall-unwind", - FnAbi::System => "system", - FnAbi::SystemUnwind => "system-unwind", - FnAbi::Sysv64 => "sysv64", - FnAbi::Sysv64Unwind => "sysv64-unwind", - FnAbi::Thiscall => "thiscall", - FnAbi::ThiscallUnwind => "thiscall-unwind", - FnAbi::Unadjusted => "unadjusted", - FnAbi::Vectorcall => "vectorcall", - FnAbi::VectorcallUnwind => "vectorcall-unwind", - FnAbi::Wasm => "wasm", - FnAbi::Win64 => "win64", - FnAbi::Win64Unwind => "win64-unwind", - FnAbi::X86Interrupt => "x86-interrupt", - FnAbi::Unknown => "unknown-abi", - } - } +pub fn lifetime_param_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> u32 { + generics::generics(db, id.parent).lifetime_param_idx(id) } #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] @@ -495,7 +382,7 @@ pub fn associated_type_shorthand_candidates( }; let mut dedup_map = FxHashSet::default(); - let param_ty = Ty::new_param(interner, param, param_idx(db, param.into()).unwrap() as u32); + let param_ty = Ty::new_param(interner, param, type_or_const_param_idx(db, param.into())); // We use the ParamEnv and not the predicates because the ParamEnv elaborates bounds. let param_env = db.trait_environment(ExpressionStoreOwnerId::from(def)); for clause in param_env.clauses { @@ -525,68 +412,61 @@ pub fn associated_type_shorthand_candidates( /// To be used from `hir` only. pub fn callable_sig_from_fn_trait<'db>( self_ty: Ty<'db>, - trait_env: ParamEnvAndCrate<'db>, + param_env: ParamEnvAndCrate<'db>, db: &'db dyn HirDatabase, ) -> Option<(FnTrait, PolyFnSig<'db>)> { - let mut table = InferenceTable::new(db, trait_env.param_env, trait_env.krate, None); - let lang_items = table.interner().lang_items(); - - let fn_once_trait = FnTrait::FnOnce.get_id(lang_items)?; - let output_assoc_type = fn_once_trait - .trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; - - // Register two obligations: - // - Self: FnOnce<?args_ty> - // - <Self as FnOnce<?args_ty>>::Output == ?ret_ty - let args_ty = table.next_ty_var(); - let args = GenericArgs::new_from_slice(&[self_ty.into(), args_ty.into()]); - let trait_ref = TraitRef::new_from_args(table.interner(), fn_once_trait.into(), args); - let projection = Ty::new_alias( - table.interner(), - AliasTy::new_from_args( - table.interner(), - rustc_type_ir::Projection { def_id: output_assoc_type.into() }, - args, - ), - ); + let ParamEnvAndCrate { param_env, krate } = param_env; + let interner = DbInterner::new_with(db, krate); + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + let lang_items = interner.lang_items(); + let cause = ObligationCause::dummy(); + + let impls_trait = |trait_: FnTrait| { + let mut ocx = ObligationCtxt::new(&infcx); + let tupled_args = infcx.next_ty_var(Span::Dummy); + let args = GenericArgs::new_from_slice(&[self_ty.into(), tupled_args.into()]); + let trait_id = trait_.get_id(lang_items)?; + let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); + let obligation = Obligation::new(interner, cause, param_env, trait_ref); + ocx.register_obligation(obligation); + if !ocx.try_evaluate_obligations().is_empty() { + return None; + } + let tupled_args = + infcx.resolve_vars_if_possible(tupled_args).replace_infer_with_error(interner); + if tupled_args.is_tuple() { Some(tupled_args) } else { None } + }; - let pred = Predicate::upcast_from(trait_ref, table.interner()); - if !table.try_obligation(pred).no_solution() { - table.register_obligation(pred); - let return_ty = table.normalize_alias_ty(projection); - for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { - let fn_x_trait = fn_x.get_id(lang_items)?; - let trait_ref = TraitRef::new_from_args(table.interner(), fn_x_trait.into(), args); - if !table - .try_obligation(Predicate::upcast_from(trait_ref, table.interner())) - .no_solution() - { - let ret_ty = table.resolve_completely(return_ty); - let args_ty = table.resolve_completely(args_ty); - let TyKind::Tuple(params) = args_ty.kind() else { - return None; - }; - let inputs_and_output = Tys::new_from_iter( - table.interner(), - params.iter().chain(std::iter::once(ret_ty)), - ); - - return Some(( - fn_x, - Binder::dummy(FnSig { - inputs_and_output, - c_variadic: false, - safety: abi::Safety::Safe, - abi: FnAbi::RustCall, - }), - )); + let (trait_, args) = 'find_trait: { + for trait_ in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { + if let Some(args) = impls_trait(trait_) { + break 'find_trait (trait_, args); } } - unreachable!("It should at least implement FnOnce at this point"); - } else { - None - } + return None; + }; + + let output_assoc_type = lang_items.FnOnceOutput?; + let output_projection = Ty::new_alias( + interner, + AliasTy::new( + interner, + rustc_type_ir::Projection { def_id: output_assoc_type.into() }, + [self_ty, args], + ), + ); + let mut ocx = ObligationCtxt::new(&infcx); + let ret = ocx.structurally_normalize_ty(&cause, param_env, output_projection).ok()?; + let ret = ret.replace_infer_with_error(interner); + + let sig = Binder::dummy(interner.mk_fn_sig( + args.tuple_fields(), + ret, + false, + Safety::Safe, + ExternAbi::Rust, + )); + Some((trait_, sig)) } struct ParamCollector { @@ -623,58 +503,128 @@ where Vec::from_iter(collector.params) } -struct TypeInferenceVarCollector<'db> { - type_inference_vars: Vec<Ty<'db>>, +pub fn known_const_to_ast<'db>( + konst: Const<'db>, + db: &'db dyn HirDatabase, + target_module: ModuleId, +) -> Option<ConstArg> { + Some(make::expr_const_value( + &konst.display_source_code(db, target_module, true).unwrap_or_else(|_| "_".to_owned()), + )) } -impl<'db> rustc_type_ir::TypeVisitor<DbInterner<'db>> for TypeInferenceVarCollector<'db> { - type Result = (); +/// A `Span` represents some location in lowered code - a type, expression or pattern. +/// +/// It has no meaning outside its body therefore it should not exit the pass it was created in +/// (e.g. inference). It is usually associated with a solver obligation or an infer var, which +/// should also not cross the pass they were created in. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Span { + ExprId(ExprId), + PatId(PatId), + BindingId(BindingId), + TypeRefId(TypeRefId), + /// An unimportant location. Errors on this will be suppressed. + Dummy, +} +impl_from!(ExprId, PatId, BindingId, TypeRefId for Span); - fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { - use crate::rustc_type_ir::Flags; - if ty.is_ty_var() { - self.type_inference_vars.push(ty); - } else if ty.flags().intersects(rustc_type_ir::TypeFlags::HAS_TY_INFER) { - ty.super_visit_with(self); - } else { - // Fast path: don't visit inner types (e.g. generic arguments) when `flags` indicate - // that there are no placeholders. +impl From<ExprOrPatId> for Span { + fn from(value: ExprOrPatId) -> Self { + match value { + ExprOrPatId::ExprId(idx) => idx.into(), + ExprOrPatId::PatId(idx) => idx.into(), } } } -pub fn collect_type_inference_vars<'db, T>(value: &T) -> Vec<Ty<'db>> -where - T: ?Sized + rustc_type_ir::TypeVisitable<DbInterner<'db>>, -{ - let mut collector = TypeInferenceVarCollector { type_inference_vars: vec![] }; - value.visit_with(&mut collector); - collector.type_inference_vars +impl Span { + pub(crate) fn pick_best(a: Span, b: Span) -> Span { + // We prefer dummy spans to minimize the risk of false errors. + if b.is_dummy() { b } else { a } + } + + #[inline] + pub fn is_dummy(&self) -> bool { + matches!(self, Self::Dummy) + } } -pub fn known_const_to_ast<'db>( - konst: Const<'db>, - db: &'db dyn HirDatabase, - display_target: DisplayTarget, -) -> Option<ConstArg> { - Some(make::expr_const_value(konst.display(db, display_target).to_string().as_str())) +/// A [`DefWithBodyId`], or an anon const. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Supertype)] +pub enum InferBodyId { + DefWithBodyId(DefWithBodyId), + AnonConstId(AnonConstId), +} +impl_from!(DefWithBodyId(FunctionId, ConstId, StaticId), AnonConstId for InferBodyId); +impl From<EnumVariantId> for InferBodyId { + fn from(id: EnumVariantId) -> Self { + InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(id)) + } } -#[derive(Debug, Copy, Clone)] -pub(crate) enum DeclOrigin { - LetExpr, - /// from `let x = ..` - LocalDecl { - has_else: bool, - }, +impl HasModule for InferBodyId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { + match self { + InferBodyId::DefWithBodyId(id) => id.module(db), + InferBodyId::AnonConstId(id) => id.module(db), + } + } } -/// Provides context for checking patterns in declarations. More specifically this -/// allows us to infer array types if the pattern is irrefutable and allows us to infer -/// the size of the array. See issue rust-lang/rust#76342. -#[derive(Debug, Copy, Clone)] -pub(crate) struct DeclContext { - pub(crate) origin: DeclOrigin, +impl HasResolver for InferBodyId { + fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { + match self { + InferBodyId::DefWithBodyId(id) => id.resolver(db), + InferBodyId::AnonConstId(id) => id.resolver(db), + } + } +} + +impl InferBodyId { + pub fn expression_store_owner(self, db: &dyn HirDatabase) -> ExpressionStoreOwnerId { + match self { + InferBodyId::DefWithBodyId(id) => id.into(), + InferBodyId::AnonConstId(id) => id.loc(db).owner, + } + } + + pub fn generic_def(self, db: &dyn HirDatabase) -> GenericDefId { + match self { + InferBodyId::DefWithBodyId(id) => id.generic_def(db), + InferBodyId::AnonConstId(id) => id.loc(db).owner.generic_def(db), + } + } + + #[inline] + pub fn as_function(self) -> Option<FunctionId> { + match self { + InferBodyId::DefWithBodyId(DefWithBodyId::FunctionId(it)) => Some(it), + _ => None, + } + } + + #[inline] + pub fn as_variant(self) -> Option<EnumVariantId> { + match self { + InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(it)) => Some(it), + _ => None, + } + } + + pub fn store_and_root_expr(self, db: &dyn HirDatabase) -> (&ExpressionStore, ExprId) { + match self { + InferBodyId::DefWithBodyId(id) => { + let body = Body::of(db, id); + (body, body.root_expr()) + } + InferBodyId::AnonConstId(id) => { + let loc = id.loc(db); + let store = ExpressionStore::of(db, loc.owner); + (store, loc.expr) + } + } + } } pub fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> { diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 335aff2c1d..5b0bcd2be8 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -8,24 +8,23 @@ pub(crate) mod diagnostics; pub(crate) mod path; -use std::{cell::OnceCell, iter, mem}; +use std::{cell::OnceCell, iter, mem, sync::OnceLock}; -use arrayvec::ArrayVec; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, EnumId, EnumVariantId, - ExpressionStoreOwnerId, FunctionId, GeneralConstId, GenericDefId, GenericParamId, HasModule, - ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, StaticId, StructId, TraitId, + ExpressionStoreOwnerId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, + ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, builtin_type::BuiltinType, - expr_store::{ExpressionStore, HygieneId, path::Path}, + expr_store::{ExpressionStore, path::Path}, hir::generics::{ - GenericParamDataRef, GenericParams, TypeOrConstParamData, TypeParamProvenance, - WherePredicate, + GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, + TypeParamProvenance, WherePredicate, }, item_tree::FieldsShape, lang_item::LangItems, - resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs}, + resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, signatures::{ ConstSignature, FunctionSignature, ImplSignature, StaticSignature, StructSignature, TraitFlags, TraitSignature, TypeAliasFlags, TypeAliasSignature, @@ -38,30 +37,32 @@ use hir_def::{ use hir_expand::name::Name; use la_arena::{Arena, ArenaMap, Idx}; use path::{PathDiagnosticCallback, PathLoweringContext}; +use rustc_abi::ExternAbi; use rustc_ast_ir::Mutability; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, BoundVarIndexKind, ConstKind, DebruijnIndex, ExistentialPredicate, - ExistentialProjection, ExistentialTraitRef, FnSig, Interner, OutlivesPredicate, TermKind, - TyKind, TypeFoldable, TypeVisitableExt, Upcast, UpcastFrom, elaborate, + AliasTyKind, BoundVarIndexKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, + ExistentialTraitRef, FnSig, Interner, OutlivesPredicate, TermKind, TyKind, TypeFoldable, + TypeVisitableExt, Upcast, UpcastFrom, elaborate, inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _}, }; use smallvec::SmallVec; use stdx::{impl_from, never}; +use thin_vec::ThinVec; use tracing::debug; -use triomphe::{Arc, ThinArc}; use crate::{ - FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, - consteval::intern_const_ref, - db::{HirDatabase, InternedOpaqueTyId}, - generics::{Generics, generics}, + ImplTraitId, Span, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + consteval::{create_anon_const, path_to_const}, + db::{AnonConstId, GeneralConstId, HirDatabase, InternedOpaqueTyId}, + generics::{Generics, SingleGenerics, generics}, + infer::unify::InferenceTable, next_solver::{ - AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, - DbInterner, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FxIndexMap, GenericArg, - GenericArgs, ParamConst, ParamEnv, PolyFnSig, Predicate, Region, SolverDefId, + AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, ConstKind, + DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind, + FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Predicate, Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, - StoredTy, TraitPredicate, TraitRef, Ty, Tys, UnevaluatedConst, abi::Safety, + StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, util::BottomUpFolder, }, }; @@ -172,7 +173,29 @@ pub(crate) enum GenericPredicateSource { AssocTyBound, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ForbidParamsAfterReason { + /// When lowering generic param defaults, you cannot refer to any param after + /// the currently lowered param, including the current param. + LoweringParamDefault, + /// Most anon const (except array repeat expressions) cannot refer to any generic + /// param. + AnonConst, + /// The type of a const param cannot refer to a type param. + ConstParamTy, +} + +pub trait TyLoweringInferVarsCtx<'db> { + fn next_ty_var(&mut self, span: Span) -> Ty<'db>; + fn next_const_var(&mut self, span: Span) -> Const<'db>; + fn next_region_var(&mut self, span: Span) -> Region<'db>; + + #[expect(private_interfaces)] + fn as_table(&mut self) -> Option<&mut InferenceTable<'db>> { + None + } +} + pub struct TyLoweringContext<'db, 'a> { pub db: &'db dyn HirDatabase, interner: DbInterner<'db>, @@ -180,17 +203,19 @@ pub struct TyLoweringContext<'db, 'a> { lang_items: &'db LangItems, resolver: &'a Resolver<'db>, store: &'a ExpressionStore, - def: GenericDefId, - generics: OnceCell<Generics<'db>>, + def: ExpressionStoreOwnerId, + generic_def: GenericDefId, + generics: &'a OnceCell<Generics<'db>>, in_binders: DebruijnIndex, impl_trait_mode: ImplTraitLoweringState, /// Tracks types with explicit `?Sized` bounds. pub(crate) unsized_types: FxHashSet<Ty<'db>>, - pub(crate) diagnostics: Vec<TyLoweringDiagnostic>, + pub(crate) diagnostics: ThinVec<TyLoweringDiagnostic>, lifetime_elision: LifetimeElisionKind<'db>, - /// When lowering the defaults for generic params, this contains the index of the currently lowered param. - /// We disallow referring to later params, or to ADT's `Self`. - lowering_param_default: Option<u32>, + forbid_params_after: Option<u32>, + forbid_params_after_reason: ForbidParamsAfterReason, + pub(crate) defined_anon_consts: ThinVec<AnonConstId>, + infer_vars: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>, } impl<'db, 'a> TyLoweringContext<'db, 'a> { @@ -198,7 +223,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { db: &'db dyn HirDatabase, resolver: &'a Resolver<'db>, store: &'a ExpressionStore, - def: GenericDefId, + def: ExpressionStoreOwnerId, + generic_def: GenericDefId, + generics: &'a OnceCell<Generics<'db>>, lifetime_elision: LifetimeElisionKind<'db>, ) -> Self { let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); @@ -212,14 +239,18 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { lang_items: interner.lang_items(), resolver, def, - generics: Default::default(), + generic_def, + generics, store, in_binders, impl_trait_mode, unsized_types: FxHashSet::default(), - diagnostics: Vec::new(), + diagnostics: ThinVec::new(), lifetime_elision, - lowering_param_default: None, + forbid_params_after: None, + forbid_params_after_reason: ForbidParamsAfterReason::AnonConst, + defined_anon_consts: ThinVec::new(), + infer_vars: None, } } @@ -255,13 +286,57 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self } - pub(crate) fn lowering_param_default(&mut self, index: u32) { - self.lowering_param_default = Some(index); + pub(crate) fn forbid_params_after(&mut self, index: u32, reason: ForbidParamsAfterReason) { + self.forbid_params_after = Some(index); + self.forbid_params_after_reason = reason; + } + + pub fn with_infer_vars_behavior( + mut self, + behavior: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>, + ) -> Self { + self.infer_vars = behavior; + self } pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) { self.diagnostics.push(TyLoweringDiagnostic { source: type_ref, kind }); } + + #[track_caller] + pub(crate) fn expect_table(&mut self) -> &mut InferenceTable<'db> { + self.infer_vars.as_mut().unwrap().as_table().unwrap() + } + + fn next_ty_var(&mut self, span: Span) -> Ty<'db> { + match &mut self.infer_vars { + Some(infer_vars) => infer_vars.next_ty_var(span), + None => { + // FIXME: Emit an error: no infer vars allowed here. + self.types.types.error + } + } + } + + fn next_const_var(&mut self, span: Span) -> Const<'db> { + match &mut self.infer_vars { + Some(infer_vars) => infer_vars.next_const_var(span), + None => { + // FIXME: Emit an error: no infer vars allowed here. + self.types.consts.error + } + } + } + + fn next_region_var(&mut self, span: Span) -> Region<'db> { + match &mut self.infer_vars { + Some(infer_vars) => infer_vars.next_region_var(span), + None => { + // FIXME: Emit an error: no infer vars allowed here. + self.types.regions.error + } + } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] @@ -282,162 +357,67 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> { - let expr_id = const_ref.expr; - let expr = &self.store[expr_id]; - match expr { - hir_def::hir::Expr::Path(path) => self - .path_to_const(path) - .unwrap_or_else(|| Const::new(self.interner, ConstKind::Error(ErrorGuaranteed))), - hir_def::hir::Expr::Literal(literal) => { - intern_const_ref(self.db, literal, const_type, self.resolver.krate()) - } - hir_def::hir::Expr::UnaryOp { expr: inner_expr, op: hir_def::hir::UnaryOp::Neg } => { - if let hir_def::hir::Expr::Literal(literal) = &self.store[*inner_expr] { - // Only handle negation for signed integers and floats - match literal { - hir_def::hir::Literal::Int(_, _) | hir_def::hir::Literal::Float(_, _) => { - if let Some(negated_literal) = literal.clone().negate() { - intern_const_ref( - self.db, - &negated_literal, - const_type, - self.resolver.krate(), - ) - } else { - Const::new(self.interner, ConstKind::Error(ErrorGuaranteed)) - } - } - // For unsigned integers, chars, bools, etc., negation is not meaningful - _ => Const::new(self.interner, ConstKind::Error(ErrorGuaranteed)), - } - } else { - // Complex negation expression (e.g. `-N` where N is a const param) - self.lower_const_as_unevaluated(expr_id, const_type) - } - } - hir_def::hir::Expr::Underscore => { - Const::new(self.interner, ConstKind::Error(ErrorGuaranteed)) - } - // Any other complex expression becomes an unevaluated anonymous const. - _ => self.lower_const_as_unevaluated(expr_id, const_type), - } - } + #[expect(clippy::manual_map, reason = "a `map()` here generates a borrowck error")] + let create_var = match &mut self.infer_vars { + Some(infer_vars) => Some( + (&mut |span| infer_vars.next_const_var(span)) as &mut dyn FnMut(Span) -> Const<'db>, + ), + None => None, + }; + let konst = create_anon_const( + self.interner, + self.def, + self.store, + const_ref.expr, + self.resolver, + const_type, + &|| self.generics.get_or_init(|| generics(self.db, self.generic_def)), + create_var, + self.forbid_params_after, + ); - /// Lower a complex const expression to an `UnevaluatedConst` backed by an `AnonConstId`. - /// - /// The `expected_ty_ref` is `None` for array lengths (implicitly `usize`) or - /// `Some(type_ref_id)` for const generic arguments where the expected type comes - /// from the const parameter declaration. - fn lower_const_as_unevaluated( - &mut self, - _expr: hir_def::hir::ExprId, - _expected_ty: Ty<'db>, - ) -> Const<'db> { - // /// Build the identity generic args for the current generic context. - // /// - // /// This maps each generic parameter to itself (as a `ParamTy`, `ParamConst`, - // /// or `EarlyParamRegion`), which is the correct substitution when creating - // /// an `UnevaluatedConst` during type lowering — the anon const inherits the - // /// parent's generics and they haven't been substituted yet. - // fn current_generic_args(&self) -> GenericArgs<'db> { - // let generics = self.generics(); - // let interner = self.interner; - // GenericArgs::new_from_iter( - // interner, - // generics.iter_id().enumerate().map(|(index, id)| match id { - // GenericParamId::TypeParamId(id) => { - // GenericArg::from(Ty::new_param(interner, id, index as u32)) - // } - // GenericParamId::ConstParamId(id) => GenericArg::from(Const::new_param( - // interner, - // ParamConst { id, index: index as u32 }, - // )), - // GenericParamId::LifetimeParamId(id) => GenericArg::from(Region::new_early_param( - // interner, - // EarlyParamRegion { id, index: index as u32 }, - // )), - // }), - // ) - // } - // let loc = AnonConstLoc { owner: self.def, expr }; - // let id = loc.intern(self.db); - // let args = self.current_generic_args(); - // Const::new( - // self.interner, - // ConstKind::Unevaluated(UnevaluatedConst::new( - // GeneralConstId::AnonConstId(id).into(), - // args, - // )), - // ) - Const::new(self.interner, ConstKind::Error(ErrorGuaranteed)) - } - - pub(crate) fn path_to_const(&mut self, path: &Path) -> Option<Const<'db>> { - match self.resolver.resolve_path_in_value_ns_fully(self.db, path, HygieneId::ROOT) { - Some(ValueNs::GenericParam(p)) => { - let args = self.generics(); - match args.type_or_const_param_idx(p.into()) { - Some(idx) => Some(self.const_param(p, idx as u32)), - None => { - never!( - "Generic list doesn't contain this param: {:?}, {:?}, {:?}", - args, - path, - p - ); - None - } - } - } - Some(ValueNs::ConstId(c)) => { - let args = GenericArgs::empty(self.interner); - Some(Const::new( - self.interner, - rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( - GeneralConstId::ConstId(c).into(), - args, - )), - )) - } - _ => None, + if let Ok(konst) = konst + && let ConstKind::Unevaluated(konst) = konst.kind() + && let GeneralConstId::AnonConstId(konst) = konst.def.0 + { + self.defined_anon_consts.push(konst); } + + konst.unwrap_or({ + // FIXME: Report an error. + self.types.consts.error + }) } - pub(crate) fn lower_path_as_const(&mut self, path: &Path, const_type: Ty<'db>) -> Const<'db> { - self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type)) + pub(crate) fn lower_path_as_const(&mut self, path: &Path, _const_type: Ty<'db>) -> Const<'db> { + path_to_const(self.db, self.resolver, &|| self.generics(), self.forbid_params_after, path) + .unwrap_or({ + // FIXME: Report an error. + self.types.consts.error + }) } fn generics(&self) -> &Generics<'db> { - self.generics.get_or_init(|| generics(self.db, self.def)) + self.generics.get_or_init(|| generics(self.db, self.generic_def)) } fn param_index_is_disallowed(&self, index: u32) -> bool { - self.lowering_param_default - .is_some_and(|disallow_params_after| index >= disallow_params_after) + self.forbid_params_after.is_some_and(|disallow_params_after| index >= disallow_params_after) } fn type_param(&mut self, id: TypeParamId, index: u32) -> Ty<'db> { if self.param_index_is_disallowed(index) { // FIXME: Report an error. - Ty::new_error(self.interner, ErrorGuaranteed) + self.types.types.error } else { Ty::new_param(self.interner, id, index) } } - fn const_param(&mut self, id: ConstParamId, index: u32) -> Const<'db> { - if self.param_index_is_disallowed(index) { - // FIXME: Report an error. - Const::error(self.interner) - } else { - Const::new_param(self.interner, ParamConst { id, index }) - } - } - fn region_param(&mut self, id: LifetimeParamId, index: u32) -> Region<'db> { if self.param_index_is_disallowed(index) { // FIXME: Report an error. - Region::error(self.interner) + self.types.regions.error } else { Region::new_early_param(self.interner, EarlyParamRegion { id, index }) } @@ -465,9 +445,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { res = Some(TypeNs::GenericParam(type_param_id)); let generics = self.generics(); - let (idx, _data) = - generics.type_or_const_param(type_param_id.into()).expect("matching generics"); - self.type_param(type_param_id, idx as u32) + let idx = generics.type_or_const_param_idx(type_param_id.into()); + self.type_param(type_param_id, idx) } &TypeRef::RawPtr(inner, mutability) => { let inner_ty = self.lower_ty(inner); @@ -475,7 +454,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } TypeRef::Array(array) => { let inner_ty = self.lower_ty(array.ty); - let const_len = self.lower_const(array.len, Ty::new_usize(interner)); + let const_len = self.lower_const(array.len, self.types.types.usize); Ty::new_array_with_const_len(interner, inner_ty, const_len) } &TypeRef::Slice(inner) => { @@ -485,12 +464,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { TypeRef::Reference(ref_) => { let inner_ty = self.lower_ty(ref_.ty); // FIXME: It should infer the eldided lifetimes instead of stubbing with error - let lifetime = ref_ - .lifetime - .map_or_else(|| Region::error(interner), |lr| self.lower_lifetime(lr)); + let lifetime = + ref_.lifetime.map_or(self.types.regions.error, |lr| self.lower_lifetime(lr)); Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability)) } - TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed), + TypeRef::Placeholder => self.next_ty_var(type_ref_id.into()), TypeRef::Fn(fn_) => self.lower_fn_ptr(fn_), TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), TypeRef::ImplTrait(bounds) => { @@ -516,8 +494,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { |f| ImplTraitId::ReturnTypeImplTrait(f, idx), |a| ImplTraitId::TypeAliasImplTrait(a, idx), ); - let opaque_ty_id: SolverDefId = - self.db.intern_impl_trait_id(impl_trait_id).into(); + let opaque_ty_id = InternedOpaqueTyId::new(self.db, impl_trait_id); // We don't want to lower the bounds inside the binders // we're currently in, because they don't end up inside @@ -534,23 +511,24 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { }); self.impl_trait_mode.opaque_type_data[idx] = actual_opaque_type_data; - let args = GenericArgs::identity_for_item(self.interner, opaque_ty_id); + let args = + GenericArgs::identity_for_item(self.interner, opaque_ty_id.into()); Ty::new_alias( self.interner, AliasTy::new_from_args( self.interner, - AliasTyKind::Opaque { def_id: opaque_ty_id }, + AliasTyKind::Opaque { def_id: opaque_ty_id.into() }, args, ), ) } ImplTraitLoweringMode::Disallowed => { // FIXME: report error - Ty::new_error(self.interner, ErrorGuaranteed) + self.types.types.error } } } - TypeRef::Error => Ty::new_error(self.interner, ErrorGuaranteed), + TypeRef::Error => self.types.types.error, }; (ty, res) } @@ -571,9 +549,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { Ty::new_fn_ptr( interner, Binder::dummy(FnSig { - abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), - safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, - c_variadic: fn_.is_varargs, + fn_sig_kind: FnSigKind::new( + fn_.abi, + if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, + fn_.is_varargs, + ), inputs_and_output: Tys::new_from_slice(&args), }), ) @@ -630,13 +610,13 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { if let Some(type_ref) = path.type_anchor() { let (ty, res) = self.lower_ty_ext(type_ref); let mut ctx = self.at_path(path_id); - return ctx.lower_ty_relative_path(ty, res, false); + return ctx.lower_ty_relative_path(ty, res, false, path_id.type_ref().into()); } let mut ctx = self.at_path(path_id); let (resolution, remaining_index) = match ctx.resolve_path_in_type_ns() { Some(it) => it, - None => return (Ty::new_error(self.interner, ErrorGuaranteed), None), + None => return (self.types.types.error, None), }; if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { @@ -646,7 +626,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { return (ty, None); } - ctx.lower_partly_resolved_path(resolution, false) + ctx.lower_partly_resolved_path(resolution, false, path_id.type_ref().into()) } fn lower_trait_ref_from_path( @@ -660,7 +640,15 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { TypeNs::TraitId(tr) => tr, _ => return None, }; - Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty, false), ctx)) + Some(( + ctx.lower_trait_ref_from_resolved_path( + resolved, + explicit_self_ty, + false, + path_id.type_ref().into(), + ), + ctx, + )) } fn lower_trait_ref( @@ -722,7 +710,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ctx.ty_ctx().unsized_types.insert(self_ty); } else { if !ignore_bindings { - assoc_bounds = ctx.assoc_type_bindings_from_type_bound(trait_ref); + assoc_bounds = ctx.assoc_type_bindings_from_type_bound( + trait_ref, + path.type_ref().into(), + ); } clause = Some(Clause(Predicate::new( interner, @@ -769,7 +760,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> { let interner = self.interner; - let dummy_self_ty = dyn_trait_dummy_self(interner); + let dummy_self_ty = self.types.types.dyn_trait_dummy_self; let mut region = None; // INVARIANT: The principal trait bound, if present, must come first. Others may be in any // order but should be in the same order for the same set but possibly different order of @@ -846,7 +837,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let mut projection_bounds = FxIndexMap::default(); for proj in projections { let key = ( - proj.skip_binder().def_id().expect_type_alias(), + proj.skip_binder().def_id().0, interner.anonymize_bound_vars( proj.map_bound(|proj| proj.projection_term.trait_ref(interner)), ), @@ -894,7 +885,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { .0 .trait_items(self.db) .associated_types() - .map(|item| (item, trait_ref)), + .map(|item| (item.into(), trait_ref)), ); } ClauseKind::Projection(pred) => { @@ -928,7 +919,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // the discussion in #56288 for alternatives. if !references_self { let key = ( - pred.skip_binder().projection_term.def_id.expect_type_alias(), + pred.skip_binder().def_id().0, interner.anonymize_bound_vars(pred.map_bound(|proj| { proj.projection_term.trait_ref(interner) })), @@ -954,7 +945,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { .filter_map(|key| projection_bounds.get(&key).copied()) .collect(); - projection_bounds.sort_unstable_by_key(|proj| proj.skip_binder().def_id()); + projection_bounds.sort_unstable_by_key(|proj| proj.skip_binder().def_id().0); let principal = principal.map(|principal| { principal.map_bound(|principal| { @@ -967,7 +958,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { .map(|arg| { if arg.walk().any(|arg| arg == dummy_self_ty.into()) { // FIXME: Report an error. - Ty::new_error(interner, ErrorGuaranteed).into() + self.types.types.error.into() } else { arg } @@ -993,8 +984,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { false }); if references_self { - proj.projection_term = - replace_dummy_self_with_error(interner, proj.projection_term); + proj.projection_term = replace_dummy_self_with_error( + interner, + self.types, + proj.projection_term, + ); } ExistentialPredicate::Projection(ExistentialProjection::erase_self_ty( @@ -1032,17 +1026,17 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } else { // FIXME: report error // (additional non-auto traits, associated type rebound, or no resolved trait) - Ty::new_error(self.interner, ErrorGuaranteed) + self.types.types.error } } - fn lower_impl_trait(&mut self, def_id: SolverDefId, bounds: &[TypeBound]) -> ImplTrait { + fn lower_impl_trait(&mut self, def_id: InternedOpaqueTyId, bounds: &[TypeBound]) -> ImplTrait { let interner = self.interner; cov_mark::hit!(lower_rpit); - let args = GenericArgs::identity_for_item(interner, def_id); + let args = GenericArgs::identity_for_item(interner, def_id.into()); let self_ty = Ty::new_alias( self.interner, - AliasTy::new_from_args(interner, rustc_type_ir::Opaque { def_id }, args), + AliasTy::new_from_args(interner, rustc_type_ir::Opaque { def_id: def_id.into() }, args), ); let (predicates, assoc_ty_bounds_start) = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { @@ -1094,11 +1088,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { Some(resolution) => match resolution { LifetimeNs::Static => Region::new_static(self.interner), LifetimeNs::LifetimeParam(id) => { - let idx = match self.generics().lifetime_idx(id) { - None => return Region::error(self.interner), - Some(idx) => idx, - }; - self.region_param(id, idx as u32) + let idx = self.generics().lifetime_param_idx(id); + self.region_param(id, idx) } }, None => Region::error(self.interner), @@ -1106,20 +1097,78 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } -fn dyn_trait_dummy_self(interner: DbInterner<'_>) -> Ty<'_> { - // This type must not appear anywhere except here. - Ty::new_fresh(interner, 0) +#[derive(Clone, PartialEq, Eq)] +pub struct TyLoweringResult<T> { + pub value: T, + info: Option<Box<(ThinVec<TyLoweringDiagnostic>, ThinVec<AnonConstId>)>>, +} + +impl<T: std::fmt::Debug> std::fmt::Debug for TyLoweringResult<T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut debug = f.debug_struct("TyLoweringResult"); + debug.field("value", &self.value); + let diagnostics = self.diagnostics(); + if !diagnostics.is_empty() { + debug.field("diagnostics", &diagnostics); + } + let defined_anon_consts = self.defined_anon_consts(); + if !defined_anon_consts.is_empty() { + debug.field("defined_anon_consts", &defined_anon_consts); + } + debug.finish() + } +} + +impl<T> TyLoweringResult<T> { + fn new( + value: T, + mut diagnostics: ThinVec<TyLoweringDiagnostic>, + mut defined_anon_consts: ThinVec<AnonConstId>, + ) -> Self { + let info = if diagnostics.is_empty() && defined_anon_consts.is_empty() { + None + } else { + diagnostics.shrink_to_fit(); + defined_anon_consts.shrink_to_fit(); + Some(Box::new((diagnostics, defined_anon_consts))) + }; + Self { value, info } + } + + fn from_ctx(value: T, ctx: TyLoweringContext<'_, '_>) -> Self { + Self::new(value, ctx.diagnostics, ctx.defined_anon_consts) + } + + fn empty(value: T) -> Self { + Self { value, info: None } + } + + #[inline] + pub fn diagnostics(&self) -> &[TyLoweringDiagnostic] { + match &self.info { + Some(info) => &info.0, + None => &[], + } + } + + #[inline] + pub fn defined_anon_consts(&self) -> &[AnonConstId] { + match &self.info { + Some(info) => &info.1, + None => &[], + } + } } fn replace_dummy_self_with_error<'db, T: TypeFoldable<DbInterner<'db>>>( interner: DbInterner<'db>, + types: &DefaultAny<'db>, t: T, ) -> T { - let dyn_trait_dummy_self = dyn_trait_dummy_self(interner); t.fold_with(&mut BottomUpFolder { interner, ty_op: |ty| { - if ty == dyn_trait_dummy_self { Ty::new_error(interner, ErrorGuaranteed) } else { ty } + if ty == types.types.dyn_trait_dummy_self { types.types.error } else { ty } }, lt_op: |lt| lt, ct_op: |ct| ct, @@ -1133,62 +1182,36 @@ pub(crate) fn lower_mutability(m: hir_def::type_ref::Mutability) -> Mutability { } } -fn unknown_const(_ty: Ty<'_>) -> Const<'_> { - Const::new(DbInterner::conjure(), ConstKind::Error(ErrorGuaranteed)) -} - -pub(crate) type Diagnostics = Option<ThinArc<(), TyLoweringDiagnostic>>; - -pub(crate) fn create_diagnostics(diagnostics: Vec<TyLoweringDiagnostic>) -> Diagnostics { - (!diagnostics.is_empty()).then(|| ThinArc::from_header_and_iter((), diagnostics.into_iter())) -} - pub(crate) fn impl_trait_query<'db>( db: &'db dyn HirDatabase, impl_id: ImplId, ) -> Option<EarlyBinder<'db, TraitRef<'db>>> { - db.impl_trait_with_diagnostics(impl_id).map(|it| it.0) + impl_trait_with_diagnostics(db, impl_id) + .as_ref() + .map(|it| it.value.get(DbInterner::new_no_crate(db))) } -pub(crate) fn impl_trait_with_diagnostics<'db>( - db: &'db dyn HirDatabase, +#[salsa::tracked(returns(ref))] +pub(crate) fn impl_trait_with_diagnostics( + db: &dyn HirDatabase, impl_id: ImplId, -) -> Option<(EarlyBinder<'db, TraitRef<'db>>, Diagnostics)> { - return impl_trait_with_diagnostics_query(db, impl_id).as_ref().map(|(binder, diags)| { - ( - binder.get_with(|(trait_id, args)| { - TraitRef::new_from_args( - DbInterner::new_no_crate(db), - (*trait_id).into(), - args.as_ref(), - ) - }), - diags.clone(), - ) - }); - - #[salsa::tracked(returns(ref))] - pub(crate) fn impl_trait_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - impl_id: ImplId, - ) -> Option<(StoredEarlyBinder<(TraitId, StoredGenericArgs)>, Diagnostics)> { - let impl_data = ImplSignature::of(db, impl_id); - let resolver = impl_id.resolver(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &impl_data.store, - impl_id.into(), - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, - ); - let self_ty = db.impl_self_ty(impl_id).skip_binder(); - let target_trait = impl_data.target_trait.as_ref()?; - let trait_ref = ctx.lower_trait_ref(target_trait, self_ty)?; - Some(( - StoredEarlyBinder::bind((trait_ref.def_id.0, trait_ref.args.store())), - create_diagnostics(ctx.diagnostics), - )) - } +) -> Option<TyLoweringResult<StoredEarlyBinder<StoredTraitRef>>> { + let impl_data = ImplSignature::of(db, impl_id); + let resolver = impl_id.resolver(db); + let generics = OnceCell::new(); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &impl_data.store, + ExpressionStoreOwnerId::Signature(impl_id.into()), + impl_id.into(), + &generics, + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + ); + let self_ty = db.impl_self_ty(impl_id).skip_binder(); + let target_trait = impl_data.target_trait.as_ref()?; + let trait_ref = ctx.lower_trait_ref(target_trait, self_ty)?; + Some(TyLoweringResult::from_ctx(StoredEarlyBinder::bind(StoredTraitRef::new(trait_ref)), ctx)) } impl ImplTraitId { @@ -1259,11 +1282,14 @@ impl ImplTraits { // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe let data = FunctionSignature::of(db, def); let resolver = def.resolver(db); + let generics = OnceCell::new(); let mut ctx_ret = TyLoweringContext::new( db, &resolver, &data.store, + ExpressionStoreOwnerId::Signature(def.into()), def.into(), + &generics, LifetimeElisionKind::Infer, ) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); @@ -1287,11 +1313,14 @@ impl ImplTraits { ) -> Option<Box<StoredEarlyBinder<ImplTraits>>> { let data = TypeAliasSignature::of(db, def); let resolver = def.resolver(db); + let generics = OnceCell::new(); let mut ctx = TyLoweringContext::new( db, &resolver, &data.store, + ExpressionStoreOwnerId::Signature(def.into()), def.into(), + &generics, LifetimeElisionKind::AnonymousReportError, ) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); @@ -1354,97 +1383,118 @@ pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBind it, GenericArgs::identity_for_item(interner, it.into()), )), - TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0, + TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).value.get(), } } /// Build the declared type of a function. This should not need to look at the /// function body. -fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> StoredEarlyBinder<StoredTy> { +fn type_for_fn<'db>(db: &'db dyn HirDatabase, def: FunctionId) -> EarlyBinder<'db, Ty<'db>> { let interner = DbInterner::new_no_crate(db); - StoredEarlyBinder::bind( - Ty::new_fn_def( - interner, - CallableDefId::FunctionId(def).into(), - GenericArgs::identity_for_item(interner, def.into()), - ) - .store(), - ) + EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::FunctionId(def).into(), + GenericArgs::identity_for_item(interner, def.into()), + )) +} + +pub(crate) fn type_for_const<'db>( + db: &'db dyn HirDatabase, + def: ConstId, +) -> EarlyBinder<'db, Ty<'db>> { + type_for_const_with_diagnostics(db, def).value.get() } /// Build the declared type of a const. -fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> StoredEarlyBinder<StoredTy> { +#[salsa_macros::tracked(returns(ref))] +pub(crate) fn type_for_const_with_diagnostics( + db: &dyn HirDatabase, + def: ConstId, +) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> { let resolver = def.resolver(db); let data = ConstSignature::of(db, def); let parent = def.loc(db).container; + let generics = OnceCell::new(); let mut ctx = TyLoweringContext::new( db, &resolver, &data.store, + ExpressionStoreOwnerId::Signature(def.into()), def.into(), + &generics, LifetimeElisionKind::AnonymousReportError, ); ctx.set_lifetime_elision(LifetimeElisionKind::for_const(ctx.interner, parent)); - StoredEarlyBinder::bind(ctx.lower_ty(data.type_ref).store()) + let result = StoredEarlyBinder::bind(ctx.lower_ty(data.type_ref).store()); + TyLoweringResult::from_ctx(result, ctx) +} + +pub(crate) fn type_for_static<'db>( + db: &'db dyn HirDatabase, + def: StaticId, +) -> EarlyBinder<'db, Ty<'db>> { + type_for_static_with_diagnostics(db, def).value.get() } /// Build the declared type of a static. -fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> StoredEarlyBinder<StoredTy> { +#[salsa_macros::tracked(returns(ref))] +pub(crate) fn type_for_static_with_diagnostics( + db: &dyn HirDatabase, + def: StaticId, +) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> { let resolver = def.resolver(db); let data = StaticSignature::of(db, def); + let generics = OnceCell::new(); let mut ctx = TyLoweringContext::new( db, &resolver, &data.store, + ExpressionStoreOwnerId::Signature(def.into()), def.into(), + &generics, LifetimeElisionKind::AnonymousReportError, ); ctx.set_lifetime_elision(LifetimeElisionKind::Elided(Region::new_static(ctx.interner))); - StoredEarlyBinder::bind(ctx.lower_ty(data.type_ref).store()) + let result = StoredEarlyBinder::bind(ctx.lower_ty(data.type_ref).store()); + TyLoweringResult::from_ctx(result, ctx) } /// Build the type of a tuple struct constructor. -fn type_for_struct_constructor( - db: &dyn HirDatabase, +fn type_for_struct_constructor<'db>( + db: &'db dyn HirDatabase, def: StructId, -) -> Option<StoredEarlyBinder<StoredTy>> { +) -> Option<EarlyBinder<'db, Ty<'db>>> { let struct_data = StructSignature::of(db, def); match struct_data.shape { FieldsShape::Record => None, FieldsShape::Unit => Some(type_for_adt(db, def.into())), FieldsShape::Tuple => { let interner = DbInterner::new_no_crate(db); - Some(StoredEarlyBinder::bind( - Ty::new_fn_def( - interner, - CallableDefId::StructId(def).into(), - GenericArgs::identity_for_item(interner, def.into()), - ) - .store(), - )) + Some(EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::StructId(def).into(), + GenericArgs::identity_for_item(interner, def.into()), + ))) } } } /// Build the type of a tuple enum variant constructor. -fn type_for_enum_variant_constructor( - db: &dyn HirDatabase, +fn type_for_enum_variant_constructor<'db>( + db: &'db dyn HirDatabase, def: EnumVariantId, -) -> Option<StoredEarlyBinder<StoredTy>> { +) -> Option<EarlyBinder<'db, Ty<'db>>> { let struct_data = def.fields(db); match struct_data.shape { FieldsShape::Record => None, FieldsShape::Unit => Some(type_for_adt(db, def.loc(db).parent.into())), FieldsShape::Tuple => { let interner = DbInterner::new_no_crate(db); - Some(StoredEarlyBinder::bind( - Ty::new_fn_def( - interner, - CallableDefId::EnumVariantId(def).into(), - GenericArgs::identity_for_item(interner, def.loc(db).parent.into()), - ) - .store(), - )) + Some(EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::EnumVariantId(def).into(), + GenericArgs::identity_for_item(interner, def.loc(db).parent.into()), + ))) } } } @@ -1453,213 +1503,190 @@ pub(crate) fn value_ty<'db>( db: &'db dyn HirDatabase, def: ValueTyDefId, ) -> Option<EarlyBinder<'db, Ty<'db>>> { - return value_ty_query(db, def).as_ref().map(|it| it.get()); - - #[salsa::tracked(returns(ref))] - pub(crate) fn value_ty_query<'db>( - db: &'db dyn HirDatabase, - def: ValueTyDefId, - ) -> Option<StoredEarlyBinder<StoredTy>> { - match def { - ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)), - ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), - ValueTyDefId::UnionId(it) => Some(type_for_adt(db, it.into())), - ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), - ValueTyDefId::ConstId(it) => Some(type_for_const(db, it)), - ValueTyDefId::StaticId(it) => Some(type_for_static(db, it)), - } + match def { + ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)), + ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), + ValueTyDefId::UnionId(it) => Some(type_for_adt(db, it.into())), + ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), + ValueTyDefId::ConstId(it) => Some(type_for_const(db, it)), + ValueTyDefId::StaticId(it) => Some(type_for_static(db, it)), } } -pub(crate) fn type_for_type_alias_with_diagnostics<'db>( - db: &'db dyn HirDatabase, +#[salsa::tracked(returns(ref), cycle_result = type_for_type_alias_with_diagnostics_cycle_result)] +pub(crate) fn type_for_type_alias_with_diagnostics( + db: &dyn HirDatabase, t: TypeAliasId, -) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { - let (ty, diags) = type_for_type_alias_with_diagnostics_query(db, t); - return (ty.get(), diags.clone()); - - #[salsa::tracked(returns(ref), cycle_result = type_for_type_alias_with_diagnostics_cycle_result)] - pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - t: TypeAliasId, - ) -> (StoredEarlyBinder<StoredTy>, Diagnostics) { - let type_alias_data = TypeAliasSignature::of(db, t); - let mut diags = None; +) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> { + let type_alias_data = TypeAliasSignature::of(db, t); + let interner = DbInterner::new_no_crate(db); + if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { + TyLoweringResult::empty(StoredEarlyBinder::bind( + Ty::new_foreign(interner, t.into()).store(), + )) + } else { let resolver = t.resolver(db); - let interner = DbInterner::new_no_crate(db); - let inner = if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { - StoredEarlyBinder::bind(Ty::new_foreign(interner, t.into()).store()) - } else { - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &type_alias_data.store, - t.into(), - LifetimeElisionKind::AnonymousReportError, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - let res = StoredEarlyBinder::bind( - type_alias_data - .ty - .map(|type_ref| ctx.lower_ty(type_ref)) - .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) - .store(), - ); - diags = create_diagnostics(ctx.diagnostics); - res - }; - (inner, diags) - } - - pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result( - db: &dyn HirDatabase, - _: salsa::Id, - _adt: TypeAliasId, - ) -> (StoredEarlyBinder<StoredTy>, Diagnostics) { - ( - StoredEarlyBinder::bind( - Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(), - ), - None, + let generics = OnceCell::new(); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + ExpressionStoreOwnerId::Signature(t.into()), + t.into(), + &generics, + LifetimeElisionKind::AnonymousReportError, ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + let res = StoredEarlyBinder::bind( + type_alias_data + .ty + .map(|type_ref| ctx.lower_ty(type_ref)) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) + .store(), + ); + TyLoweringResult::from_ctx(res, ctx) } } +pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result( + db: &dyn HirDatabase, + _: salsa::Id, + _adt: TypeAliasId, +) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> { + TyLoweringResult::empty(StoredEarlyBinder::bind( + Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(), + )) +} + pub(crate) fn impl_self_ty_query<'db>( db: &'db dyn HirDatabase, impl_id: ImplId, ) -> EarlyBinder<'db, Ty<'db>> { - db.impl_self_ty_with_diagnostics(impl_id).0 + impl_self_ty_with_diagnostics(db, impl_id).value.get() } -pub(crate) fn impl_self_ty_with_diagnostics<'db>( - db: &'db dyn HirDatabase, +#[salsa::tracked(returns(ref), cycle_result = impl_self_ty_with_diagnostics_cycle_result)] +pub(crate) fn impl_self_ty_with_diagnostics( + db: &dyn HirDatabase, impl_id: ImplId, -) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { - let (ty, diags) = impl_self_ty_with_diagnostics_query(db, impl_id); - return (ty.get(), diags.clone()); - - #[salsa::tracked(returns(ref), cycle_result = impl_self_ty_with_diagnostics_cycle_result)] - pub(crate) fn impl_self_ty_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - impl_id: ImplId, - ) -> (StoredEarlyBinder<StoredTy>, Diagnostics) { - let resolver = impl_id.resolver(db); +) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> { + let resolver = impl_id.resolver(db); + let generics = OnceCell::new(); + let impl_data = ImplSignature::of(db, impl_id); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &impl_data.store, + ExpressionStoreOwnerId::Signature(impl_id.into()), + impl_id.into(), + &generics, + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + ); + let ty = ctx.lower_ty(impl_data.self_ty); + assert!(!ty.has_escaping_bound_vars()); + TyLoweringResult::from_ctx(StoredEarlyBinder::bind(ty.store()), ctx) +} - let impl_data = ImplSignature::of(db, impl_id); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &impl_data.store, - impl_id.into(), - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, - ); - let ty = ctx.lower_ty(impl_data.self_ty); - assert!(!ty.has_escaping_bound_vars()); - (StoredEarlyBinder::bind(ty.store()), create_diagnostics(ctx.diagnostics)) - } +pub(crate) fn impl_self_ty_with_diagnostics_cycle_result( + db: &dyn HirDatabase, + _: salsa::Id, + _impl_id: ImplId, +) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> { + TyLoweringResult::empty(StoredEarlyBinder::bind( + Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(), + )) +} - pub(crate) fn impl_self_ty_with_diagnostics_cycle_result( - db: &dyn HirDatabase, - _: salsa::Id, - _impl_id: ImplId, - ) -> (StoredEarlyBinder<StoredTy>, Diagnostics) { - ( - StoredEarlyBinder::bind( - Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(), - ), - None, - ) +pub(crate) fn const_param_ty<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> { + let param_types = const_param_types(db, def.parent()); + match param_types.get(def.local_id()) { + Some(ty) => ty.as_ref(), + None => Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed), } } -pub(crate) fn const_param_ty_query<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> { - db.const_param_ty_with_diagnostics(def).0 +pub(crate) fn const_param_types( + db: &dyn HirDatabase, + def: GenericDefId, +) -> &ArenaMap<LocalTypeOrConstParamId, StoredTy> { + &const_param_types_with_diagnostics(db, def).value } -// returns None if def is a type arg -pub(crate) fn const_param_ty_with_diagnostics<'db>( - db: &'db dyn HirDatabase, - def: ConstParamId, -) -> (Ty<'db>, Diagnostics) { - let (ty, diags) = const_param_ty_with_diagnostics_query(db, (), def); - return (ty.as_ref(), diags.clone()); - - // FIXME: Make this query non-interned. - #[salsa::tracked(returns(ref), cycle_result = const_param_ty_with_diagnostics_cycle_result)] - pub(crate) fn const_param_ty_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - _: (), - def: ConstParamId, - ) -> (StoredTy, Diagnostics) { - let (parent_data, store) = GenericParams::with_store(db, def.parent()); - let data = &parent_data[def.local_id()]; - let resolver = def.parent().resolver(db); - let interner = DbInterner::new_no_crate(db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - store, - def.parent(), - LifetimeElisionKind::AnonymousReportError, - ); - let ty = match data { - TypeOrConstParamData::TypeParamData(_) => { - never!(); - Ty::new_error(interner, ErrorGuaranteed) - } - TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty), - }; - (ty.store(), create_diagnostics(ctx.diagnostics)) +#[salsa::tracked(returns(ref), cycle_result = const_param_types_with_diagnostics_cycle_result)] +pub(crate) fn const_param_types_with_diagnostics( + db: &dyn HirDatabase, + def: GenericDefId, +) -> TyLoweringResult<ArenaMap<LocalTypeOrConstParamId, StoredTy>> { + let mut result = ArenaMap::new(); + let (data, store) = GenericParams::with_store(db, def); + let resolver = def.resolver(db); + let generics = OnceCell::new(); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + store, + ExpressionStoreOwnerId::Signature(def), + def, + &generics, + LifetimeElisionKind::AnonymousReportError, + ); + ctx.forbid_params_after(0, ForbidParamsAfterReason::ConstParamTy); + for (local_id, param_data) in data.iter_type_or_consts() { + if let TypeOrConstParamData::ConstParamData(param_data) = param_data { + result.insert(local_id, ctx.lower_ty(param_data.ty).store()); + } } + result.shrink_to_fit(); + TyLoweringResult::from_ctx(result, ctx) +} - pub(crate) fn const_param_ty_with_diagnostics_cycle_result( - db: &dyn HirDatabase, - _: salsa::Id, - _: (), - _def: ConstParamId, - ) -> (StoredTy, Diagnostics) { - let interner = DbInterner::new_no_crate(db); - (Ty::new_error(interner, ErrorGuaranteed).store(), None) - } +fn const_param_types_with_diagnostics_cycle_result( + _db: &dyn HirDatabase, + _: salsa::Id, + _def: GenericDefId, +) -> TyLoweringResult<ArenaMap<LocalTypeOrConstParamId, StoredTy>> { + TyLoweringResult::empty(ArenaMap::default()) } pub(crate) fn field_types_query( db: &dyn HirDatabase, variant_id: VariantId, ) -> &ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>> { - &db.field_types_with_diagnostics(variant_id).0 + &field_types_with_diagnostics(db, variant_id).value } /// Build the type of all specific fields of a struct or enum variant. #[salsa::tracked(returns(ref))] -pub(crate) fn field_types_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, +pub(crate) fn field_types_with_diagnostics( + db: &dyn HirDatabase, variant_id: VariantId, -) -> (ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>, Diagnostics) { +) -> TyLoweringResult<ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>> { let var_data = variant_id.fields(db); let fields = var_data.fields(); if fields.is_empty() { - return (ArenaMap::default(), None); + return TyLoweringResult::empty(ArenaMap::default()); } - let (resolver, def): (_, GenericDefId) = match variant_id { + let (resolver, generic_def): (_, GenericDefId) = match variant_id { VariantId::StructId(it) => (it.resolver(db), it.into()), VariantId::UnionId(it) => (it.resolver(db), it.into()), VariantId::EnumVariantId(it) => (it.resolver(db), it.lookup(db).parent.into()), }; + let generics = OnceCell::new(); let mut res = ArenaMap::default(); let mut ctx = TyLoweringContext::new( db, &resolver, &var_data.store, - def, + ExpressionStoreOwnerId::VariantFields(variant_id), + generic_def, + &generics, LifetimeElisionKind::AnonymousReportError, ); for (field_id, field_data) in var_data.fields().iter() { res.insert(field_id, StoredEarlyBinder::bind(ctx.lower_ty(field_data.type_ref).store())); } - (res, create_diagnostics(ctx.diagnostics)) + TyLoweringResult::from_ctx(res, ctx) } #[derive(Debug, PartialEq, Eq, Default)] @@ -1776,20 +1803,21 @@ fn resolve_type_param_assoc_type_shorthand( assoc_name: Name, ) -> AssocTypeShorthandResolution { let generics = generics(db, def); + let store = generics.store(); + let generics = &OnceCell::from(generics); let resolver = def.resolver(db); let mut ctx = TyLoweringContext::new( db, &resolver, - generics.store(), + store, + ExpressionStoreOwnerId::Signature(def), def, + generics, LifetimeElisionKind::AnonymousReportError, ); let interner = ctx.interner; - let param_ty = Ty::new_param( - interner, - param, - generics.type_or_const_param_idx(param.into()).unwrap() as u32, - ); + let generics = generics.get().unwrap(); + let param_ty = Ty::new_param(interner, param, generics.type_or_const_param_idx(param.into())); let mut this_trait_resolution = None; if let GenericDefId::TraitId(containing_trait) = param.parent() @@ -1805,9 +1833,7 @@ fn resolve_type_param_assoc_type_shorthand( } let mut supertraits_resolution = None; - for maybe_parent_generics in - std::iter::successors(Some(&generics), |generics| generics.parent_generics()) - { + for maybe_parent_generics in generics.iter_owners().rev() { ctx.store = maybe_parent_generics.store(); for pred in maybe_parent_generics.where_predicates() { let (WherePredicate::TypeBound { target, bound } @@ -1869,7 +1895,9 @@ fn resolve_type_param_assoc_type_shorthand( let (assoc_type, args) = assoc_type_and_args .get_with(|(assoc_type, args)| (*assoc_type, args.as_ref())) .skip_binder(); - let args = EarlyBinder::bind(args).instantiate(interner, bounded_trait_ref.args); + let args = EarlyBinder::bind(args) + .instantiate(interner, bounded_trait_ref.args) + .skip_norm_wip(); let current_result = StoredEarlyBinder::bind((assoc_type, args.store())); if let Some(this_trait_resolution) = &this_trait_resolution { if *this_trait_resolution == current_result { @@ -1927,7 +1955,11 @@ pub(crate) fn type_alias_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, ) -> EarlyBinder<'db, &'db [Clause<'db>]> { - type_alias_bounds_with_diagnostics(db, type_alias).0.predicates.map_bound(|it| it.as_slice()) + type_alias_bounds_with_diagnostics(db, type_alias) + .value + .predicates + .get() + .map_bound(|it| it.as_slice()) } #[inline] @@ -1935,89 +1967,74 @@ pub(crate) fn type_alias_self_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, ) -> EarlyBinder<'db, &'db [Clause<'db>]> { - let (TypeAliasBounds { predicates, assoc_ty_bounds_start }, _) = - type_alias_bounds_with_diagnostics(db, type_alias); - predicates.map_bound(|it| &it.as_slice()[..assoc_ty_bounds_start as usize]) + let TypeAliasBounds { predicates, assoc_ty_bounds_start } = + &type_alias_bounds_with_diagnostics(db, type_alias).value; + predicates.get().map_bound(|it| &it.as_slice()[..*assoc_ty_bounds_start as usize]) } #[derive(PartialEq, Eq, Debug, Hash)] -struct TypeAliasBounds<T> { +pub struct TypeAliasBounds<T> { predicates: T, assoc_ty_bounds_start: u32, } -fn type_alias_bounds_with_diagnostics<'db>( - db: &'db dyn HirDatabase, +#[salsa::tracked(returns(ref))] +pub(crate) fn type_alias_bounds_with_diagnostics( + db: &dyn HirDatabase, type_alias: TypeAliasId, -) -> (TypeAliasBounds<EarlyBinder<'db, Clauses<'db>>>, Diagnostics) { - let (TypeAliasBounds { predicates, assoc_ty_bounds_start }, diags) = - type_alias_bounds_with_diagnostics_query(db, type_alias); - return ( - TypeAliasBounds { - predicates: predicates.get(), - assoc_ty_bounds_start: *assoc_ty_bounds_start, - }, - diags.clone(), +) -> TyLoweringResult<TypeAliasBounds<StoredEarlyBinder<StoredClauses>>> { + let type_alias_data = TypeAliasSignature::of(db, type_alias); + let resolver = type_alias.resolver(db); + let generics = OnceCell::new(); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + ExpressionStoreOwnerId::Signature(type_alias.into()), + type_alias.into(), + &generics, + LifetimeElisionKind::AnonymousReportError, ); + let interner = ctx.interner; - #[salsa::tracked(returns(ref))] - pub fn type_alias_bounds_with_diagnostics_query<'db>( - db: &'db dyn HirDatabase, - type_alias: TypeAliasId, - ) -> (TypeAliasBounds<StoredEarlyBinder<StoredClauses>>, Diagnostics) { - let type_alias_data = TypeAliasSignature::of(db, type_alias); - let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &type_alias_data.store, - type_alias.into(), - LifetimeElisionKind::AnonymousReportError, - ); - let interner = ctx.interner; - let def_id = type_alias.into(); + let item_args = GenericArgs::identity_for_item(interner, type_alias.into()); + let interner_ty = Ty::new_projection_from_args(interner, type_alias.into(), item_args); - let item_args = GenericArgs::identity_for_item(interner, def_id); - let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args); + let mut bounds = Vec::new(); + let mut assoc_ty_bounds = Vec::new(); + for bound in &type_alias_data.bounds { + ctx.lower_type_bound(bound, interner_ty, false).for_each(|(pred, source)| match source { + GenericPredicateSource::SelfOnly => { + bounds.push(pred); + } + GenericPredicateSource::AssocTyBound => { + assoc_ty_bounds.push(pred); + } + }); + } - let mut bounds = Vec::new(); - let mut assoc_ty_bounds = Vec::new(); - for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, interner_ty, false).for_each( - |(pred, source)| match source { - GenericPredicateSource::SelfOnly => { - bounds.push(pred); - } - GenericPredicateSource::AssocTyBound => { - assoc_ty_bounds.push(pred); - } - }, + if !ctx.unsized_types.contains(&interner_ty) { + let sized_trait = ctx.lang_items.Sized; + if let Some(sized_trait) = sized_trait { + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_slice(&[interner_ty.into()]), ); - } - - if !ctx.unsized_types.contains(&interner_ty) { - let sized_trait = ctx.lang_items.Sized; - if let Some(sized_trait) = sized_trait { - let trait_ref = TraitRef::new_from_args( - interner, - sized_trait.into(), - GenericArgs::new_from_slice(&[interner_ty.into()]), - ); - bounds.push(trait_ref.upcast(interner)); - }; - } + bounds.push(trait_ref.upcast(interner)); + }; + } - let assoc_ty_bounds_start = bounds.len() as u32; - bounds.extend(assoc_ty_bounds); + let assoc_ty_bounds_start = bounds.len() as u32; + bounds.extend(assoc_ty_bounds); - ( - TypeAliasBounds { - predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&bounds).store()), - assoc_ty_bounds_start, - }, - create_diagnostics(ctx.diagnostics), - ) - } + TyLoweringResult::from_ctx( + TypeAliasBounds { + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&bounds).store()), + assoc_ty_bounds_start, + }, + ctx, + ) } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -2048,7 +2065,7 @@ impl<'db> GenericPredicates { pub fn query_with_diagnostics( db: &'db dyn HirDatabase, def: GenericDefId, - ) -> (GenericPredicates, Diagnostics) { + ) -> TyLoweringResult<GenericPredicates> { generic_predicates(db, def) } } @@ -2058,17 +2075,26 @@ fn generic_predicates_cycle_result( _db: &dyn HirDatabase, _: salsa::Id, _def: GenericDefId, -) -> (GenericPredicates, Diagnostics) { - ( - GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( - Clauses::default().store(), - )), - None, - ) +) -> TyLoweringResult<GenericPredicates> { + TyLoweringResult::empty(GenericPredicates::from_explicit_own_predicates( + StoredEarlyBinder::bind(Clauses::default().store()), + )) } impl GenericPredicates { #[inline] + pub fn empty() -> &'static GenericPredicates { + static EMPTY: OnceLock<GenericPredicates> = OnceLock::new(); + EMPTY.get_or_init(|| GenericPredicates { + predicates: StoredEarlyBinder::bind(Clauses::default().store()), + has_trait_implied_predicate: false, + parent_explicit_self_predicates_start: 0, + own_predicates_start: 0, + own_assoc_ty_bounds_start: 0, + }) + } + + #[inline] pub(crate) fn from_explicit_own_predicates( predicates: StoredEarlyBinder<StoredClauses>, ) -> Self { @@ -2084,7 +2110,7 @@ impl GenericPredicates { #[inline] pub fn query(db: &dyn HirDatabase, def: GenericDefId) -> &GenericPredicates { - &Self::query_with_diagnostics(db, def).0 + &Self::query_with_diagnostics(db, def).value } #[inline] @@ -2161,8 +2187,10 @@ pub(crate) fn param_env_from_predicates<'db>( interner: DbInterner<'db>, predicates: &'db GenericPredicates, ) -> ParamEnv<'db> { - let clauses = - rustc_type_ir::elaborate::elaborate(interner, predicates.all_predicates().iter_identity()); + let clauses = rustc_type_ir::elaborate::elaborate( + interner, + predicates.all_predicates().iter_identity().map(Unnormalized::skip_norm_wip), + ); let clauses = Clauses::new_from_iter(interner, clauses); // FIXME: We should normalize projections here, like rustc does. @@ -2178,8 +2206,8 @@ pub(crate) fn trait_environment<'db>( return ParamEnv { clauses: trait_environment_query(db, def).as_ref() }; #[salsa::tracked(returns(ref))] - pub(crate) fn trait_environment_query<'db>( - db: &'db dyn HirDatabase, + pub(crate) fn trait_environment_query( + db: &dyn HirDatabase, def: GenericDefId, ) -> StoredClauses { let module = def.module(db); @@ -2192,17 +2220,25 @@ pub(crate) fn trait_environment<'db>( /// Resolve the where clause(s) of an item with generics, /// with a given filter #[tracing::instrument(skip(db), ret)] -fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredicates, Diagnostics) { +fn generic_predicates( + db: &dyn HirDatabase, + def: GenericDefId, +) -> TyLoweringResult<GenericPredicates> { let generics = generics(db, def); + let store = generics.store(); + let generics = &OnceCell::from(generics); let resolver = def.resolver(db); let interner = DbInterner::new_no_crate(db); let mut ctx = TyLoweringContext::new( db, &resolver, - generics.store(), + store, + ExpressionStoreOwnerId::Signature(def), def, + generics, LifetimeElisionKind::AnonymousReportError, ); + let generics = generics.get().unwrap(); let sized_trait = ctx.lang_items.Sized; // We need to lower parents and self separately - see the comment below lowering of implicit @@ -2211,16 +2247,13 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic let mut parent_predicates = Vec::new(); let mut own_assoc_ty_bounds = Vec::new(); let mut parent_assoc_ty_bounds = Vec::new(); - let all_generics = - std::iter::successors(Some(&generics), |generics| generics.parent_generics()) - .collect::<ArrayVec<_, 2>>(); let own_implicit_trait_predicate = implicit_trait_predicate(interner, def); - let parent_implicit_trait_predicate = if all_generics.len() > 1 { - implicit_trait_predicate(interner, all_generics.last().unwrap().def()) + let parent_implicit_trait_predicate = if let Some(parent) = generics.parent() { + implicit_trait_predicate(interner, parent.def()) } else { None }; - for &maybe_parent_generics in all_generics.iter().rev() { + for maybe_parent_generics in generics.iter_owners() { // Collect only diagnostics from the child, not including parents. ctx.diagnostics.clear(); @@ -2291,12 +2324,9 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic parent_predicates.push(clause); } }; - let parent_params_len = maybe_parent_generics.len_parent(); - maybe_parent_generics.iter_self().enumerate().for_each( - |(param_idx, (param_id, param_data))| { - add_sized_clause((param_idx + parent_params_len) as u32, param_id, param_data); - }, - ); + maybe_parent_generics.iter_with_idx().for_each(|(param_idx, param_id, param_data)| { + add_sized_clause(param_idx, param_id, param_data); + }); } // We do not clear `ctx.unsized_types`, as the `?Sized` clause of a child (e.g. an associated type) can @@ -2305,7 +2335,8 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic // But we do have to lower the parent first. } - let diagnostics = create_diagnostics(ctx.diagnostics); + let diagnostics = mem::take(&mut ctx.diagnostics); + let defined_anon_consts = mem::take(&mut ctx.defined_anon_consts); let predicates = parent_implicit_trait_predicate .iter() @@ -2331,7 +2362,7 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic own_assoc_ty_bounds_start, predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()), }; - return (predicates, diagnostics); + return TyLoweringResult::new(predicates, diagnostics, defined_anon_consts); fn implicit_trait_predicate<'db>( interner: DbInterner<'db>, @@ -2360,26 +2391,15 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic fn push_const_arg_has_type_predicates<'db>( db: &'db dyn HirDatabase, predicates: &mut Vec<Clause<'db>>, - generics: &Generics<'db>, + single_generics: &SingleGenerics<'db>, ) { let interner = DbInterner::new_no_crate(db); - let const_params_offset = generics.len_parent() + generics.len_lifetimes_self(); - for (param_index, (param_idx, param_data)) in generics.iter_self_type_or_consts().enumerate() { - if !matches!(param_data, TypeOrConstParamData::ConstParamData(_)) { - continue; - } - - let param_id = ConstParamId::from_unchecked(TypeOrConstParamId { - parent: generics.def(), - local_id: param_idx, - }); + for (param_index, param_id, _) in single_generics.iter_with_idx() { + let GenericParamId::ConstParamId(param_id) = param_id else { continue }; predicates.push(Clause( ClauseKind::ConstArgHasType( - Const::new_param( - interner, - ParamConst { id: param_id, index: (param_index + const_params_offset) as u32 }, - ), - db.const_param_ty_ns(param_id), + Const::new_param(interner, ParamConst { id: param_id, index: param_index }), + db.const_param_ty(param_id), ) .upcast(interner), )); @@ -2387,82 +2407,82 @@ fn push_const_arg_has_type_predicates<'db>( } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GenericDefaults(Option<Arc<[Option<StoredEarlyBinder<StoredGenericArg>>]>>); +pub struct GenericDefaults(ThinVec<Option<StoredEarlyBinder<StoredGenericArg>>>); impl GenericDefaults { #[inline] - pub fn get<'db>(&self, idx: usize) -> Option<EarlyBinder<'db, GenericArg<'db>>> { - Some(self.0.as_ref()?[idx].as_ref()?.get_with(|it| it.as_ref())) + pub fn as_ref(&self) -> GenericDefaultsRef<'_> { + GenericDefaultsRef(&self.0) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct GenericDefaultsRef<'db>(&'db [Option<StoredEarlyBinder<StoredGenericArg>>]); + +impl<'db> GenericDefaultsRef<'db> { + #[inline] + pub fn get(self, idx: usize) -> Option<EarlyBinder<'db, GenericArg<'db>>> { + Some(self.0.get(idx)?.as_ref()?.get()) } } -pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) -> GenericDefaults { - db.generic_defaults_with_diagnostics(def).0 +pub(crate) fn generic_defaults(db: &dyn HirDatabase, def: GenericDefId) -> GenericDefaultsRef<'_> { + generic_defaults_with_diagnostics(db, def).value.as_ref() } /// Resolve the default type params from generics. /// /// Diagnostics are only returned for this `GenericDefId` (returned defaults include parents). -pub(crate) fn generic_defaults_with_diagnostics_query( +#[salsa_macros::tracked(returns(ref), cycle_result = generic_defaults_with_diagnostics_cycle_result)] +pub(crate) fn generic_defaults_with_diagnostics( db: &dyn HirDatabase, def: GenericDefId, -) -> (GenericDefaults, Diagnostics) { - let generic_params = generics(db, def); - if generic_params.is_empty() { - return (GenericDefaults(None), None); +) -> TyLoweringResult<GenericDefaults> { + let generics = generics(db, def); + if generics.has_no_params() { + return TyLoweringResult::empty(GenericDefaults(ThinVec::new())); } let resolver = def.resolver(db); - let store_for_self = generic_params.store(); + let store_for_self = generics.store(); + let generics = &OnceCell::from(generics); let mut ctx = TyLoweringContext::new( db, &resolver, store_for_self, + ExpressionStoreOwnerId::Signature(def), def, + generics, LifetimeElisionKind::AnonymousReportError, ) .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed); - let mut idx = 0; - let mut has_any_default = false; - let mut defaults = generic_params - .iter_parents_with_store() - .map(|((_id, p), store)| { - ctx.store = store; - let (result, has_default) = handle_generic_param(&mut ctx, idx, p); - has_any_default |= has_default; - idx += 1; - result - }) - .collect::<Vec<_>>(); + let generics = generics.get().unwrap(); + let mut defaults = ThinVec::new(); + if let Some(parent) = generics.parent() { + ctx.store = parent.store(); + defaults.extend( + parent.iter_with_idx().map(|(idx, _id, p)| handle_generic_param(&mut ctx, idx, p)), + ); + } ctx.diagnostics.clear(); // Don't include diagnostics from the parent. + ctx.defined_anon_consts.clear(); ctx.store = store_for_self; - defaults.extend(generic_params.iter_self().map(|(_id, p)| { - let (result, has_default) = handle_generic_param(&mut ctx, idx, p); - has_any_default |= has_default; - idx += 1; - result - })); - let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics)); - let defaults = if has_any_default { - GenericDefaults(Some(Arc::from_iter(defaults))) - } else { - GenericDefaults(None) - }; - return (defaults, diagnostics); + defaults.extend( + generics.iter_self_with_idx().map(|(idx, _id, p)| handle_generic_param(&mut ctx, idx, p)), + ); + defaults.shrink_to_fit(); + return TyLoweringResult::from_ctx(GenericDefaults(defaults), ctx); fn handle_generic_param<'db>( ctx: &mut TyLoweringContext<'db, '_>, - idx: usize, + idx: u32, p: GenericParamDataRef<'_>, - ) -> (Option<StoredEarlyBinder<StoredGenericArg>>, bool) { - ctx.lowering_param_default(idx as u32); + ) -> Option<StoredEarlyBinder<StoredGenericArg>> { + ctx.forbid_params_after(idx, ForbidParamsAfterReason::LoweringParamDefault); match p { GenericParamDataRef::TypeParamData(p) => { let ty = p.default.map(|ty| ctx.lower_ty(ty)); - ( - ty.map(|ty| StoredEarlyBinder::bind(GenericArg::from(ty).store())), - p.default.is_some(), - ) + ty.map(|ty| StoredEarlyBinder::bind(GenericArg::from(ty).store())) } GenericParamDataRef::ConstParamData(p) => { let val = p.default.map(|c| { @@ -2470,19 +2490,19 @@ pub(crate) fn generic_defaults_with_diagnostics_query( let c = ctx.lower_const(c, param_ty); GenericArg::from(c).store() }); - (val.map(StoredEarlyBinder::bind), p.default.is_some()) + val.map(StoredEarlyBinder::bind) } - GenericParamDataRef::LifetimeParamData(_) => (None, false), + GenericParamDataRef::LifetimeParamData(_) => None, } } } -pub(crate) fn generic_defaults_with_diagnostics_cycle_result( +fn generic_defaults_with_diagnostics_cycle_result( _db: &dyn HirDatabase, _: salsa::Id, _def: GenericDefId, -) -> (GenericDefaults, Diagnostics) { - (GenericDefaults(None), None) +) -> TyLoweringResult<GenericDefaults> { + TyLoweringResult::empty(GenericDefaults(ThinVec::new())) } /// Build the signature of a callable item (function, struct or enum variant). @@ -2490,64 +2510,79 @@ pub(crate) fn callable_item_signature<'db>( db: &'db dyn HirDatabase, def: CallableDefId, ) -> EarlyBinder<'db, PolyFnSig<'db>> { - return callable_item_signature_query(db, def).get_with(|sig| sig.get()); + callable_item_signature_with_diagnostics(db, def).value.get() +} - #[salsa::tracked(returns(ref))] - pub(crate) fn callable_item_signature_query<'db>( - db: &'db dyn HirDatabase, - def: CallableDefId, - ) -> StoredEarlyBinder<StoredPolyFnSig> { - match def { - CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), - CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s), - CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e), +#[salsa::tracked(returns(ref))] +pub(crate) fn callable_item_signature_with_diagnostics( + db: &dyn HirDatabase, + def: CallableDefId, +) -> TyLoweringResult<StoredEarlyBinder<StoredPolyFnSig>> { + match def { + CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), + CallableDefId::StructId(s) => TyLoweringResult::empty(fn_sig_for_struct_constructor(db, s)), + CallableDefId::EnumVariantId(e) => { + TyLoweringResult::empty(fn_sig_for_enum_variant_constructor(db, e)) } } } -fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> StoredEarlyBinder<StoredPolyFnSig> { +fn fn_sig_for_fn( + db: &dyn HirDatabase, + def: FunctionId, +) -> TyLoweringResult<StoredEarlyBinder<StoredPolyFnSig>> { let data = FunctionSignature::of(db, def); let resolver = def.resolver(db); let interner = DbInterner::new_no_crate(db); + let generics = OnceCell::new(); let mut ctx_params = TyLoweringContext::new( db, &resolver, &data.store, + ExpressionStoreOwnerId::Signature(def.into()), def.into(), + &generics, LifetimeElisionKind::for_fn_params(data), ); let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); + let mut ctx_ret = TyLoweringContext::new( + db, + &resolver, + &data.store, + ExpressionStoreOwnerId::Signature(def.into()), + def.into(), + &generics, + LifetimeElisionKind::for_fn_ret(interner), + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); let ret = match data.ret_type { - Some(ret_type) => { - let mut ctx_ret = TyLoweringContext::new( - db, - &resolver, - &data.store, - def.into(), - LifetimeElisionKind::for_fn_ret(interner), - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - ctx_ret.lower_ty(ret_type) - } - None => Ty::new_tup(interner, &[]), + Some(ret_type) => ctx_ret.lower_ty(ret_type), + None => Ty::new_unit(interner), }; let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); + + ctx_params.diagnostics.extend(ctx_ret.diagnostics); + ctx_params.defined_anon_consts.extend(ctx_ret.defined_anon_consts); + // If/when we track late bound vars, we need to switch this to not be `dummy` - StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { - abi: data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), - c_variadic: data.is_varargs(), - safety: if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, + let result = StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { inputs_and_output, - }))) + fn_sig_kind: FnSigKind::new( + data.abi, + if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, + data.is_varargs(), + ), + }))); + TyLoweringResult::from_ctx(result, ctx_params) } -fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> StoredEarlyBinder<StoredTy> { +fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> { let interner = DbInterner::new_no_crate(db); let args = GenericArgs::identity_for_item(interner, adt.into()); let ty = Ty::new_adt(interner, adt, args); - StoredEarlyBinder::bind(ty.store()) + EarlyBinder::bind(ty) } fn fn_sig_for_struct_constructor( @@ -2559,11 +2594,9 @@ fn fn_sig_for_struct_constructor( let ret = type_for_adt(db, def.into()).skip_binder(); let inputs_and_output = - Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret.as_ref()))); + Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret))); StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { - abi: FnAbi::Rust, - c_variadic: false, - safety: Safety::Safe, + fn_sig_kind: FnSigKind::new(ExternAbi::Rust, Safety::Safe, false), inputs_and_output, }))) } @@ -2578,28 +2611,29 @@ fn fn_sig_for_enum_variant_constructor( let ret = type_for_adt(db, parent.into()).skip_binder(); let inputs_and_output = - Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret.as_ref()))); + Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret))); StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { - abi: FnAbi::Rust, - c_variadic: false, - safety: Safety::Safe, + fn_sig_kind: FnSigKind::new(ExternAbi::Rust, Safety::Safe, false), inputs_and_output, }))) } -// FIXME(next-solver): should merge this with `explicit_item_bounds` in some way +// FIXME: Remove this. pub(crate) fn associated_ty_item_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, ) -> EarlyBinder<'db, BoundExistentialPredicates<'db>> { let type_alias_data = TypeAliasSignature::of(db, type_alias); - let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); + let resolver = type_alias.resolver(db); let interner = DbInterner::new_no_crate(db); + let generics = OnceCell::new(); let mut ctx = TyLoweringContext::new( db, &resolver, &type_alias_data.store, + ExpressionStoreOwnerId::Signature(type_alias.into()), type_alias.into(), + &generics, LifetimeElisionKind::AnonymousReportError, ); // FIXME: we should never create non-existential predicates in the first place @@ -2682,5 +2716,8 @@ pub(crate) fn associated_type_by_name_including_super_traits_allow_ambiguity<'db .get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref())) .skip_binder(); let interner = DbInterner::new_no_crate(db); - Some((assoc_type, EarlyBinder::bind(trait_args).instantiate(interner, trait_ref.args))) + Some(( + assoc_type, + EarlyBinder::bind(trait_args).instantiate(interner, trait_ref.args).skip_norm_wip(), + )) } diff --git a/crates/hir-ty/src/lower/diagnostics.rs b/crates/hir-ty/src/lower/diagnostics.rs index 009f047109..2565fb46ce 100644 --- a/crates/hir-ty/src/lower/diagnostics.rs +++ b/crates/hir-ty/src/lower/diagnostics.rs @@ -81,6 +81,11 @@ pub enum PathLoweringDiagnostic { def: GenericDefId, expected_count: u32, }, + /// Generic defaults are not allowed to refer to `Self`. + GenericDefaultRefersToSelf { + /// Index of the `Self` segment. + segment: u32, + }, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs index 4f70732178..ff9718af11 100644 --- a/crates/hir-ty/src/lower/path.rs +++ b/crates/hir-ty/src/lower/path.rs @@ -22,27 +22,26 @@ use rustc_type_ir::{ inherent::{GenericArgs as _, Region as _, Ty as _}, }; use smallvec::SmallVec; -use stdx::never; use crate::{ GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource, - PathLoweringDiagnostic, TyDefId, ValueTyDefId, - consteval::{unknown_const, unknown_const_as_generic}, + PathLoweringDiagnostic, Span, TyDefId, ValueTyDefId, db::HirDatabase, generics::{Generics, generics}, + infer::unify::InferenceTable, lower::{ - AssocTypeShorthandResolution, GenericPredicateSource, LifetimeElisionKind, - PathDiagnosticCallbackData, + AssocTypeShorthandResolution, ForbidParamsAfterReason, GenericPredicateSource, + LifetimeElisionKind, PathDiagnosticCallbackData, const_param_ty, }, next_solver::{ - Binder, Clause, Const, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, GenericArgs, - Predicate, ProjectionPredicate, Region, TraitRef, Ty, + AliasTermKind, Binder, Clause, Const, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, + GenericArgs, Predicate, ProjectionPredicate, Region, TraitRef, Ty, }, }; use super::{ ImplTraitLoweringMode, TyLoweringContext, - associated_type_by_name_including_super_traits_allow_ambiguity, const_param_ty_query, ty_query, + associated_type_by_name_including_super_traits_allow_ambiguity, ty_query, }; type CallbackData<'a> = @@ -85,6 +84,11 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { } } + #[track_caller] + pub(crate) fn expect_table(&mut self) -> &mut InferenceTable<'db> { + self.ctx.expect_table() + } + #[inline] #[cold] fn on_diagnostic(&mut self, diag: PathLoweringDiagnostic) { @@ -150,17 +154,18 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { // We need the original resolution to lower `Self::AssocTy` correctly res: Option<TypeNs>, infer_args: bool, + span: Span, ) -> (Ty<'db>, Option<TypeNs>) { let remaining_segments = self.segments.len() - self.current_segment_idx; match remaining_segments { 0 => (ty, res), 1 => { // resolve unselected assoc types - (self.select_associated_type(res, infer_args), None) + (self.select_associated_type(res, infer_args, span), None) } _ => { // FIXME report error (ambiguous associated type) - (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None) + (self.ctx.types.types.error, None) } } } @@ -170,6 +175,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { &mut self, resolution: TypeNs, infer_args: bool, + span: Span, ) -> (Ty<'db>, Option<TypeNs>) { let remaining_segments = self.segments.skip(self.current_segment_idx + 1); tracing::debug!(?remaining_segments); @@ -182,8 +188,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { 1 => { let trait_ref = self.lower_trait_ref_from_resolved_path( trait_, - Ty::new_error(self.ctx.interner, ErrorGuaranteed), + self.ctx.types.types.error, infer_args, + span, ); tracing::debug!(?trait_ref); self.skip_resolved_segment(); @@ -204,6 +211,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { infer_args, None, true, + span, ); let args = GenericArgs::new_from_iter( self.ctx.interner, @@ -223,7 +231,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { } None => { // FIXME: report error (associated type not found) - Ty::new_error(self.ctx.interner, ErrorGuaranteed) + self.ctx.types.types.error } } } @@ -231,11 +239,11 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { // Trait object type without dyn; this should be handled in upstream. See // `lower_path()`. stdx::never!("unexpected fully resolved trait path"); - Ty::new_error(self.ctx.interner, ErrorGuaranteed) + self.ctx.types.types.error } _ => { // FIXME report error (ambiguous associated type) - Ty::new_error(self.ctx.interner, ErrorGuaranteed) + self.ctx.types.types.error } }; return (ty, None); @@ -243,17 +251,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { TypeNs::GenericParam(param_id) => { let generics = self.ctx.generics(); let idx = generics.type_or_const_param_idx(param_id.into()); - match idx { - None => { - never!("no matching generics"); - Ty::new_error(self.ctx.interner, ErrorGuaranteed) - } - Some(idx) => { - let (pidx, _param) = generics.iter().nth(idx).unwrap(); - assert_eq!(pidx, param_id.into()); - self.ctx.type_param(param_id, idx as u32) - } - } + self.ctx.type_param(param_id, idx) } TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty(impl_id).skip_binder(), TypeNs::AdtSelfType(adt) => { @@ -261,19 +259,19 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { Ty::new_adt(self.ctx.interner, adt, args) } - TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args), - TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args), - TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args), + TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args, span), + TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args, span), + TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args, span), // FIXME: report error TypeNs::EnumVariantId(_) | TypeNs::ModuleId(_) => { - return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); + return (self.ctx.types.types.error, None); } }; tracing::debug!(?ty); self.skip_resolved_segment(); - self.lower_ty_relative_path(ty, Some(resolution), infer_args) + self.lower_ty_relative_path(ty, Some(resolution), infer_args, span) } /// This returns whether to keep the resolution (`true`) of throw it (`false`). @@ -299,9 +297,15 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { TypeNs::AdtSelfType(_) => { prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy); - if self.ctx.lowering_param_default.is_some() { - // Generic defaults are not allowed to refer to `Self`. - // FIXME: Emit an error. + if self.ctx.forbid_params_after.is_some() + && self.ctx.forbid_params_after_reason + == ForbidParamsAfterReason::LoweringParamDefault + { + // FIXME: Handle other reasons. + let segment = self.current_segment_u32(); + self.on_diagnostic(PathLoweringDiagnostic::GenericDefaultRefersToSelf { + segment, + }); return false; } } @@ -475,13 +479,17 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { } #[tracing::instrument(skip(self), ret)] - fn select_associated_type(&mut self, res: Option<TypeNs>, infer_args: bool) -> Ty<'db> { + fn select_associated_type( + &mut self, + res: Option<TypeNs>, + infer_args: bool, + span: Span, + ) -> Ty<'db> { let interner = self.ctx.interner; let db = self.ctx.db; - let def = self.ctx.def; + let def = self.ctx.generic_def; let segment = self.current_or_prev_segment; let assoc_name = segment.name; - let error_ty = || Ty::new_error(self.ctx.interner, ErrorGuaranteed); let (assoc_type, trait_args) = match res { Some(TypeNs::GenericParam(param)) => { let AssocTypeShorthandResolution::Resolved(assoc_type) = @@ -492,7 +500,8 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { assoc_name.clone(), ) else { - return error_ty(); + // FIXME: Emit an error. + return self.ctx.types.types.error; }; assoc_type .get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref())) @@ -500,9 +509,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { } Some(TypeNs::SelfType(impl_)) => { let Some(impl_trait) = db.impl_trait(impl_) else { - return error_ty(); + return self.ctx.types.types.error; }; - let impl_trait = impl_trait.instantiate_identity(); + let impl_trait = impl_trait.instantiate_identity().skip_norm_wip(); // Searching for `Self::Assoc` in `impl Trait for Type` is like searching for `Self::Assoc` in `Trait`. let AssocTypeShorthandResolution::Resolved(assoc_type) = super::resolve_type_param_assoc_type_shorthand( @@ -512,21 +521,27 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { assoc_name.clone(), ) else { - return error_ty(); + // FIXME: Emit an error. + return self.ctx.types.types.error; }; let (assoc_type, trait_args) = assoc_type .get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref())) .skip_binder(); - (assoc_type, EarlyBinder::bind(trait_args).instantiate(interner, impl_trait.args)) + ( + assoc_type, + EarlyBinder::bind(trait_args) + .instantiate(interner, impl_trait.args) + .skip_norm_wip(), + ) } - _ => return error_ty(), + _ => return self.ctx.types.types.error, }; // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent // generic params. It's inefficient to splice the `Substitution`s, so we may want // that method to optionally take parent `Substitution` as we already know them at // this point (`t.substitution`). - let substs = self.substs_from_path_segment(assoc_type.into(), infer_args, None, true); + let substs = self.substs_from_path_segment(assoc_type.into(), infer_args, None, true, span); let substs = GenericArgs::new_from_iter( interner, @@ -536,7 +551,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { Ty::new_projection_from_args(interner, assoc_type.into(), substs) } - fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { + fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool, span: Span) -> Ty<'db> { let generic_def = match typeable { TyDefId::BuiltinType(builtinty) => { return Ty::from_builtin_type(self.ctx.interner, builtinty); @@ -544,9 +559,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { TyDefId::AdtId(it) => it.into(), TyDefId::TypeAliasId(it) => it.into(), }; - let args = self.substs_from_path_segment(generic_def, infer_args, None, false); + let args = self.substs_from_path_segment(generic_def, infer_args, None, false, span); let ty = ty_query(self.ctx.db, typeable); - ty.instantiate(self.ctx.interner, args) + ty.instantiate(self.ctx.interner, args).skip_norm_wip() } /// Collect generic arguments from a path into a `Substs`. See also @@ -559,6 +574,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { resolved: ValueTyDefId, infer_args: bool, lowering_assoc_type_generics: bool, + span: Span, ) -> GenericArgs<'db> { let interner = self.ctx.interner; let prev_current_segment_idx = self.current_segment_idx; @@ -602,6 +618,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { infer_args, None, lowering_assoc_type_generics, + span, ); self.current_segment_idx = prev_current_segment_idx; self.current_or_prev_segment = prev_current_segment; @@ -614,6 +631,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { infer_args: bool, explicit_self_ty: Option<Ty<'db>>, lowering_assoc_type_generics: bool, + span: Span, ) -> GenericArgs<'db> { let old_lifetime_elision = self.ctx.lifetime_elision; @@ -638,7 +656,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, ); - return unknown_subst(self.ctx.interner, def); + return GenericArgs::error_for_item(self.ctx.interner, def.into()); } // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. @@ -654,6 +672,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { PathGenericsSource::Segment(self.current_segment_u32()), lowering_assoc_type_generics, self.ctx.lifetime_elision, + span, ); self.ctx.lifetime_elision = old_lifetime_elision; result @@ -668,10 +687,12 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { generics_source: PathGenericsSource, lowering_assoc_type_generics: bool, lifetime_elision: LifetimeElisionKind<'db>, + span: Span, ) -> GenericArgs<'db> { struct LowererCtx<'a, 'b, 'c, 'db> { ctx: &'a mut PathLoweringContext<'b, 'c, 'db>, generics_source: PathGenericsSource, + span: Span, } impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, '_, 'db> { @@ -725,7 +746,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }; self.ctx .ctx - .lower_const(konst, const_param_ty_query(self.ctx.ctx.db, const_id)) + .lower_const(konst, const_param_ty(self.ctx.ctx.db, const_id)) .into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), @@ -734,12 +755,13 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { fn provided_type_like_const( &mut self, + type_ref: TypeRefId, const_ty: Ty<'db>, arg: TypeLikeConst<'_>, ) -> Const<'db> { match arg { TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty), - TypeLikeConst::Infer => unknown_const(const_ty), + TypeLikeConst::Infer => self.ctx.ctx.next_const_var(type_ref.into()), } } @@ -750,17 +772,18 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { param: GenericParamDataRef<'_>, infer_args: bool, preceding_args: &[GenericArg<'db>], + had_count_error: bool, ) -> GenericArg<'db> { - let default = - || { - self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map( - |default| default.instantiate(self.ctx.ctx.interner, preceding_args), - ) - }; + let default = || { + self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(|default| { + default.instantiate(self.ctx.ctx.interner, preceding_args).skip_norm_wip() + }) + }; + // If `!infer_args`, we've already emitted an error, so put a dummy span. + let span = if !infer_args || had_count_error { Span::Dummy } else { self.span }; match param { GenericParamDataRef::LifetimeParamData(_) => { - Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) - .into() + self.ctx.ctx.next_region_var(span).into() } GenericParamDataRef::TypeParamData(param) => { if !infer_args @@ -769,7 +792,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { { return default; } - Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() + self.ctx.ctx.next_ty_var(span).into() } GenericParamDataRef::ConstParamData(param) => { if !infer_args @@ -778,10 +801,10 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { { return default; } - let GenericParamId::ConstParamId(const_id) = param_id else { + let GenericParamId::ConstParamId(_) = param_id else { unreachable!("non-const param ID for const param"); }; - unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) + self.ctx.ctx.next_const_var(span).into() } } } @@ -791,13 +814,8 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { GenericParamId::TypeParamId(_) => { Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() } - GenericParamId::ConstParamId(const_id) => { - unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) - } - GenericParamId::LifetimeParamId(_) => { - Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) - .into() - } + GenericParamId::ConstParamId(_) => self.ctx.ctx.types.consts.error.into(), + GenericParamId::LifetimeParamId(_) => self.ctx.ctx.types.regions.error.into(), } } @@ -841,7 +859,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { lifetime_elision, lowering_assoc_type_generics, explicit_self_ty, - &mut LowererCtx { ctx: self, generics_source }, + &mut LowererCtx { ctx: self, generics_source, span }, ) } @@ -850,8 +868,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { resolved: TraitId, explicit_self_ty: Ty<'db>, infer_args: bool, + span: Span, ) -> TraitRef<'db> { - let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args); + let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args, span); TraitRef::new_from_args(self.ctx.interner, resolved.into(), args) } @@ -860,13 +879,21 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { resolved: TraitId, explicit_self_ty: Ty<'db>, infer_args: bool, + span: Span, ) -> GenericArgs<'db> { - self.substs_from_path_segment(resolved.into(), infer_args, Some(explicit_self_ty), false) + self.substs_from_path_segment( + resolved.into(), + infer_args, + Some(explicit_self_ty), + false, + span, + ) } pub(super) fn assoc_type_bindings_from_type_bound<'c>( mut self, trait_ref: TraitRef<'db>, + span: Span, ) -> Option<impl Iterator<Item = (Clause<'db>, GenericPredicateSource)> + use<'a, 'b, 'c, 'db>> { let interner = self.ctx.interner; @@ -898,14 +925,18 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }, false, this.ctx.lifetime_elision, + span, ) }); let args = GenericArgs::new_from_iter( interner, super_trait_args.iter().chain(args.iter().skip(super_trait_args.len())), ); - let projection_term = - AliasTerm::new_from_args(interner, associated_ty.into(), args); + let projection_term = AliasTerm::new_from_args( + interner, + AliasTermKind::ProjectionTy { def_id: associated_ty.into() }, + args, + ); let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity( binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); @@ -1000,8 +1031,12 @@ pub(crate) trait GenericArgsLowerer<'db> { arg: &HirGenericArg, ) -> GenericArg<'db>; - fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>) - -> Const<'db>; + fn provided_type_like_const( + &mut self, + type_ref: TypeRefId, + const_ty: Ty<'db>, + arg: TypeLikeConst<'_>, + ) -> Const<'db>; fn inferred_kind( &mut self, @@ -1010,6 +1045,7 @@ pub(crate) trait GenericArgsLowerer<'db> { param: GenericParamDataRef<'_>, infer_args: bool, preceding_args: &[GenericArg<'db>], + had_count_error: bool, ) -> GenericArg<'db>; fn parent_arg(&mut self, param_idx: u32, param_id: GenericParamId) -> GenericArg<'db>; @@ -1178,7 +1214,14 @@ pub(crate) fn substs_from_args_and_bindings<'db>( ctx.provided_kind(self_param_id, self_param, self_ty) } else { explicit_self_ty.map(|it| it.into()).unwrap_or_else(|| { - ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs) + ctx.inferred_kind( + def, + self_param_id, + self_param, + infer_args, + &substs, + had_count_error, + ) }) }; params.next(); @@ -1197,7 +1240,14 @@ pub(crate) fn substs_from_args_and_bindings<'db>( { // Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here // we will handle it as if it was specified, instead of inferring it. - substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); + substs.push(ctx.inferred_kind( + def, + param_id, + param, + infer_args, + &substs, + had_count_error, + )); params.next(); } (HirGenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_)) @@ -1213,7 +1263,14 @@ pub(crate) fn substs_from_args_and_bindings<'db>( ) => { // We expected a lifetime argument, but got a type or const // argument. That means we're inferring the lifetime. - substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); + substs.push(ctx.inferred_kind( + def, + param_id, + param, + infer_args, + &substs, + had_count_error, + )); params.next(); force_infer_lt = Some((arg_idx as u32, param_id)); } @@ -1222,8 +1279,9 @@ pub(crate) fn substs_from_args_and_bindings<'db>( let GenericParamId::ConstParamId(param_id) = param_id else { panic!("unmatching param kinds"); }; - let const_ty = const_param_ty_query(db, param_id); - substs.push(ctx.provided_type_like_const(const_ty, konst).into()); + let const_ty = const_param_ty(db, param_id); + substs + .push(ctx.provided_type_like_const(*type_ref, const_ty, konst).into()); args.next(); params.next(); } else { @@ -1281,7 +1339,14 @@ pub(crate) fn substs_from_args_and_bindings<'db>( | LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true } | LifetimeElisionKind::AnonymousReportError => { assert!(had_count_error); - ctx.inferred_kind(def, param_id, param, infer_args, &substs) + ctx.inferred_kind( + def, + param_id, + param, + infer_args, + &substs, + had_count_error, + ) } LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { Region::new_static(interner).into() @@ -1291,11 +1356,18 @@ pub(crate) fn substs_from_args_and_bindings<'db>( | LifetimeElisionKind::Infer => { // FIXME: With `AnonymousCreateParameter`, we need to create a new lifetime parameter here // (but this will probably be done in hir-def lowering instead). - ctx.inferred_kind(def, param_id, param, infer_args, &substs) + ctx.inferred_kind( + def, + param_id, + param, + infer_args, + &substs, + had_count_error, + ) } } } else { - ctx.inferred_kind(def, param_id, param, infer_args, &substs) + ctx.inferred_kind(def, param_id, param, infer_args, &substs, had_count_error) }; substs.push(param); params.next(); @@ -1324,17 +1396,3 @@ fn type_looks_like_const( _ => None, } } - -fn unknown_subst<'db>(interner: DbInterner<'db>, def: impl Into<GenericDefId>) -> GenericArgs<'db> { - let params = generics(interner.db(), def.into()); - GenericArgs::new_from_iter( - interner, - params.iter_id().map(|id| match id { - GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(), - GenericParamId::ConstParamId(id) => { - unknown_const_as_generic(const_param_ty_query(interner.db(), id)) - } - GenericParamId::LifetimeParamId(_) => Region::error(interner).into(), - }), - ) -} diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 68c4833d81..5e90e371fc 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -23,8 +23,8 @@ use hir_def::{ nameres::{DefMap, block_def_map, crate_def_map}, resolver::Resolver, signatures::{ConstSignature, FunctionSignature}, + unstable_features::UnstableFeatures, }; -use intern::{Symbol, sym}; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ TypeVisitableExt, @@ -35,13 +35,13 @@ use stdx::impl_from; use triomphe::Arc; use crate::{ - all_super_traits, + Span, all_super_traits, db::HirDatabase, infer::{InferenceContext, unify::InferenceTable}, lower::GenericPredicates, next_solver::{ AnyImplId, Binder, ClauseKind, DbInterner, FnSig, GenericArgs, ParamEnv, PredicateKind, - SimplifiedType, SolverDefId, TraitRef, Ty, TyKind, TypingMode, + SimplifiedType, SolverDefId, TraitRef, Ty, TyKind, TypingMode, Unnormalized, infer::{ BoundRegionConversionTime, DbInternerInferExt, InferCtxt, InferOk, select::ImplSource, @@ -57,32 +57,15 @@ pub use self::probe::{ Candidate, CandidateKind, CandidateStep, CandidateWithPrivate, Mode, Pick, PickKind, }; -#[derive(Debug, Clone)] -pub struct MethodResolutionUnstableFeatures { - arbitrary_self_types: bool, - arbitrary_self_types_pointers: bool, - supertrait_item_shadowing: bool, -} - -impl MethodResolutionUnstableFeatures { - pub fn from_def_map(def_map: &DefMap) -> Self { - Self { - arbitrary_self_types: def_map.is_unstable_feature_enabled(&sym::arbitrary_self_types), - arbitrary_self_types_pointers: def_map - .is_unstable_feature_enabled(&sym::arbitrary_self_types_pointers), - supertrait_item_shadowing: def_map - .is_unstable_feature_enabled(&sym::supertrait_item_shadowing), - } - } -} - pub struct MethodResolutionContext<'a, 'db> { pub infcx: &'a InferCtxt<'db>, pub resolver: &'a Resolver<'db>, pub param_env: ParamEnv<'db>, pub traits_in_scope: &'a FxHashSet<TraitId>, pub edition: Edition, - pub unstable_features: &'a MethodResolutionUnstableFeatures, + pub features: &'a UnstableFeatures, + pub call_span: Span, + pub receiver_span: Span, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] @@ -152,7 +135,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { receiver: ExprId, call_expr: ExprId, ) -> Result<(MethodCallee<'db>, bool), MethodError<'db>> { - let (pick, is_visible) = match self.lookup_probe(name, self_ty) { + let (pick, is_visible) = match self.lookup_probe(call_expr, receiver, name, self_ty) { Ok(it) => (it, true), Err(MethodError::PrivateMatch(it)) => { // FIXME: Report error. @@ -177,10 +160,12 @@ impl<'a, 'db> InferenceContext<'a, 'db> { #[instrument(level = "debug", skip(self))] pub(crate) fn lookup_probe( &self, + call_expr: ExprId, + receiver: ExprId, method_name: Name, self_ty: Ty<'db>, ) -> probe::PickResult<'db> { - self.with_method_resolution(|ctx| { + self.with_method_resolution(call_expr.into(), receiver.into(), |ctx| { let pick = ctx.probe_for_name(probe::Mode::MethodCall, method_name, self_ty)?; Ok(pick) }) @@ -188,6 +173,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { pub(crate) fn with_method_resolution<R>( &self, + call_span: Span, + receiver_span: Span, f: impl FnOnce(&MethodResolutionContext<'_, 'db>) -> R, ) -> R { let traits_in_scope = self.get_traits_in_scope(); @@ -201,7 +188,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { param_env: self.table.param_env, traits_in_scope, edition: self.edition, - unstable_features: &self.unstable_features, + features: self.features, + call_span, + receiver_span, }; f(&ctx) } @@ -234,8 +223,8 @@ impl<'db> InferenceTable<'db> { pub(super) fn lookup_method_for_operator( &self, cause: ObligationCause, - method_name: Symbol, trait_def_id: TraitId, + method_item: FunctionId, self_ty: Ty<'db>, opt_rhs_ty: Option<Ty<'db>>, treat_opaques: TreatNotYetDefinedOpaques, @@ -258,7 +247,7 @@ impl<'db> InferenceTable<'db> { // FIXME: We should stop passing `None` for the failure case // when probing for call exprs. I.e. `opt_rhs_ty` should always // be set when it needs to be. - self.next_var_for_param(param_id) + self.var_for_def(param_id, cause.span()) } } }, @@ -288,13 +277,6 @@ impl<'db> InferenceTable<'db> { // Trait must have a method named `m_name` and it should not have // type parameters or early-bound regions. let interner = self.interner(); - // We use `Ident::with_dummy_span` since no built-in operator methods have - // any macro-specific hygiene, so the span's context doesn't really matter. - let Some(method_item) = - trait_def_id.trait_items(self.db).method_by_name(&Name::new_symbol_root(method_name)) - else { - panic!("expected associated item for operator trait") - }; let def_id = method_item; @@ -307,11 +289,16 @@ impl<'db> InferenceTable<'db> { // N.B., instantiate late-bound regions before normalizing the // function signature so that normalization does not need to deal // with bound regions. - let fn_sig = - self.db.callable_item_signature(method_item.into()).instantiate(interner, args); let fn_sig = self - .infer_ctxt - .instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, fn_sig); + .db + .callable_item_signature(method_item.into()) + .instantiate(interner, args) + .skip_norm_wip(); + let fn_sig = self.infer_ctxt.instantiate_binder_with_fresh_vars( + cause.span(), + BoundRegionConversionTime::FnCall, + fn_sig, + ); // Register obligations for the parameters. This will include the // `Self` parameter, which in turn has a bound of the main trait, @@ -323,8 +310,8 @@ impl<'db> InferenceTable<'db> { // any late-bound regions appearing in its bounds. let bounds = GenericPredicates::query_all(self.db, method_item.into()); let bounds = clauses_as_obligations( - bounds.iter_instantiated(interner, args.as_slice()), - ObligationCause::new(), + bounds.iter_instantiated(interner, args.as_slice()).map(Unnormalized::skip_norm_wip), + cause, self.param_env, ); @@ -338,7 +325,7 @@ impl<'db> InferenceTable<'db> { for ty in fn_sig.inputs_and_output { obligations.push(Obligation::new( interner, - obligation.cause.clone(), + obligation.cause, self.param_env, Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(ty.into()))), )); @@ -612,7 +599,7 @@ impl InherentImpls { for impl_id in module_data.scope.inherent_impls() { let interner = DbInterner::new_no_crate(db); let self_ty = db.impl_self_ty(impl_id); - let self_ty = self_ty.instantiate_identity(); + let self_ty = self_ty.instantiate_identity().skip_norm_wip(); if let Some(self_ty) = simplify_type(interner, self_ty, TreatParams::InstantiateWithInfer) { @@ -727,7 +714,7 @@ impl TraitImpls { for (_module_id, module_data) in def_map.modules() { for impl_id in module_data.scope.trait_impls() { let trait_ref = match db.impl_trait(impl_id) { - Some(tr) => tr.instantiate_identity(), + Some(tr) => tr.instantiate_identity().skip_norm_wip(), None => continue, }; // Reservation impls should be ignored during trait resolution, so we never need diff --git a/crates/hir-ty/src/method_resolution/confirm.rs b/crates/hir-ty/src/method_resolution/confirm.rs index 94c70c29f7..c425e69dc5 100644 --- a/crates/hir-ty/src/method_resolution/confirm.rs +++ b/crates/hir-ty/src/method_resolution/confirm.rs @@ -2,9 +2,10 @@ //! is valid and registering all obligations. use hir_def::{ - FunctionId, GenericDefId, GenericParamId, ItemContainerId, TraitId, + FunctionId, GenericDefId, GenericParamId, TraitId, expr_store::path::{GenericArg as HirGenericArg, GenericArgs as HirGenericArgs}, hir::{ExprId, generics::GenericParamDataRef}, + type_ref::TypeRefId, }; use rustc_type_ir::{ TypeFoldable, @@ -15,9 +16,9 @@ use tracing::debug; use crate::{ Adjust, Adjustment, AutoBorrow, IncorrectGenericsLenKind, InferenceDiagnostic, - LifetimeElisionKind, PointerCast, + LifetimeElisionKind, PointerCast, Span, db::HirDatabase, - infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch}, + infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext}, lower::{ GenericPredicates, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, @@ -26,7 +27,7 @@ use crate::{ next_solver::{ Binder, Clause, ClauseKind, Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, FnSig, GenericArg, GenericArgs, ParamConst, PolyExistentialTraitRef, PolyTraitRef, Region, - TraitRef, Ty, TyKind, + TraitRef, Ty, TyKind, Unnormalized, infer::{ BoundRegionConversionTime, InferCtxt, traits::{ObligationCause, PredicateObligation}, @@ -38,7 +39,7 @@ use crate::{ struct ConfirmContext<'a, 'b, 'db> { ctx: &'a mut InferenceContext<'b, 'db>, candidate: FunctionId, - expr: ExprId, + call_expr: ExprId, } #[derive(Debug)] @@ -73,9 +74,9 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { fn new( ctx: &'a mut InferenceContext<'b, 'db>, candidate: FunctionId, - expr: ExprId, + call_expr: ExprId, ) -> ConfirmContext<'a, 'b, 'db> { - ConfirmContext { ctx, candidate, expr } + ConfirmContext { ctx, candidate, call_expr } } #[inline] @@ -136,7 +137,8 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { ); let illegal_sized_bound = self.predicates_require_illegal_sized_bound( GenericPredicates::query_all(self.db(), self.candidate.into()) - .iter_instantiated(self.interner(), filler_args.as_slice()), + .iter_instantiated(self.interner(), filler_args.as_slice()) + .map(Unnormalized::skip_norm_wip), ); // Unify the (adjusted) self type with what the method expects. @@ -180,7 +182,8 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { ) -> (Ty<'db>, Box<[Adjustment]>) { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. - let mut autoderef = self.ctx.table.autoderef_with_tracking(unadjusted_self_ty); + let mut autoderef = + self.ctx.table.autoderef_with_tracking(unadjusted_self_ty, self.call_expr.into()); let Some((mut target, n)) = autoderef.nth(pick.autoderefs) else { return (Ty::new_error(self.interner(), ErrorGuaranteed), Box::new([])); }; @@ -190,7 +193,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { self.ctx.table.register_infer_ok(autoderef.adjust_steps_as_infer_ok()); match pick.autoref_or_ptr_adjustment { Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { - let region = self.infcx().next_region_var(); + let region = self.infcx().next_region_var(self.call_expr.into()); // Type we're wrapping in a reference, used later for unsizing let base_ty = target; @@ -254,7 +257,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { ) -> GenericArgs<'db> { match pick.kind { probe::InherentImplPick(impl_def_id) => { - self.infcx().fresh_args_for_item(impl_def_id.into()) + self.infcx().fresh_args_for_item(self.call_expr.into(), impl_def_id.into()) } probe::ObjectPick(trait_def_id) => { @@ -296,7 +299,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { // the process we will unify the transformed-self-type // of the method with the actual type in order to // unify some of these variables. - self.infcx().fresh_args_for_item(trait_def_id.into()) + self.infcx().fresh_args_for_item(self.call_expr.into(), trait_def_id.into()) } probe::WhereClausePick(poly_trait_ref) => { @@ -316,12 +319,11 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { // yield an object-type (e.g., `&Object` or `Box<Object>` // etc). - let mut autoderef = self.ctx.table.autoderef(self_ty); + let mut autoderef = self.ctx.table.autoderef(self_ty, self.call_expr.into()); // We don't need to gate this behind arbitrary self types // per se, but it does make things a bit more gated. - if self.ctx.unstable_features.arbitrary_self_types - || self.ctx.unstable_features.arbitrary_self_types_pointers + if self.ctx.features.arbitrary_self_types || self.ctx.features.arbitrary_self_types_pointers { autoderef = autoderef.use_receiver_trait(); } @@ -401,8 +403,8 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { let GenericParamId::ConstParamId(const_id) = param_id else { unreachable!("non-const param ID for const param"); }; - let const_ty = self.ctx.db.const_param_ty_ns(const_id); - self.ctx.make_body_const(*konst, const_ty).into() + let const_ty = self.ctx.db.const_param_ty(const_id); + self.ctx.create_body_anon_const(konst.expr, const_ty, false).into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), } @@ -410,12 +412,13 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { fn provided_type_like_const( &mut self, - const_ty: Ty<'db>, + _type_ref: TypeRefId, + _const_ty: Ty<'db>, arg: TypeLikeConst<'_>, ) -> Const<'db> { match arg { - TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty), - TypeLikeConst::Infer => self.ctx.table.next_const_var(), + TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path), + TypeLikeConst::Infer => self.ctx.table.next_const_var(Span::Dummy), } } @@ -424,12 +427,15 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { _def: GenericDefId, param_id: GenericParamId, _param: GenericParamDataRef<'_>, - _infer_args: bool, + infer_args: bool, _preceding_args: &[GenericArg<'db>], + had_count_error: bool, ) -> GenericArg<'db> { // Always create an inference var, even when `infer_args == false`. This helps with diagnostics, // and I think it's also required in the presence of `impl Trait` (that must be inferred). - self.ctx.table.next_var_for_param(param_id) + let span = + if !infer_args || had_count_error { Span::Dummy } else { self.expr.into() }; + self.ctx.table.var_for_def(param_id, span) } fn parent_arg(&mut self, param_idx: u32, _param_id: GenericParamId) -> GenericArg<'db> { @@ -463,7 +469,11 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { LifetimeElisionKind::Infer, false, None, - &mut LowererCtx { ctx: self.ctx, expr: self.expr, parent_args: parent_args.as_slice() }, + &mut LowererCtx { + ctx: self.ctx, + expr: self.call_expr, + parent_args: parent_args.as_slice(), + }, ) } @@ -477,17 +487,14 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { "unify_receivers: self_ty={:?} method_self_ty={:?} pick={:?}", self_ty, method_self_ty, pick ); - let cause = ObligationCause::new(); + let cause = ObligationCause::new(self.call_expr); match self.ctx.table.at(&cause).sup(method_self_ty, self_ty) { Ok(infer_ok) => { self.ctx.table.register_infer_ok(infer_ok); } Err(_) => { - if self.ctx.unstable_features.arbitrary_self_types { - self.ctx.result.type_mismatches.get_or_insert_default().insert( - self.expr.into(), - TypeMismatch { expected: method_self_ty.store(), actual: self_ty.store() }, - ); + if self.ctx.features.arbitrary_self_types { + self.ctx.emit_type_mismatch(self.call_expr.into(), method_self_ty, self_ty); } } } @@ -509,13 +516,17 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { let def_id = self.candidate; let method_predicates = clauses_as_obligations( GenericPredicates::query_all(self.db(), def_id.into()) - .iter_instantiated(self.interner(), all_args), - ObligationCause::new(), + .iter_instantiated(self.interner(), all_args) + .map(Unnormalized::skip_norm_wip), + ObligationCause::new(self.call_expr), self.ctx.table.param_env, ); - let sig = - self.db().callable_item_signature(def_id.into()).instantiate(self.interner(), all_args); + let sig = self + .db() + .callable_item_signature(def_id.into()) + .instantiate(self.interner(), all_args) + .skip_norm_wip(); debug!("type scheme instantiated, sig={:?}", sig); let sig = self.instantiate_binder_with_fresh_vars(sig); @@ -536,13 +547,13 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { // this is a projection from a trait reference, so we have to // make sure that the trait reference inputs are well-formed. - self.ctx.table.add_wf_bounds(all_args); + self.ctx.table.add_wf_bounds(self.call_expr.into(), all_args); // the function type must also be well-formed (this is not // implied by the args being well-formed because of inherent // impls and late-bound regions - see issue #28609). for ty in sig.inputs_and_output { - self.ctx.table.register_wf_obligation(ty.into(), ObligationCause::new()); + self.ctx.table.register_wf_obligation(ty.into(), ObligationCause::new(self.call_expr)); } } @@ -570,9 +581,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { fn check_for_illegal_method_calls(&self) { // Disallow calls to the method `drop` defined in the `Drop` trait. - if let ItemContainerId::TraitId(trait_def_id) = self.candidate.loc(self.db()).container - && self.ctx.lang_items.Drop.is_some_and(|drop_trait| drop_trait == trait_def_id) - { + if self.ctx.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == self.candidate) { // FIXME: Report an error. } } @@ -610,6 +619,10 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { where T: TypeFoldable<DbInterner<'db>> + Copy, { - self.infcx().instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, value) + self.infcx().instantiate_binder_with_fresh_vars( + self.call_expr.into(), + BoundRegionConversionTime::FnCall, + value, + ) } } diff --git a/crates/hir-ty/src/method_resolution/probe.rs b/crates/hir-ty/src/method_resolution/probe.rs index 3604076ccd..4b2f0cfd70 100644 --- a/crates/hir-ty/src/method_resolution/probe.rs +++ b/crates/hir-ty/src/method_resolution/probe.rs @@ -15,7 +15,7 @@ use rustc_type_ir::{ InferTy, TypeVisitableExt, Upcast, Variance, elaborate::{self, supertrait_def_ids}, fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}, - inherent::{AdtDef as _, BoundExistentialPredicates as _, IntoKind, Ty as _}, + inherent::{BoundExistentialPredicates as _, IntoKind, Ty as _}, }; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -32,7 +32,7 @@ use crate::{ }, next_solver::{ Binder, Canonical, ClauseKind, DbInterner, FnSig, GenericArg, GenericArgs, Goal, ParamEnv, - PolyTraitRef, Predicate, Region, SimplifiedType, TraitRef, Ty, TyKind, + PolyTraitRef, Predicate, Region, SimplifiedType, TraitRef, Ty, TyKind, Unnormalized, infer::{ BoundRegionConversionTime, InferCtxt, InferOk, canonical::{QueryResponse, canonicalizer::OriginalQueryValues}, @@ -284,7 +284,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { // special handling for this "trivial case" is a good idea. let infcx = self.infcx; - let (self_ty, var_values) = infcx.instantiate_canonical(&query_input); + let (self_ty, var_values) = + infcx.instantiate_canonical(self.call_span, &query_input); debug!(?self_ty, ?query_input, "probe_op: Mode::Path"); let prev_opaque_entries = self.infcx.inner.borrow_mut().opaque_types().num_entries(); @@ -315,7 +316,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { // ambiguous. if let Some(bad_ty) = &steps.opt_bad_ty { if bad_ty.reached_raw_pointer - && !self.unstable_features.arbitrary_self_types_pointers + && !self.features.arbitrary_self_types_pointers && self.edition.at_least_2018() { // this case used to be allowed by the compiler, @@ -338,7 +339,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { let ty = self .infcx .instantiate_query_response_and_region_obligations( - &ObligationCause::new(), + &ObligationCause::new(self.receiver_span), self.param_env, &orig_values, ty, @@ -380,7 +381,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { // chain to support recursive calls. We do error if the final // infer var is not an opaque. let infcx = self.infcx; - let (self_ty, inference_vars) = infcx.instantiate_canonical(self_ty); + let (self_ty, inference_vars) = + infcx.instantiate_canonical(self.receiver_span, self_ty); let prev_opaque_entries = infcx.inner.borrow_mut().opaque_types().num_entries(); let self_ty_is_opaque = |ty: Ty<'_>| { @@ -401,18 +403,20 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> { // converted to, in order to find out which of those methods might actually // be callable. let mut autoderef_via_deref = - Autoderef::new(infcx, self.param_env, self_ty).include_raw_pointers(); + Autoderef::new(infcx, self.param_env, self_ty, self.receiver_span) + .include_raw_pointers(); let mut reached_raw_pointer = false; - let arbitrary_self_types_enabled = self.unstable_features.arbitrary_self_types - || self.unstable_features.arbitrary_self_types_pointers; + let arbitrary_self_types_enabled = + self.features.arbitrary_self_types || self.features.arbitrary_self_types_pointers; let (mut steps, reached_recursion_limit) = if arbitrary_self_types_enabled { let reachable_via_deref = autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false)); - let mut autoderef_via_receiver = Autoderef::new(infcx, self.param_env, self_ty) - .include_raw_pointers() - .use_receiver_trait(); + let mut autoderef_via_receiver = + Autoderef::new(infcx, self.param_env, self_ty, self.receiver_span) + .include_raw_pointers() + .use_receiver_trait(); let steps = autoderef_via_receiver .by_ref() .zip(reachable_via_deref) @@ -613,7 +617,7 @@ impl<'db> ProbeChoice<'db> for ProbeForNameChoice<'db> { // We collapse to a subtrait pick *after* filtering unstable candidates // to make sure we don't prefer a unstable subtrait method over a stable // supertrait method. - if this.ctx.unstable_features.supertrait_item_shadowing + if this.ctx.features.supertrait_item_shadowing && let Some(pick) = this.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates) { @@ -921,7 +925,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { // will still match the original object type, but it won't pollute our // type variables in any form, so just do that! let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) = - self.infcx().instantiate_canonical(self_ty); + self.infcx().instantiate_canonical(self.ctx.call_span, self_ty); self.assemble_inherent_candidates_from_object(generalized_self_ty); self.assemble_inherent_impl_candidates_for_type( @@ -935,7 +939,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { } } TyKind::Adt(def, _) => { - let def_id = def.def_id().0; + let def_id = def.def_id(); self.assemble_inherent_impl_candidates_for_type( &SimplifiedType::Adt(def_id.into()), receiver_steps, @@ -1117,7 +1121,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { #[instrument(level = "debug", skip(self))] fn assemble_extension_candidates_for_trait(&mut self, trait_def_id: TraitId) { - let trait_args = self.infcx().fresh_args_for_item(trait_def_id.into()); + let trait_args = self.infcx().fresh_args_for_item(self.ctx.call_span, trait_def_id.into()); let trait_ref = TraitRef::new_from_args(self.interner(), trait_def_id.into(), trait_args); self.with_trait_item(trait_def_id, |this, item| { @@ -1183,8 +1187,8 @@ impl<'a, 'db> ProbeContext<'a, 'db, ProbeForNameChoice<'db>> { // The errors emitted by this function are part of // the arbitrary self types work, and should not impact // other users. - if !self.ctx.unstable_features.arbitrary_self_types - && !self.ctx.unstable_features.arbitrary_self_types_pointers + if !self.ctx.features.arbitrary_self_types + && !self.ctx.features.arbitrary_self_types_pointers { return Ok(()); } @@ -1268,7 +1272,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self .infcx() .instantiate_query_response_and_region_obligations( - &ObligationCause::new(), + &ObligationCause::new(self.ctx.receiver_span), self.param_env(), self.orig_steps_var_values, &step.self_ty, @@ -1495,8 +1499,12 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { &self, trait_ref: TraitRef<'db>, ) -> SelectionResult<'db, Selection<'db>> { - let obligation = - Obligation::new(self.interner(), ObligationCause::new(), self.param_env(), trait_ref); + let obligation = Obligation::new( + self.interner(), + ObligationCause::new(self.ctx.call_span), + self.param_env(), + trait_ref, + ); self.infcx().select(&obligation) } @@ -1510,6 +1518,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { } TraitCandidate(trait_ref) => self.infcx().probe(|_| { let trait_ref = self.infcx().instantiate_binder_with_fresh_vars( + self.ctx.call_span, BoundRegionConversionTime::FnCall, trait_ref, ); @@ -1522,7 +1531,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { // up with the `self` parameter of the method. let _ = self .infcx() - .at(&ObligationCause::dummy(), self.param_env()) + .at(&ObligationCause::new(self.ctx.call_span), self.param_env()) .sup(xform_self_ty, self_ty); match self.select_trait_candidate(trait_ref) { Ok(Some(ImplSource::UserDefined(ref impl_data))) => { @@ -1553,7 +1562,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { ) -> ProbeResult { self.infcx().probe(|_| { let mut result = ProbeResult::Match; - let cause = &ObligationCause::new(); + let cause = &ObligationCause::new(self.ctx.call_span); let mut ocx = ObligationCtxt::new(self.infcx()); // Subtle: we're not *really* instantiating the current self type while @@ -1574,9 +1583,13 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { match probe.kind { InherentImplCandidate { impl_def_id, .. } => { - let impl_args = self.infcx().fresh_args_for_item(impl_def_id.into()); - let impl_ty = - self.db().impl_self_ty(impl_def_id).instantiate(self.interner(), impl_args); + let impl_args = + self.infcx().fresh_args_for_item(self.ctx.call_span, impl_def_id.into()); + let impl_ty = self + .db() + .impl_self_ty(impl_def_id) + .instantiate(self.interner(), impl_args) + .skip_norm_wip(); (xform_self_ty, xform_ret_ty) = self.xform_self_ty(probe.item, impl_ty, impl_args.as_slice()); match ocx.relate( @@ -1595,8 +1608,10 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { // Check whether the impl imposes obligations we have to worry about. let impl_bounds = GenericPredicates::query_all(self.db(), impl_def_id.into()); let impl_bounds = clauses_as_obligations( - impl_bounds.iter_instantiated(self.interner(), impl_args.as_slice()), - ObligationCause::new(), + impl_bounds + .iter_instantiated(self.interner(), impl_args.as_slice()) + .map(Unnormalized::skip_norm_wip), + ObligationCause::new(self.ctx.call_span), self.param_env(), ); // Convert the bounds into obligations. @@ -1632,6 +1647,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { } let trait_ref = self.infcx().instantiate_binder_with_fresh_vars( + self.ctx.call_span, BoundRegionConversionTime::FnCall, poly_trait_ref, ); @@ -1655,7 +1671,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { } let obligation = Obligation::new( self.interner(), - cause.clone(), + *cause, self.param_env(), Binder::dummy(trait_ref), ); @@ -1667,6 +1683,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { } ObjectCandidate(poly_trait_ref) | WhereClauseCandidate(poly_trait_ref) => { let trait_ref = self.infcx().instantiate_binder_with_fresh_vars( + self.ctx.call_span, BoundRegionConversionTime::FnCall, poly_trait_ref, ); @@ -1728,7 +1745,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { if let Some(xform_ret_ty) = xform_ret_ty { ocx.register_obligation(Obligation::new( self.interner(), - cause.clone(), + *cause, self.param_env(), ClauseKind::WellFormed(xform_ret_ty.into()), )); @@ -1799,7 +1816,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { // reachable. In this case we don't care about opaque // types there. let Ok(ok) = self.infcx().instantiate_query_response_and_region_obligations( - &ObligationCause::new(), + &ObligationCause::new(self.ctx.receiver_span), self.param_env(), self.orig_steps_var_values, &step.self_ty, @@ -2029,8 +2046,12 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { // In general, during probe we erase regions. Region::new_erased(self.interner()).into() } - GenericParamId::TypeParamId(_) => self.infcx().next_ty_var().into(), - GenericParamId::ConstParamId(_) => self.infcx().next_const_var().into(), + GenericParamId::TypeParamId(_) => { + self.infcx().next_ty_var(self.ctx.call_span).into() + } + GenericParamId::ConstParamId(_) => { + self.infcx().next_const_var(self.ctx.call_span).into() + } } } }, @@ -2038,7 +2059,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { fn_sig.instantiate(self.interner(), args) }; - self.interner().instantiate_bound_regions_with_erased(xform_fn_sig) + self.interner().instantiate_bound_regions_with_erased(xform_fn_sig.skip_norm_wip()) } fn with_impl_item(&mut self, def_id: ImplId, callback: impl FnMut(&mut Self, CandidateId)) { diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index a8e06f3a2b..5f61b1defb 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -5,7 +5,7 @@ use std::{collections::hash_map::Entry, fmt::Display, iter}; use base_db::Crate; use either::Either; use hir_def::{ - DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId, + FieldId, StaticId, TupleFieldId, UnionId, VariantId, hir::{BindingId, Expr, ExprId, Ordering, PatId}, }; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; @@ -16,7 +16,7 @@ use smallvec::{SmallVec, smallvec}; use stdx::{impl_from, never}; use crate::{ - CallableDefId, InferenceResult, MemoryMap, + CallableDefId, InferBodyId, InferenceResult, MemoryMap, consteval::usize_const, db::{HirDatabase, InternedClosureId}, display::{DisplayTarget, HirDisplay}, @@ -47,9 +47,6 @@ pub use monomorphization::{ monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query, }; -pub(crate) use lower::mir_body_cycle_result; -pub(crate) use monomorphization::monomorphized_mir_body_cycle_result; - use super::consteval::try_const_usize; pub type BasicBlockId = Idx<BasicBlock>; @@ -200,9 +197,10 @@ impl<V: PartialEq> ProjectionElem<V> { } }, ProjectionElem::Field(Either::Left(f)) => match base.kind() { - TyKind::Adt(_, subst) => { - db.field_types(f.parent)[f.local_id].get().instantiate(interner, subst) - } + TyKind::Adt(_, subst) => db.field_types(f.parent)[f.local_id] + .get() + .instantiate(interner, subst) + .skip_norm_wip(), ty => { never!("Only adt has field, found {:?}", ty); Ty::new_error(interner, ErrorGuaranteed) @@ -1087,7 +1085,7 @@ pub struct MirBody { pub basic_blocks: Arena<BasicBlock>, pub locals: Arena<Local>, pub start_block: BasicBlockId, - pub owner: DefWithBodyId, + pub owner: InferBodyId, pub binding_locals: ArenaMap<BindingId, LocalId>, pub upvar_locals: FxHashMap<BindingId, Vec<(LocalId, crate::closure_analysis::Place)>>, pub param_locals: Vec<LocalId>, diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index 17715d3fcd..940bc57259 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -5,13 +5,14 @@ use std::iter; -use hir_def::{DefWithBodyId, ExpressionStoreOwnerId, HasModule}; +use either::Either; +use hir_def::HasModule; use la_arena::ArenaMap; use rustc_hash::FxHashMap; use stdx::never; -use triomphe::Arc; use crate::{ + InferBodyId, closure_analysis::ProjectionKind as HirProjectionKind, db::{HirDatabase, InternedClosureId}, display::DisplayTarget, @@ -57,91 +58,109 @@ pub struct BorrowRegion { #[derive(Debug, Clone, PartialEq, Eq)] pub struct BorrowckResult { - pub mir_body: Arc<MirBody>, + owner: Either<InferBodyId, InternedClosureId>, pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>, pub moved_out_of_ref: Vec<MovedOutOfRef>, pub partially_moved: Vec<PartiallyMoved>, pub borrow_regions: Vec<BorrowRegion>, } -fn all_mir_bodies( - db: &dyn HirDatabase, - def: DefWithBodyId, - mut cb: impl FnMut(Arc<MirBody>) -> BorrowckResult, - mut merge_from_closures: impl FnMut(&mut BorrowckResult, &BorrowckResult), -) -> Result<Arc<[BorrowckResult]>, MirLowerError> { - fn for_closure( - db: &dyn HirDatabase, +impl BorrowckResult { + pub fn mir_body<'db>(&self, db: &'db dyn HirDatabase) -> &'db MirBody { + match self.owner { + Either::Left(it) => db.mir_body(it).unwrap(), + Either::Right(it) => db.mir_body_for_closure(it).unwrap(), + } + } +} + +fn all_mir_bodies<'db>( + db: &'db dyn HirDatabase, + def: InferBodyId, + mut cb: impl FnMut(&'db MirBody, Either<InferBodyId, InternedClosureId>) -> BorrowckResult, + mut merge_from_closures: impl FnMut( + (&mut BorrowckResult, &'db MirBody), + (&BorrowckResult, &'db MirBody), + ), +) -> Result<Box<[BorrowckResult]>, MirLowerError> { + fn for_closure<'db>( + db: &'db dyn HirDatabase, c: InternedClosureId, - results: &mut Vec<BorrowckResult>, - cb: &mut impl FnMut(Arc<MirBody>) -> BorrowckResult, - merge_from_closures: &mut impl FnMut(&mut BorrowckResult, &BorrowckResult), + results: &mut Vec<(BorrowckResult, &'db MirBody)>, + cb: &mut impl FnMut(&'db MirBody, Either<InferBodyId, InternedClosureId>) -> BorrowckResult, + merge_from_closures: &mut impl FnMut( + (&mut BorrowckResult, &'db MirBody), + (&BorrowckResult, &'db MirBody), + ), ) -> Result<(), MirLowerError> { match db.mir_body_for_closure(c) { Ok(body) => { let parent_index = results.len(); - results.push(cb(body.clone())); + results.push((cb(body, Either::Right(c)), body)); body.closures .iter() .try_for_each(|&it| for_closure(db, it, results, cb, merge_from_closures))?; merge(results, merge_from_closures, parent_index); Ok(()) } - Err(e) => Err(e), + Err(e) => Err(e.clone()), } } - fn merge( - results: &mut [BorrowckResult], - merge: &mut impl FnMut(&mut BorrowckResult, &BorrowckResult), + fn merge<'db>( + results: &mut [(BorrowckResult, &'db MirBody)], + merge: &mut impl FnMut((&mut BorrowckResult, &'db MirBody), (&BorrowckResult, &'db MirBody)), parent_index: usize, ) { let (parent_and_before, children) = results.split_at_mut(parent_index + 1); - let parent = &mut parent_and_before[parent_and_before.len() - 1]; - children.iter().for_each(|child| merge(parent, child)); + let (parent, parent_mir_body) = &mut parent_and_before[parent_and_before.len() - 1]; + children.iter().for_each(|(child, child_mir_body)| { + merge((parent, parent_mir_body), (child, child_mir_body)) + }); } let mut results = Vec::new(); match db.mir_body(def) { Ok(body) => { - results.push(cb(body.clone())); + results.push((cb(body, Either::Left(def)), body)); body.closures.iter().try_for_each(|&it| { for_closure(db, it, &mut results, &mut cb, &mut merge_from_closures) })?; merge(&mut results, &mut merge_from_closures, 0); - Ok(results.into()) + Ok(results.into_iter().map(|(it, _)| it).collect()) } - Err(e) => Err(e), + Err(e) => Err(e.clone()), } } +#[salsa_macros::tracked(returns(ref), lru = 2024)] pub fn borrowck_query( db: &dyn HirDatabase, - def: DefWithBodyId, -) -> Result<Arc<[BorrowckResult]>, MirLowerError> { + def: InferBodyId, +) -> Result<Box<[BorrowckResult]>, MirLowerError> { let _p = tracing::info_span!("borrowck_query").entered(); let module = def.module(db); let interner = DbInterner::new_with(db, module.krate(db)); - let env = db.trait_environment(ExpressionStoreOwnerId::from(def)); + let env = db.trait_environment(def.expression_store_owner(db)); // This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`. let typing_mode = TypingMode::borrowck(interner, def.into()); let res = all_mir_bodies( db, def, - |body| { + |body, owner| { // FIXME(next-solver): Opaques. let infcx = interner.infer_ctxt().build(typing_mode); BorrowckResult { - mutability_of_locals: mutability_of_locals(&infcx, env, &body), - moved_out_of_ref: moved_out_of_ref(&infcx, env, &body), - partially_moved: partially_moved(&infcx, env, &body), - borrow_regions: borrow_regions(db, &body), - mir_body: body, + owner, + mutability_of_locals: mutability_of_locals(&infcx, env, body), + moved_out_of_ref: moved_out_of_ref(&infcx, env, body), + partially_moved: partially_moved(&infcx, env, body), + borrow_regions: borrow_regions(db, body), } }, - |parent, child| { - for (upvar, child_locals) in &child.mir_body.upvar_locals { - let Some(&parent_local) = parent.mir_body.binding_locals.get(*upvar) else { + |(parent, parent_mir_body), (child, child_mir_body)| { + for (upvar, child_locals) in &child_mir_body.upvar_locals { + let Some(&parent_local) = parent_mir_body.binding_locals.get(*upvar) else { continue; }; for (child_local, capture_place) in child_locals { diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 80e429c4c8..3372f6ec2e 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -5,20 +5,19 @@ use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range}; use base_db::{Crate, target::TargetLoadError}; use either::Either; use hir_def::{ - AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, FunctionId, GeneralConstId, - HasModule, ItemContainerId, Lookup, StaticId, VariantId, - expr_store::{Body, HygieneId}, + AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, + VariantId, + expr_store::{Body, ExpressionStore, HygieneId}, item_tree::FieldsShape, lang_item::LangItems, layout::{TagEncoding, Variants}, - resolver::{HasResolver, TypeNs, ValueNs}, + resolver::{HasResolver, ValueNs}, signatures::{ EnumSignature, FunctionSignature, StaticFlags, StaticSignature, StructFlags, StructSignature, TraitSignature, }, }; -use hir_expand::{InFile, mod_path::path, name::Name}; -use intern::sym; +use hir_expand::{InFile, mod_path::path}; use la_arena::ArenaMap; use macros::GenericTypeVisitable; use rustc_abi::{Size, TargetDataLayout}; @@ -30,7 +29,7 @@ use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ AliasTyKind, - inherent::{AdtDef, GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, + inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, }; use span::FileId; use stdx::never; @@ -38,9 +37,9 @@ use syntax::{SyntaxNodePtr, TextRange}; use triomphe::Arc; use crate::{ - CallableDefId, ComplexMemoryMap, InferenceResult, MemoryMap, ParamEnvAndCrate, + CallableDefId, ComplexMemoryMap, InferBodyId, InferenceResult, MemoryMap, ParamEnvAndCrate, consteval::{self, ConstEvalError, try_const_usize}, - db::{HirDatabase, InternedClosureId}, + db::{GeneralConstId, HirDatabase, InternedClosureId}, display::{ClosureStyle, DisplayTarget, HirDisplay}, infer::PointerCast, layout::{Layout, LayoutError, RustcEnumVariantIdx}, @@ -156,26 +155,26 @@ impl TlsData { } } -struct StackFrame { - locals: Locals, +struct StackFrame<'a> { + locals: Locals<'a>, destination: Option<BasicBlockId>, prev_stack_ptr: usize, - span: (MirSpan, DefWithBodyId), + span: (MirSpan, InferBodyId), } #[derive(Clone)] -enum MirOrDynIndex { - Mir(Arc<MirBody>), +enum MirOrDynIndex<'a> { + Mir(&'a MirBody), Dyn(usize), } -pub struct Evaluator<'db> { +pub struct Evaluator<'a, 'db> { db: &'db dyn HirDatabase, param_env: ParamEnvAndCrate<'db>, - target_data_layout: Arc<TargetDataLayout>, + target_data_layout: &'db TargetDataLayout, stack: Vec<u8>, heap: Vec<u8>, - code_stack: Vec<StackFrame>, + code_stack: Vec<StackFrame<'a>>, /// Stores the global location of the statics. We const evaluate every static first time we need it /// and see it's missing, then we add it to this to reuse. static_locations: FxHashMap<StaticId, Address>, @@ -190,11 +189,11 @@ pub struct Evaluator<'db> { layout_cache: RefCell<FxHashMap<Ty<'db>, Arc<Layout>>>, projected_ty_cache: RefCell<FxHashMap<(Ty<'db>, PlaceElem), Ty<'db>>>, not_special_fn_cache: RefCell<FxHashSet<FunctionId>>, - mir_or_dyn_index_cache: RefCell<FxHashMap<(FunctionId, GenericArgs<'db>), MirOrDynIndex>>, + mir_or_dyn_index_cache: RefCell<FxHashMap<(FunctionId, GenericArgs<'db>), MirOrDynIndex<'a>>>, /// Constantly dropping and creating `Locals` is very costly. We store /// old locals that we normally want to drop here, to reuse their allocations /// later. - unused_locals_store: RefCell<FxHashMap<DefWithBodyId, Vec<Locals>>>, + unused_locals_store: RefCell<FxHashMap<InferBodyId, Vec<Locals<'a>>>>, cached_ptr_size: usize, cached_fn_trait_func: Option<FunctionId>, cached_fn_mut_trait_func: Option<FunctionId>, @@ -237,17 +236,21 @@ impl Interval { Self { addr, size } } - fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { + fn get<'b, 'a, 'db: 'a>(&self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> { memory.read_memory(self.addr, self.size) } - fn write_from_bytes<'db>(&self, memory: &mut Evaluator<'db>, bytes: &[u8]) -> Result<'db, ()> { + fn write_from_bytes<'a, 'db: 'a>( + &self, + memory: &mut Evaluator<'a, 'db>, + bytes: &[u8], + ) -> Result<'db, ()> { memory.write_memory(self.addr, bytes) } - fn write_from_interval<'db>( + fn write_from_interval<'a, 'db: 'a>( &self, - memory: &mut Evaluator<'db>, + memory: &mut Evaluator<'a, 'db>, interval: Interval, ) -> Result<'db, ()> { memory.copy_from_interval(self.addr, interval) @@ -259,16 +262,22 @@ impl Interval { } impl<'db> IntervalAndTy<'db> { - fn get<'a>(&self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { + fn get<'b, 'a>(&self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> + where + 'db: 'a, + { memory.read_memory(self.interval.addr, self.interval.size) } - fn new( + fn new<'a>( addr: Address, ty: Ty<'db>, - evaluator: &Evaluator<'db>, - locals: &Locals, - ) -> Result<'db, IntervalAndTy<'db>> { + evaluator: &Evaluator<'a, 'db>, + locals: &Locals<'a>, + ) -> Result<'db, IntervalAndTy<'db>> + where + 'db: 'a, + { let size = evaluator.size_of_sized(ty, locals, "type of interval")?; Ok(IntervalAndTy { interval: Interval { addr, size }, ty }) } @@ -286,7 +295,7 @@ impl From<Interval> for IntervalOrOwned { } impl IntervalOrOwned { - fn get<'a, 'db>(&'a self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> { + fn get<'b, 'a, 'db: 'a>(&'b self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> { Ok(match self { IntervalOrOwned::Owned(o) => o, IntervalOrOwned::Borrowed(b) => b.get(memory)?, @@ -362,7 +371,7 @@ pub enum MirEvalError { InvalidConst, InFunction( Box<MirEvalError>, - Vec<(Either<FunctionId, InternedClosureId>, MirSpan, DefWithBodyId)>, + Vec<(Either<FunctionId, InternedClosureId>, MirSpan, InferBodyId)>, ), ExecutionLimitExceeded, StackOverflow, @@ -401,7 +410,16 @@ impl MirEvalError { writeln!(f, "In {closure:?}")?; } } - let source_map = &Body::with_source_map(db, *def).1; + let (source_map, self_param_syntax) = match *def { + InferBodyId::DefWithBodyId(def) => { + let body = &Body::with_source_map(db, def).1; + (&**body, body.self_param_syntax()) + } + InferBodyId::AnonConstId(def) => { + let store = ExpressionStore::with_source_map(db, def.loc(db).owner).1; + (store, None) + } + }; let span: InFile<SyntaxNodePtr> = match span { MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { Ok(s) => s.map(|it| it.into()), @@ -421,7 +439,7 @@ impl MirEvalError { None => continue, } } - MirSpan::SelfParam => match source_map.self_param_syntax() { + MirSpan::SelfParam => match self_param_syntax { Some(s) => s.map(|it| it.syntax_node_ptr()), None => continue, }, @@ -449,6 +467,7 @@ impl MirEvalError { ItemContainerId::ImplId(impl_id) => Some({ db.impl_self_ty(impl_id) .instantiate_identity() + .skip_norm_wip() .display(db, display_target) .to_string() }), @@ -576,9 +595,9 @@ impl DropFlags { } #[derive(Debug)] -struct Locals { +struct Locals<'a> { ptr: ArenaMap<LocalId, Interval>, - body: Arc<MirBody>, + body: &'a MirBody, drop_flags: DropFlags, } @@ -596,9 +615,9 @@ impl MirOutput { } } -pub fn interpret_mir<'db>( +pub fn interpret_mir<'a, 'db: 'a>( db: &'db dyn HirDatabase, - body: Arc<MirBody>, + body: &MirBody, // FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now // they share their body with their parent, so in MIR lowering we have locals of the parent body, which // might have placeholders. With this argument, we (wrongly) assume that every placeholder type has @@ -613,7 +632,7 @@ pub fn interpret_mir<'db>( if evaluator.ptr_size() != size_of::<usize>() { not_supported!("targets with different pointer size from host"); } - let interval = evaluator.interpret_mir(body.clone(), None.into_iter())?; + let interval = evaluator.interpret_mir(body, None.into_iter())?; let bytes = interval.get(&evaluator)?; let mut memory_map = evaluator.create_memory_map( bytes, @@ -638,13 +657,13 @@ const EXECUTION_LIMIT: usize = 100_000; #[cfg(not(test))] const EXECUTION_LIMIT: usize = 10_000_000; -impl<'db> Evaluator<'db> { +impl<'a, 'db: 'a> Evaluator<'a, 'db> { pub fn new( db: &'db dyn HirDatabase, - owner: DefWithBodyId, + owner: InferBodyId, assert_placeholder_ty_is_unused: bool, trait_env: Option<ParamEnvAndCrate<'db>>, - ) -> Result<'db, Evaluator<'db>> { + ) -> Result<'db, Evaluator<'a, 'db>> { let module = owner.module(db); let crate_id = module.krate(db); let target_data_layout = match db.target_data_layout(crate_id) { @@ -666,7 +685,7 @@ impl<'db> Evaluator<'db> { db, random_state: oorandom::Rand64::new(0), param_env: trait_env.unwrap_or_else(|| ParamEnvAndCrate { - param_env: db.trait_environment(ExpressionStoreOwnerId::from(owner)), + param_env: db.trait_environment(owner.expression_store_owner(db)), krate: crate_id, }), crate_id, @@ -682,15 +701,9 @@ impl<'db> Evaluator<'db> { mir_or_dyn_index_cache: RefCell::new(Default::default()), unused_locals_store: RefCell::new(Default::default()), cached_ptr_size, - cached_fn_trait_func: lang_items - .Fn - .and_then(|x| x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call))), - cached_fn_mut_trait_func: lang_items.FnMut.and_then(|x| { - x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_mut)) - }), - cached_fn_once_trait_func: lang_items.FnOnce.and_then(|x| { - x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_once)) - }), + cached_fn_trait_func: lang_items.Fn_call, + cached_fn_mut_trait_func: lang_items.FnMut_call_mut, + cached_fn_once_trait_func: lang_items.FnOnce_call_once, infcx, }) } @@ -705,11 +718,11 @@ impl<'db> Evaluator<'db> { self.infcx.interner.lang_items() } - fn place_addr(&self, p: &Place, locals: &Locals) -> Result<'db, Address> { + fn place_addr(&self, p: &Place, locals: &Locals<'a>) -> Result<'db, Address> { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0) } - fn place_interval(&self, p: &Place, locals: &Locals) -> Result<'db, Interval> { + fn place_interval(&self, p: &Place, locals: &Locals<'a>) -> Result<'db, Interval> { let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?; Ok(Interval { addr: place_addr_and_ty.0, @@ -736,10 +749,10 @@ impl<'db> Evaluator<'db> { r } - fn place_addr_and_ty_and_metadata<'a>( - &'a self, + fn place_addr_and_ty_and_metadata<'b>( + &'b self, p: &Place, - locals: &'a Locals, + locals: &'b Locals<'a>, ) -> Result<'db, (Address, Ty<'db>, Option<IntervalOrOwned>)> { let mut addr = locals.ptr[p.local].addr; let mut ty: Ty<'db> = locals.body.locals[p.local].ty.as_ref(); @@ -873,11 +886,11 @@ impl<'db> Evaluator<'db> { self.layout(Ty::new_adt(self.interner(), adt, subst)) } - fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result<'db, Ty<'db>> { + fn place_ty<'b>(&'b self, p: &Place, locals: &'b Locals<'a>) -> Result<'db, Ty<'db>> { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1) } - fn operand_ty(&self, o: &Operand, locals: &Locals) -> Result<'db, Ty<'db>> { + fn operand_ty(&self, o: &Operand, locals: &Locals<'a>) -> Result<'db, Ty<'db>> { Ok(match &o.kind { OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?, OperandKind::Constant { konst: _, ty } => ty.as_ref(), @@ -898,7 +911,7 @@ impl<'db> Evaluator<'db> { fn operand_ty_and_eval( &mut self, o: &Operand, - locals: &mut Locals, + locals: &mut Locals<'a>, ) -> Result<'db, IntervalAndTy<'db>> { Ok(IntervalAndTy { interval: self.eval_operand(o, locals)?, @@ -908,7 +921,7 @@ impl<'db> Evaluator<'db> { fn interpret_mir( &mut self, - body: Arc<MirBody>, + body: &'a MirBody, args: impl Iterator<Item = IntervalOrOwned>, ) -> Result<'db, Interval> { if let Some(it) = self.stack_depth_limit.checked_sub(1) { @@ -917,8 +930,8 @@ impl<'db> Evaluator<'db> { return Err(MirEvalError::StackOverflow); } let mut current_block_idx = body.start_block; - let (mut locals, prev_stack_ptr) = self.create_locals_for_body(&body, None)?; - self.fill_locals_for_body(&body, &mut locals, args)?; + let (mut locals, prev_stack_ptr) = self.create_locals_for_body(body, None)?; + self.fill_locals_for_body(body, &mut locals, args)?; let prev_code_stack = mem::take(&mut self.code_stack); let span = (MirSpan::Unknown, body.owner); self.code_stack.push(StackFrame { locals, destination: None, prev_stack_ptr, span }); @@ -1041,7 +1054,7 @@ impl<'db> Evaluator<'db> { let my_code_stack = mem::replace(&mut self.code_stack, prev_code_stack); let mut error_stack = vec![]; for frame in my_code_stack.into_iter().rev() { - if let DefWithBodyId::FunctionId(f) = frame.locals.body.owner { + if let Some(f) = frame.locals.body.owner.as_function() { error_stack.push((Either::Left(f), frame.span.0, frame.span.1)); } } @@ -1073,7 +1086,7 @@ impl<'db> Evaluator<'db> { fn fill_locals_for_body( &mut self, body: &MirBody, - locals: &mut Locals, + locals: &mut Locals<'a>, args: impl Iterator<Item = IntervalOrOwned>, ) -> Result<'db, ()> { let mut remain_args = body.param_locals.len(); @@ -1096,19 +1109,15 @@ impl<'db> Evaluator<'db> { fn create_locals_for_body( &mut self, - body: &Arc<MirBody>, + body: &'a MirBody, destination: Option<Interval>, - ) -> Result<'db, (Locals, usize)> { + ) -> Result<'db, (Locals<'a>, usize)> { let mut locals = match self.unused_locals_store.borrow_mut().entry(body.owner).or_default().pop() { - None => Locals { - ptr: ArenaMap::new(), - body: body.clone(), - drop_flags: DropFlags::default(), - }, + None => Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() }, Some(mut l) => { l.drop_flags.clear(); - l.body = body.clone(); + l.body = body; l } }; @@ -1145,7 +1154,7 @@ impl<'db> Evaluator<'db> { Ok((locals, prev_stack_pointer)) } - fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result<'db, IntervalOrOwned> { + fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'a>) -> Result<'db, IntervalOrOwned> { use IntervalOrOwned::*; Ok(match r { Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?), @@ -1651,7 +1660,7 @@ impl<'db> Evaluator<'db> { let TyKind::Adt(adt_def, _) = ty.kind() else { return Ok(0); }; - let AdtId::EnumId(e) = adt_def.def_id().0 else { + let AdtId::EnumId(e) = adt_def.def_id() else { return Ok(0); }; match &layout.variants { @@ -1662,7 +1671,7 @@ impl<'db> Evaluator<'db> { Ok(r) } Variants::Multiple { tag, tag_encoding, variants, .. } => { - let size = tag.size(&*self.target_data_layout).bytes_usize(); + let size = tag.size(self.target_data_layout).bytes_usize(); let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field let is_signed = tag.is_signed(); match tag_encoding { @@ -1701,13 +1710,13 @@ impl<'db> Evaluator<'db> { return Ok(it); } if let TyKind::Adt(adt_ef, subst) = kind - && let AdtId::StructId(struct_id) = adt_ef.def_id().0 + && let AdtId::StructId(struct_id) = adt_ef.def_id() { let field_types = self.db.field_types(struct_id.into()); if let Some(ty) = field_types.iter().last().map(|it| it.1.get().instantiate(self.interner(), subst)) { - return self.coerce_unsized_look_through_fields(ty, goal); + return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); } } Err(MirEvalError::CoerceUnsizedError(ty.store())) @@ -1768,8 +1777,8 @@ impl<'db> Evaluator<'db> { } TyKind::Adt(adt_def, target_subst) => match ¤t_ty.kind() { TyKind::Adt(current_adt_def, current_subst) => { - let id = adt_def.def_id().0; - let current_id = current_adt_def.def_id().0; + let id = adt_def.def_id(); + let current_id = current_adt_def.def_id(); if id != current_id { not_supported!("unsizing struct with different type"); } @@ -1784,10 +1793,12 @@ impl<'db> Evaluator<'db> { }; let target_last_field = self.db.field_types(id.into())[last_field] .get() - .instantiate(self.interner(), target_subst); + .instantiate(self.interner(), target_subst) + .skip_norm_wip(); let current_last_field = self.db.field_types(id.into())[last_field] .get() - .instantiate(self.interner(), current_subst); + .instantiate(self.interner(), current_subst) + .skip_norm_wip(); return self.unsizing_ptr_from_addr( target_last_field, current_last_field, @@ -1804,10 +1815,10 @@ impl<'db> Evaluator<'db> { &mut self, it: VariantId, subst: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, ) -> Result<'db, (usize, Arc<Layout>, Option<(usize, usize, i128)>)> { let adt = it.adt_id(self.db); - if let DefWithBodyId::VariantId(f) = locals.body.owner + if let Some(f) = locals.body.owner.as_variant() && let VariantId::EnumVariantId(it) = it && let AdtId::EnumId(e) = adt && f.lookup(self.db).parent == e @@ -1851,7 +1862,7 @@ impl<'db> Evaluator<'db> { if have_tag { Some(( layout.fields.offset(0).bytes_usize(), - tag.size(&*self.target_data_layout).bytes_usize(), + tag.size(self.target_data_layout).bytes_usize(), discriminant, )) } else { @@ -1898,7 +1909,7 @@ impl<'db> Evaluator<'db> { Ok(result) } - fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<'db, Interval> { + fn eval_operand(&mut self, it: &Operand, locals: &mut Locals<'a>) -> Result<'db, Interval> { Ok(match &it.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { locals.drop_flags.remove_place(p, &locals.body.projection_store); @@ -2051,7 +2062,7 @@ impl<'db> Evaluator<'db> { #[allow(clippy::double_parens)] fn allocate_const_in_heap( &mut self, - locals: &Locals, + locals: &Locals<'a>, konst: Const<'db>, ) -> Result<'db, Interval> { match konst.kind() { @@ -2059,9 +2070,9 @@ impl<'db> Evaluator<'db> { ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => { let mut id = const_id.0; let mut subst = subst; - if let hir_def::GeneralConstId::ConstId(c) = id { + if let GeneralConstId::ConstId(c) = id { let (c, s) = lookup_impl_const(&self.infcx, self.param_env.param_env, c, subst); - id = hir_def::GeneralConstId::ConstId(c); + id = GeneralConstId::ConstId(c); subst = s; } let allocation = match id { @@ -2077,9 +2088,13 @@ impl<'db> Evaluator<'db> { MirEvalError::ConstEvalError(name, Box::new(e)) })? } - GeneralConstId::AnonConstId(_) => { - not_supported!("anonymous const evaluation") - } + GeneralConstId::AnonConstId(anon_const_id) => self + .db + .anon_const_eval(anon_const_id, subst, Some(self.param_env)) + .map_err(|e| { + let name = id.name(self.db); + MirEvalError::ConstEvalError(name, Box::new(e)) + })?, }; self.allocate_allocation_in_heap(locals, allocation) } @@ -2089,7 +2104,7 @@ impl<'db> Evaluator<'db> { fn allocate_allocation_in_heap( &mut self, - locals: &Locals, + locals: &Locals<'a>, allocation: Allocation<'db>, ) -> Result<'db, Interval> { let AllocationData { ty, memory: ref v, ref memory_map } = *allocation; @@ -2128,7 +2143,7 @@ impl<'db> Evaluator<'db> { Ok(Interval::new(addr, size)) } - fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result<'db, Interval> { + fn eval_place(&mut self, p: &Place, locals: &Locals<'a>) -> Result<'db, Interval> { let addr = self.place_addr(p, locals)?; Ok(Interval::new( addr, @@ -2228,13 +2243,17 @@ impl<'db> Evaluator<'db> { Ok(()) } - fn size_align_of(&self, ty: Ty<'db>, locals: &Locals) -> Result<'db, Option<(usize, usize)>> { + fn size_align_of( + &self, + ty: Ty<'db>, + locals: &Locals<'a>, + ) -> Result<'db, Option<(usize, usize)>> { if let Some(layout) = self.layout_cache.borrow().get(&ty) { return Ok(layout .is_sized() .then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize))); } - if let DefWithBodyId::VariantId(f) = locals.body.owner + if let Some(f) = locals.body.owner.as_variant() && let Some((AdtId::EnumId(e), _)) = ty.as_adt() && f.lookup(self.db).parent == e { @@ -2257,7 +2276,7 @@ impl<'db> Evaluator<'db> { fn size_of_sized( &self, ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, what: &'static str, ) -> Result<'db, usize> { match self.size_align_of(ty, locals)? { @@ -2271,7 +2290,7 @@ impl<'db> Evaluator<'db> { fn size_align_of_sized( &self, ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, what: &'static str, ) -> Result<'db, (usize, usize)> { match self.size_align_of(ty, locals)? { @@ -2312,13 +2331,13 @@ impl<'db> Evaluator<'db> { &self, bytes: &[u8], ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, ) -> Result<'db, ComplexMemoryMap<'db>> { - fn rec<'db>( - this: &Evaluator<'db>, + fn rec<'a, 'db: 'a>( + this: &Evaluator<'a, 'db>, bytes: &[u8], ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, mm: &mut ComplexMemoryMap<'db>, stack_depth_limit: usize, ) -> Result<'db, ()> { @@ -2409,7 +2428,7 @@ impl<'db> Evaluator<'db> { )?; } } - TyKind::Adt(adt, subst) => match adt.def_id().0 { + TyKind::Adt(adt, subst) => match adt.def_id() { AdtId::StructId(s) => { let data = s.fields(this.db); let layout = this.layout(ty)?; @@ -2419,7 +2438,10 @@ impl<'db> Evaluator<'db> { .fields .offset(u32::from(f.into_raw()) as usize) .bytes_usize(); - let ty = field_types[f].get().instantiate(this.interner(), subst); + let ty = field_types[f] + .get() + .instantiate(this.interner(), subst) + .skip_norm_wip(); let size = this.layout(ty)?.size.bytes_usize(); rec( this, @@ -2436,7 +2458,7 @@ impl<'db> Evaluator<'db> { if let Some((v, l)) = detect_variant_from_bytes( &layout, this.db, - &this.target_data_layout, + this.target_data_layout, bytes, e, ) { @@ -2445,7 +2467,10 @@ impl<'db> Evaluator<'db> { for (f, _) in data.fields().iter() { let offset = l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); - let ty = field_types[f].get().instantiate(this.interner(), subst); + let ty = field_types[f] + .get() + .instantiate(this.interner(), subst) + .skip_norm_wip(); let size = this.layout(ty)?.size.bytes_usize(); rec( this, @@ -2487,7 +2512,7 @@ impl<'db> Evaluator<'db> { ty_of_bytes: impl Fn(&[u8]) -> Result<'db, Ty<'db>> + Copy, addr: Address, ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, ) -> Result<'db, ()> { // FIXME: support indirect references let layout = self.layout(ty)?; @@ -2527,11 +2552,11 @@ impl<'db> Evaluator<'db> { let new_id = self.vtable_map.id(ty); self.write_memory(addr, &new_id.to_le_bytes())?; } - TyKind::Adt(id, args) => match id.def_id().0 { + TyKind::Adt(id, args) => match id.def_id() { AdtId::StructId(s) => { for (i, (_, ty)) in self.db.field_types(s.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); - let ty = ty.get().instantiate(self.interner(), args); + let ty = ty.get().instantiate(self.interner(), args).skip_norm_wip(); self.patch_addresses( patch_map, ty_of_bytes, @@ -2546,13 +2571,13 @@ impl<'db> Evaluator<'db> { if let Some((ev, layout)) = detect_variant_from_bytes( &layout, self.db, - &self.target_data_layout, + self.target_data_layout, self.read_memory(addr, layout.size.bytes_usize())?, e, ) { for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); - let ty = ty.get().instantiate(self.interner(), args); + let ty = ty.get().instantiate(self.interner(), args).skip_norm_wip(); self.patch_addresses( patch_map, ty_of_bytes, @@ -2619,10 +2644,10 @@ impl<'db> Evaluator<'db> { bytes: Interval, destination: Interval, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, target_bb: Option<BasicBlockId>, span: MirSpan, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { let id = from_bytes!(usize, bytes.get(self)?); let next_ty = self.vtable_map.ty(id)?; use rustc_type_ir::TyKind; @@ -2650,9 +2675,9 @@ impl<'db> Evaluator<'db> { generic_args: GenericArgs<'db>, destination: Interval, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { let mir_body = self .db .monomorphized_mir_body_for_closure( @@ -2688,10 +2713,10 @@ impl<'db> Evaluator<'db> { generic_args: GenericArgs<'db>, destination: Interval, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, target_bb: Option<BasicBlockId>, span: MirSpan, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { match def { CallableDefId::FunctionId(def) => { if self.detect_fn_trait(def).is_some() { @@ -2746,9 +2771,9 @@ impl<'db> Evaluator<'db> { &self, def: FunctionId, generic_args: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, - ) -> Result<'db, MirOrDynIndex> { + ) -> Result<'db, MirOrDynIndex<'a>> { let pair = (def, generic_args); if let Some(r) = self.mir_or_dyn_index_cache.borrow().get(&pair) { return Ok(r.clone()); @@ -2788,11 +2813,11 @@ impl<'db> Evaluator<'db> { mut def: FunctionId, args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, target_bb: Option<BasicBlockId>, span: MirSpan, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { if self.detect_and_exec_special_function( def, args, @@ -2854,18 +2879,18 @@ impl<'db> Evaluator<'db> { fn exec_looked_up_function( &mut self, - mir_body: Arc<MirBody>, - locals: &Locals, + mir_body: &'a MirBody, + locals: &Locals<'a>, def: FunctionId, arg_bytes: impl Iterator<Item = IntervalOrOwned>, span: MirSpan, destination: Interval, target_bb: Option<BasicBlockId>, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { Ok(if let Some(target_bb) = target_bb { let (mut locals, prev_stack_ptr) = - self.create_locals_for_body(&mir_body, Some(destination))?; - self.fill_locals_for_body(&mir_body, &mut locals, arg_bytes.into_iter())?; + self.create_locals_for_body(mir_body, Some(destination))?; + self.fill_locals_for_body(mir_body, &mut locals, arg_bytes.into_iter())?; let span = (span, locals.body.owner); Some(StackFrame { locals, destination: Some(target_bb), prev_stack_ptr, span }) } else { @@ -2885,11 +2910,11 @@ impl<'db> Evaluator<'db> { def: FunctionId, args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, target_bb: Option<BasicBlockId>, span: MirSpan, - ) -> Result<'db, Option<StackFrame>> { + ) -> Result<'db, Option<StackFrame<'a>>> { let func = args .first() .ok_or_else(|| MirEvalError::InternalError("fn trait with no arg".into()))?; @@ -2954,7 +2979,7 @@ impl<'db> Evaluator<'db> { } } - fn eval_static(&mut self, st: StaticId, locals: &Locals) -> Result<'db, Address> { + fn eval_static(&mut self, st: StaticId, locals: &Locals<'a>) -> Result<'db, Address> { if let Some(o) = self.static_locations.get(&st) { return Ok(*o); }; @@ -3001,7 +3026,12 @@ impl<'db> Evaluator<'db> { } } - fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<'db, ()> { + fn drop_place( + &mut self, + place: &Place, + locals: &mut Locals<'a>, + span: MirSpan, + ) -> Result<'db, ()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; if !locals.drop_flags.remove_place(place, &locals.body.projection_store) { return Ok(()); @@ -3016,15 +3046,12 @@ impl<'db> Evaluator<'db> { fn run_drop_glue_deep( &mut self, ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, addr: Address, _metadata: &[u8], span: MirSpan, ) -> Result<'db, ()> { - let Some(drop_fn) = (|| { - let drop_trait = self.lang_items().Drop?; - drop_trait.trait_items(self.db).method_by_name(&Name::new_symbol_root(sym::drop)) - })() else { + let Some(drop_fn) = self.lang_items().Drop_drop else { // in some tests we don't have drop trait in minicore, and // we can ignore drop in them. return Ok(()); @@ -3046,7 +3073,7 @@ impl<'db> Evaluator<'db> { } match ty.kind() { TyKind::Adt(adt_def, subst) => { - let id = adt_def.def_id().0; + let id = adt_def.def_id(); match id { AdtId::StructId(s) => { let data = StructSignature::of(self.db, s); @@ -3066,7 +3093,8 @@ impl<'db> Evaluator<'db> { let addr = addr.offset(offset); let ty = field_types[field] .get() - .instantiate(self.interner(), subst); + .instantiate(self.interner(), subst) + .skip_norm_wip(); self.run_drop_glue_deep(ty, locals, addr, &[], span)?; } } @@ -3122,7 +3150,7 @@ impl<'db> Evaluator<'db> { pub fn render_const_using_debug_impl<'db>( db: &'db dyn HirDatabase, - owner: DefWithBodyId, + owner: InferBodyId, c: Allocation<'db>, ty: Ty<'db>, ) -> Result<'db, String> { @@ -3135,16 +3163,9 @@ pub fn render_const_using_debug_impl<'db>( drop_flags: DropFlags::default(), }; let data = evaluator.allocate_allocation_in_heap(locals, c)?; + let lang_items = evaluator.interner().lang_items(); let resolver = owner.resolver(db); - let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully( - db, - &hir_def::expr_store::path::Path::from_known_path_with_no_generic(path![core::fmt::Debug]), - ) else { - not_supported!("core::fmt::Debug not found"); - }; - let Some(debug_fmt_fn) = - debug_trait.trait_items(db).method_by_name(&Name::new_symbol_root(sym::fmt)) - else { + let Some(debug_fmt_fn) = lang_items.Debug_fmt else { not_supported!("core::fmt::Debug::fmt not found"); }; // a1 = &[""] diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 9586d38abc..b4a5aa8a87 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -4,9 +4,8 @@ use std::cmp::{self, Ordering}; use hir_def::{attrs::AttrFlags, signatures::FunctionSignature}; -use hir_expand::name::Name; -use intern::sym; -use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _}; +use rustc_abi::ExternAbi; +use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, SliceLike, Ty as _}; use stdx::never; use crate::{ @@ -29,13 +28,13 @@ enum EvalLangItem { DropInPlace, } -impl<'db> Evaluator<'db> { +impl<'a, 'db: 'a> Evaluator<'a, 'db> { pub(super) fn detect_and_exec_special_function( &mut self, def: FunctionId, args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, span: MirSpan, ) -> Result<'db, bool> { @@ -60,7 +59,9 @@ impl<'db> Evaluator<'db> { ); } let is_extern_c = match def.lookup(self.db).container { - hir_def::ItemContainerId::ExternBlockId(block) => block.abi(self.db) == Some(sym::C), + hir_def::ItemContainerId::ExternBlockId(block) => { + matches!(block.abi(self.db), ExternAbi::C { .. }) + } _ => false, }; if is_extern_c { @@ -132,7 +133,7 @@ impl<'db> Evaluator<'db> { def: FunctionId, args: &[IntervalAndTy<'db>], self_ty: Ty<'db>, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, span: MirSpan, ) -> Result<'db, ()> { @@ -190,7 +191,7 @@ impl<'db> Evaluator<'db> { layout: Arc<Layout>, addr: Address, def: FunctionId, - locals: &Locals, + locals: &Locals<'a>, destination: Interval, span: MirSpan, ) -> Result<'db, ()> { @@ -296,7 +297,7 @@ impl<'db> Evaluator<'db> { it: EvalLangItem, generic_args: GenericArgs<'db>, args: &[IntervalAndTy<'db>], - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, ) -> Result<'db, Vec<u8>> { use EvalLangItem::*; @@ -368,7 +369,7 @@ impl<'db> Evaluator<'db> { id: i64, args: &[IntervalAndTy<'db>], destination: Interval, - _locals: &Locals, + _locals: &Locals<'a>, _span: MirSpan, ) -> Result<'db, ()> { match id { @@ -399,7 +400,7 @@ impl<'db> Evaluator<'db> { args: &[IntervalAndTy<'db>], _generic_args: GenericArgs<'db>, destination: Interval, - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, ) -> Result<'db, ()> { match as_str { @@ -563,7 +564,7 @@ impl<'db> Evaluator<'db> { args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, destination: Interval, - locals: &Locals, + locals: &Locals<'a>, span: MirSpan, needs_override: bool, ) -> Result<'db, bool> { @@ -1202,11 +1203,7 @@ impl<'db> Evaluator<'db> { let addr = tuple.interval.addr.offset(offset); args.push(IntervalAndTy::new(addr, field, self, locals)?); } - if let Some(target) = self.lang_items().FnOnce - && let Some(def) = target - .trait_items(self.db) - .method_by_name(&Name::new_symbol_root(sym::call_once)) - { + if let Some(def) = self.lang_items().FnOnce_call_once { self.exec_fn_trait( def, &args, @@ -1345,7 +1342,7 @@ impl<'db> Evaluator<'db> { &mut self, ty: Ty<'db>, metadata: Interval, - locals: &Locals, + locals: &Locals<'a>, ) -> Result<'db, (usize, usize)> { Ok(match ty.kind() { TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1), @@ -1360,7 +1357,7 @@ impl<'db> Evaluator<'db> { "dyn concrete type", )?, TyKind::Adt(adt_def, subst) => { - let id = adt_def.def_id().0; + let id = adt_def.def_id(); let layout = self.layout_adt(id, subst)?; let id = match id { AdtId::StructId(s) => s, @@ -1373,7 +1370,8 @@ impl<'db> Evaluator<'db> { .unwrap() .1 .get() - .instantiate(self.interner(), subst); + .instantiate(self.interner(), subst) + .skip_norm_wip(); let sized_part_size = layout.fields.offset(field_types.iter().count() - 1).bytes_usize(); let sized_part_align = layout.align.bytes() as usize; @@ -1404,7 +1402,7 @@ impl<'db> Evaluator<'db> { args: &[IntervalAndTy<'db>], generic_args: GenericArgs<'db>, destination: Interval, - locals: &Locals, + locals: &Locals<'a>, _span: MirSpan, ) -> Result<'db, ()> { // We are a single threaded runtime with no UB checking and no optimization, so diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs index e0b3e571b8..074c5a9c77 100644 --- a/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -6,14 +6,14 @@ use crate::consteval::try_const_usize; use super::*; -impl<'db> Evaluator<'db> { +impl<'a, 'db: 'a> Evaluator<'a, 'db> { fn detect_simd_ty(&self, ty: Ty<'db>) -> Result<'db, (usize, Ty<'db>)> { match ty.kind() { TyKind::Adt(adt_def, subst) => { let len = match subst.as_slice().get(1).and_then(|it| it.konst()) { Some(len) => len, _ => { - if let AdtId::StructId(id) = adt_def.def_id().0 { + if let AdtId::StructId(id) = adt_def.def_id() { let struct_data = id.fields(self.db); let fields = struct_data.fields(); let Some((first_field, _)) = fields.iter().next() else { @@ -21,7 +21,8 @@ impl<'db> Evaluator<'db> { }; let field_ty = self.db.field_types(id.into())[first_field] .get() - .instantiate(self.interner(), subst); + .instantiate(self.interner(), subst) + .skip_norm_wip(); return Ok((fields.len(), field_ty)); } return Err(MirEvalError::InternalError( @@ -53,7 +54,7 @@ impl<'db> Evaluator<'db> { args: &[IntervalAndTy<'db>], _generic_args: GenericArgs<'db>, destination: Interval, - _locals: &Locals, + _locals: &Locals<'a>, _span: MirSpan, ) -> Result<'db, ()> { match name { diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 0f0ed729c9..3852db909e 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -4,8 +4,8 @@ use std::{fmt::Write, iter, mem}; use base_db::Crate; use hir_def::{ - AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GeneralConstId, GenericParamId, - HasModule, ItemContainerId, LocalFieldId, Lookup, TraitId, TupleId, + AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule, + ItemContainerId, LocalFieldId, Lookup, TraitId, TupleId, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ClosureKind, ExprId, ExprOrPatId, @@ -22,19 +22,18 @@ use itertools::{EitherOrBoth, Itertools}; use la_arena::ArenaMap; use rustc_apfloat::Float; use rustc_hash::FxHashMap; -use rustc_type_ir::inherent::{AdtDef, Const as _, GenericArgs as _, IntoKind, Ty as _}; +use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _}; use span::{Edition, FileId}; use syntax::TextRange; -use triomphe::Arc; use crate::{ - Adjust, Adjustment, AutoBorrow, CallableDefId, ParamEnvAndCrate, + Adjust, Adjustment, AutoBorrow, CallableDefId, InferBodyId, ParamEnvAndCrate, consteval::ConstEvalError, - db::{HirDatabase, InternedClosure, InternedClosureId}, + db::{GeneralConstId, HirDatabase, InternedClosure, InternedClosureId}, display::{DisplayTarget, HirDisplay, hir_display_with_store}, generics::generics, infer::{ - CaptureSourceStack, CapturedPlace, TypeMismatch, UpvarCapture, + CaptureSourceStack, CapturedPlace, UpvarCapture, cast::CastTy, closure::analysis::expr_use_visitor::{ Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, @@ -82,7 +81,8 @@ struct DropScope { struct MirLowerCtx<'a, 'db> { result: MirBody, - owner: DefWithBodyId, + owner: InferBodyId, + store_owner: ExpressionStoreOwnerId, current_loop_blocks: Option<LoopBlocks>, labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>, discr_temp: Option<Place>, @@ -110,8 +110,7 @@ pub enum MirLowerError { UnresolvedMethod(String), UnresolvedField, UnsizedTemporary(StoredTy), - MissingFunctionDefinition(DefWithBodyId, ExprId), - TypeMismatch(TypeMismatch), + MissingFunctionDefinition(InferBodyId, ExprId), HasErrors, /// This should never happen. Type mismatch should catch everything. TypeError(&'static str), @@ -190,20 +189,21 @@ impl MirLowerError { } } MirLowerError::MissingFunctionDefinition(owner, it) => { - let body = Body::of(db, *owner); + let owner = owner.expression_store_owner(db); + let store = ExpressionStore::of(db, owner); writeln!( f, "Missing function definition for {}", - body.pretty_print_expr(db, *owner, *it, display_target.edition) + hir_def::expr_store::pretty::print_expr_hir( + db, + store, + owner, + *it, + display_target.edition + ) )?; } MirLowerError::HasErrors => writeln!(f, "Type inference result contains errors")?, - MirLowerError::TypeMismatch(e) => writeln!( - f, - "Type mismatch: Expected {}, found {}", - e.expected.as_ref().display(db, display_target), - e.actual.as_ref().display(db, display_target), - )?, MirLowerError::GenericArgNotProvided(id, subst) => { let param_name = match *id { GenericParamId::TypeParamId(id) => { @@ -289,7 +289,7 @@ type Result<'db, T> = std::result::Result<T, MirLowerError>; impl<'a, 'db> MirLowerCtx<'a, 'db> { fn new( db: &'db dyn HirDatabase, - owner: DefWithBodyId, + owner: InferBodyId, store: &'a ExpressionStore, infer: &'a InferenceResult, ) -> Self { @@ -312,8 +312,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { owner, closures: vec![], }; - let resolver = owner.resolver(db); - let env = db.trait_environment(ExpressionStoreOwnerId::from(owner)); + let store_owner = owner.expression_store_owner(db); + let resolver = store_owner.resolver(db); + let env = db.trait_environment(store_owner); let interner = DbInterner::new_with(db, resolver.krate()); // FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body? let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); @@ -325,6 +326,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { store, types: crate::next_solver::default_types(db), owner, + store_owner, resolver, current_loop_blocks: None, labeled_loop_blocks: Default::default(), @@ -475,7 +477,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { not_supported!("builtin#asm") } Expr::Missing => { - if let DefWithBodyId::FunctionId(f) = self.owner { + if let Some(f) = self.owner.as_function() { let assoc = f.lookup(self.db); if let ItemContainerId::TraitId(t) = assoc.container { let name = &FunctionSignature::of(self.db, f).name; @@ -505,7 +507,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } } else { let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, expr_id); + self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id); let hygiene = self.store.expr_path_hygiene(expr_id); let result = self .resolver @@ -563,14 +565,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { Ok(Some(current)) } ValueNs::GenericParam(p) => { - let Some(def) = self.owner.as_generic_def_id(self.db) else { - not_supported!("owner without generic def id"); - }; + let def = self.owner.generic_def(self.db); let generics = generics(self.db, def); - let index = generics - .type_or_const_param_idx(p.into()) - .ok_or(MirLowerError::TypeError("fail to lower const generic param"))? - as u32; + let index = generics.type_or_const_param_idx(p.into()); self.push_assignment( current, place, @@ -581,7 +578,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { ParamConst { id: p, index }, ) .store(), - ty: self.db.const_param_ty_ns(p).store(), + ty: self.db.const_param_ty(p).store(), }, span: None, }), @@ -626,7 +623,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; self.push_fake_read(current, cond_place, expr_id.into()); let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, expr_id); + self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id); let (then_target, else_target) = self.pattern_match(current, None, cond_place, *pat)?; self.resolver.reset_to_guard(resolver_guard); @@ -764,7 +761,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_fake_read(current, cond_place, expr_id.into()); let mut end = None; let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, expr_id); + self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id); for MatchArm { pat, guard, expr } in arms.iter() { let (then, mut otherwise) = self.pattern_match(current, None, cond_place, *pat)?; @@ -886,13 +883,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { RecordSpread::FieldDefaults => not_supported!("empty record spread"), }; let variant_id = - self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path { - Some(p) => MirLowerError::UnresolvedName( - hir_display_with_store(&**p, self.store) + self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| { + MirLowerError::UnresolvedName( + hir_display_with_store(path, self.store) .display(self.db, self.display_target()) .to_string(), - ), - None => MirLowerError::RecordLiteralWithoutPath, + ) })?; let subst = match self.expr_ty_without_adjust(expr_id).kind() { TyKind::Adt(_, s) => s, @@ -1194,7 +1190,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; self.push_fake_read(current, value, expr_id.into()); let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, expr_id); + self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id); current = self.pattern_match_assignment(current, value, target)?; self.resolver.reset_to_guard(resolver_guard); Ok(Some(current)) @@ -1255,7 +1251,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { not_supported!("closure with non closure type"); }; self.result.closures.push(id.0); - let closure_data = &self.infer.closures_data[&id.0.loc(self.db).1]; + let closure_data = &self.infer.closures_data[&id.0.loc(self.db).expr]; let span = |sources: &[CaptureSourceStack]| match sources .first() @@ -1541,23 +1537,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { subst: GenericArgs<'db>, const_id: GeneralConstId, ) -> Result<'db, Operand> { - if matches!(const_id, GeneralConstId::AnonConstId(_)) { - // FIXME: - not_supported!("anon consts are not supported yet in const eval"); - } let konst = Const::new_unevaluated( self.interner(), UnevaluatedConst { def: const_id.into(), args: subst }, ); - let ty = self - .db - .value_ty(match const_id { - GeneralConstId::ConstId(id) => id.into(), - GeneralConstId::StaticId(id) => id.into(), - GeneralConstId::AnonConstId(_) => unreachable!("handled above"), - }) - .unwrap() - .instantiate(self.interner(), subst); + let ty = match const_id { + GeneralConstId::ConstId(id) => self.db.value_ty(id.into()).unwrap(), + GeneralConstId::StaticId(id) => self.db.value_ty(id.into()).unwrap(), + GeneralConstId::AnonConstId(id) => id.loc(self.db).ty.get(), + }; + let ty = ty.instantiate(self.interner(), subst).skip_norm_wip(); Ok(Operand { kind: OperandKind::Constant { konst: konst.store(), ty: ty.store() }, span: None, @@ -1841,8 +1830,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_fake_read(current, init_place, span); // Using the initializer for the resolver scope is good enough for us, as it cannot create new declarations // and has all declarations of the `let`. - let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, *expr_id); + let resolver_guard = self.resolver.update_to_inner_scope( + self.db, + self.store_owner, + *expr_id, + ); (current, else_block) = self.pattern_match(current, None, init_place, *pat)?; self.resolver.reset_to_guard(resolver_guard); @@ -1973,8 +1965,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { match self.result.binding_locals.get(b) { Some(it) => Ok(*it), None => { - // FIXME: It should never happens, but currently it will happen in `const_dependent_on_local` test, which - // is a hir lowering problem IMO. + // FIXME: It should never happens, but currently it will happen in some cases, not sure when exactly. // never!("Using inaccessible local for binding is always a bug"); Err(MirLowerError::InaccessibleLocal) } @@ -2098,7 +2089,7 @@ fn convert_closure_capture_projections( } TyKind::Adt(adt_def, _) => { let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx)); - let field = match adt_def.def_id().0 { + let field = match adt_def.def_id() { AdtId::StructId(id) => { FieldId { parent: id.into(), local_id: local_field_id } } @@ -2141,16 +2132,15 @@ fn cast_kind<'db>( }) } +#[salsa_macros::tracked(returns(ref), cycle_result = mir_body_for_closure_cycle_result)] pub fn mir_body_for_closure_query<'db>( db: &'db dyn HirDatabase, closure: InternedClosureId, -) -> Result<'db, Arc<MirBody>> { - let InternedClosure(owner, expr) = closure.loc(db); - let body_owner = - owner.as_def_with_body().expect("MIR lowering should only happen for body-owned closures"); - let body = Body::of(db, body_owner); +) -> Result<'db, MirBody> { + let InternedClosure { owner: body_owner, expr, .. } = closure.loc(db); + let store = ExpressionStore::of(db, body_owner.expression_store_owner(db)); let infer = InferenceResult::of(db, body_owner); - let Expr::Closure { args, body: root, .. } = &body[expr] else { + let Expr::Closure { args, body: root, .. } = &store[expr] else { implementation_error!("closure expression is not closure"); }; let crate::next_solver::TyKind::Closure(_, substs) = infer.expr_ty(expr).kind() else { @@ -2158,7 +2148,7 @@ pub fn mir_body_for_closure_query<'db>( }; let kind = substs.as_closure().kind(); let captures = infer.closures_data[&expr].min_captures.values().flatten(); - let mut ctx = MirLowerCtx::new(db, body_owner, &body.store, infer); + let mut ctx = MirLowerCtx::new(db, body_owner, store, infer); // 0 is return local ctx.result.locals.alloc(Local { ty: infer.expr_ty(*root).store() }); @@ -2183,7 +2173,7 @@ pub fn mir_body_for_closure_query<'db>( ctx.result.param_locals.push(closure_local); let sig = ctx.interner().signature_unclosure(substs.as_closure().sig(), Safety::Safe); - let resolver_guard = ctx.resolver.update_to_inner_scope(db, body_owner, expr); + let resolver_guard = ctx.resolver.update_to_inner_scope(db, ctx.store_owner, expr); let current = ctx.lower_params_and_bindings( args.iter().zip(sig.skip_binder().inputs().iter()).map(|(it, y)| (*it, *y)), None, @@ -2297,49 +2287,66 @@ pub fn mir_body_for_closure_query<'db>( return Err(MirLowerError::UnresolvedUpvar(err)); } ctx.result.shrink_to_fit(); - Ok(Arc::new(ctx.result)) + Ok(ctx.result) } -pub fn mir_body_query<'db>( - db: &'db dyn HirDatabase, - def: DefWithBodyId, -) -> Result<'db, Arc<MirBody>> { +#[salsa_macros::tracked(returns(ref), cycle_result = mir_body_cycle_result)] +pub fn mir_body_query<'db>(db: &'db dyn HirDatabase, def: InferBodyId) -> Result<'db, MirBody> { let krate = def.krate(db); let edition = krate.data(db).edition; let detail = match def { - DefWithBodyId::FunctionId(it) => { + InferBodyId::DefWithBodyId(DefWithBodyId::FunctionId(it)) => { FunctionSignature::of(db, it).name.display(db, edition).to_string() } - DefWithBodyId::StaticId(it) => { + InferBodyId::DefWithBodyId(DefWithBodyId::StaticId(it)) => { StaticSignature::of(db, it).name.display(db, edition).to_string() } - DefWithBodyId::ConstId(it) => ConstSignature::of(db, it) + InferBodyId::DefWithBodyId(DefWithBodyId::ConstId(it)) => ConstSignature::of(db, it) .name .clone() .unwrap_or_else(Name::missing) .display(db, edition) .to_string(), - DefWithBodyId::VariantId(it) => { + InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(it)) => { let loc = it.lookup(db); loc.parent.enum_variants(db).variants[loc.index as usize] .1 .display(db, edition) .to_string() } + InferBodyId::AnonConstId(_) => "{const}".to_owned(), }; let _p = tracing::info_span!("mir_body_query", ?detail).entered(); - let body = Body::of(db, def); + let (store, root_expr, self_param, params) = match def { + InferBodyId::DefWithBodyId(def) => { + let body = Body::of(db, def); + (&**body, body.root_expr(), body.self_param(), &*body.params) + } + InferBodyId::AnonConstId(def) => { + let loc = def.loc(db); + let store = ExpressionStore::of(db, loc.owner); + (store, loc.expr, None, &[][..]) + } + }; let infer = InferenceResult::of(db, def); - let mut result = lower_body_to_mir(db, def, body, infer, body.root_expr())?; + let mut result = lower_body_to_mir(db, def, store, infer, root_expr, self_param, params)?; result.shrink_to_fit(); - Ok(Arc::new(result)) + Ok(result) } -pub(crate) fn mir_body_cycle_result<'db>( +fn mir_body_cycle_result<'db>( _db: &'db dyn HirDatabase, _: salsa::Id, - _def: DefWithBodyId, -) -> Result<'db, Arc<MirBody>> { + _def: InferBodyId, +) -> Result<'db, MirBody> { + Err(MirLowerError::Loop) +} + +fn mir_body_for_closure_cycle_result<'db>( + _db: &'db dyn HirDatabase, + _: salsa::Id, + _def: InternedClosureId, +) -> Result<'db, MirBody> { Err(MirLowerError::Loop) } @@ -2347,42 +2354,31 @@ pub(crate) fn mir_body_cycle_result<'db>( /// then delegates to [`lower_to_mir_with_store`]. pub fn lower_body_to_mir<'db>( db: &'db dyn HirDatabase, - owner: DefWithBodyId, - body: &Body, + owner: InferBodyId, + store: &ExpressionStore, infer: &InferenceResult, - // FIXME: root_expr should always be the body.body_expr, - // but this is currently also used for `X` in `[(); X]` which live in the same expression store root_expr: ExprId, + self_param: Option<BindingId>, + params: &[PatId], ) -> Result<'db, MirBody> { - let is_root = root_expr == body.root_expr(); // Extract params and self_param only when lowering the body's root expression for a function. - if is_root && let DefWithBodyId::FunctionId(fid) = owner { + if let Some(fid) = owner.as_function() { let callable_sig = db.callable_item_signature(fid.into()).instantiate_identity().skip_binder(); let mut param_tys = callable_sig.inputs().iter().copied(); - let self_param = body.self_param.and_then(|id| Some((id, param_tys.next()?))); + let self_param = self_param.and_then(|id| Some((id, param_tys.next()?))); lower_to_mir_with_store( db, owner, - &body.store, + store, infer, root_expr, - body.params.iter().copied().zip(param_tys), + params.iter().copied().zip(param_tys), self_param, - is_root, ) } else { - lower_to_mir_with_store( - db, - owner, - &body.store, - infer, - root_expr, - iter::empty(), - None, - is_root, - ) + lower_to_mir_with_store(db, owner, store, infer, root_expr, iter::empty(), None) } } @@ -2392,24 +2388,22 @@ pub fn lower_body_to_mir<'db>( /// const (picks bindings owned by `root_expr`). pub fn lower_to_mir_with_store<'db>( db: &'db dyn HirDatabase, - owner: DefWithBodyId, + owner: InferBodyId, store: &ExpressionStore, infer: &InferenceResult, root_expr: ExprId, params: impl Iterator<Item = (PatId, Ty<'db>)> + Clone, self_param: Option<(BindingId, Ty<'db>)>, - is_root: bool, ) -> Result<'db, MirBody> { - if infer.type_mismatches().next().is_some() || infer.is_erroneous() { + if infer.has_type_mismatches() || infer.is_erroneous() { return Err(MirLowerError::HasErrors); } let mut ctx = MirLowerCtx::new(db, owner, store, infer); // 0 is return local ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr).store() }); - let binding_picker = |b: BindingId| { - let owner = ctx.store.binding_owner(b); - if is_root { owner.is_none() } else { owner == Some(root_expr) } - }; + let expected_binding_owner = + if matches!(owner, InferBodyId::DefWithBodyId(_)) { None } else { Some(root_expr) }; + let binding_picker = |b: BindingId| ctx.store.binding_owner(b) == expected_binding_owner; let current = ctx.lower_params_and_bindings(params, self_param, binding_picker)?; if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?; diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index fb4a9add81..2ed7aedecf 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -1,7 +1,6 @@ //! MIR lowering for places use hir_def::FunctionId; -use intern::sym; use rustc_type_ir::inherent::{Region as _, Ty as _}; use super::*; @@ -131,7 +130,7 @@ impl<'db> MirLowerCtx<'_, 'db> { match &self.store[expr_id] { Expr::Path(p) => { let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, expr_id); + self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id); let hygiene = self.store.expr_path_hygiene(expr_id); let resolved = self.resolver.resolve_path_in_value_ns_fully(self.db, p, hygiene); self.resolver.reset_to_guard(resolver_guard); @@ -183,10 +182,7 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id.into(), 'b: { if let Some((f, _)) = self.infer.method_resolution(expr_id) - && let Some(deref_trait) = self.lang_items().DerefMut - && let Some(deref_fn) = deref_trait - .trait_items(self.db) - .method_by_name(&Name::new_symbol_root(sym::deref_mut)) + && let Some(deref_fn) = self.lang_items().DerefMut_deref_mut { break 'b deref_fn == f; } @@ -315,18 +311,12 @@ impl<'db> MirLowerCtx<'_, 'db> { mutability: bool, ) -> Result<'db, Option<(Place, BasicBlockId)>> { let lang_items = self.lang_items(); - let (mutability, trait_lang_item, trait_method_name, borrow_kind) = if !mutability { - ( - Mutability::Not, - lang_items.Deref, - Name::new_symbol_root(sym::deref), - BorrowKind::Shared, - ) + let (mutability, deref_fn, borrow_kind) = if !mutability { + (Mutability::Not, lang_items.Deref_deref, BorrowKind::Shared) } else { ( Mutability::Mut, - lang_items.DerefMut, - Name::new_symbol_root(sym::deref_mut), + lang_items.DerefMut_deref_mut, BorrowKind::Mut { kind: MutBorrowKind::Default }, ) }; @@ -335,11 +325,7 @@ impl<'db> MirLowerCtx<'_, 'db> { let target_ty_ref = Ty::new_ref(self.interner(), error_region, target_ty, mutability); let ref_place: Place = self.temp(ty_ref, current, span)?.into(); self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span); - let deref_trait = trait_lang_item.ok_or(MirLowerError::LangItemNotFound)?; - let deref_fn = deref_trait - .trait_items(self.db) - .method_by_name(&trait_method_name) - .ok_or(MirLowerError::LangItemNotFound)?; + let deref_fn = deref_fn.ok_or(MirLowerError::LangItemNotFound)?; let deref_fn_op = Operand::const_zst(Ty::new_fn_def( self.interner(), CallableDefId::FunctionId(deref_fn).into(), diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 99c5f0fc65..c924c5bdf0 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -4,7 +4,7 @@ use hir_def::{hir::ExprId, signatures::VariantFields}; use rustc_type_ir::inherent::{IntoKind, Ty as _}; use crate::{ - BindingMode, + BindingMode, ByRef, mir::{ LocalId, MutBorrowKind, Operand, OperandKind, lower::{ @@ -104,7 +104,7 @@ impl<'db> MirLowerCtx<'_, 'db> { ) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> { self.pattern_match_binding( id, - BindingMode::Move, + BindingMode(ByRef::No, rustc_ast_ir::Mutability::Not), local.into(), MirSpan::SelfParam, current, @@ -132,7 +132,7 @@ impl<'db> MirLowerCtx<'_, 'db> { .into(), ); Ok(match &self.store[pattern] { - Pat::Missing => return Err(MirLowerError::IncompletePattern), + Pat::Missing | Pat::Rest => return Err(MirLowerError::IncompletePattern), Pat::Wild => (current, current_else), Pat::Tuple { args, ellipsis } => { let subst = match self.infer.pat_ty(pattern).kind() { @@ -248,9 +248,18 @@ impl<'db> MirLowerCtx<'_, 'db> { (current, current_else) } Pat::Slice { prefix, slice, suffix } => { + let pat_ty = self.infer.pat_ty(pattern); + // FIXME: MIR lowering should be skipped for bodies with inference errors. Once + // that happens, this recovery for invalid slice patterns can be removed. + if !matches!(pat_ty.kind(), TyKind::Array(..) | TyKind::Slice(_)) { + return Err(MirLowerError::TypeError( + "non array or slice type matched with slice pattern", + )); + } + if mode == MatchingMode::Check { // emit runtime length check for slice - if let TyKind::Slice(_) = self.infer.pat_ty(pattern).kind() { + if let TyKind::Slice(_) = pat_ty.kind() { let pattern_len = prefix.len() + suffix.len(); let place_len: Place = self .temp(Ty::new_usize(self.interner()), current, pattern.into())? @@ -385,7 +394,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.push_match_assignment( current, local, - BindingMode::Move, + BindingMode(ByRef::No, rustc_ast_ir::Mutability::Not), cond_place, pattern.into(), ); @@ -504,6 +513,7 @@ impl<'db> MirLowerCtx<'_, 'db> { (current, current_else) } Pat::Box { .. } => not_supported!("box pattern"), + Pat::Deref { .. } => not_supported!("deref pattern"), Pat::ConstBlock(_) => not_supported!("const block pattern"), }) } @@ -535,13 +545,13 @@ impl<'db> MirLowerCtx<'_, 'db> { current, target_place.into(), match mode { - BindingMode::Move => { + BindingMode(ByRef::No, _) => { Operand { kind: OperandKind::Copy(cond_place), span: None }.into() } - BindingMode::Ref(rustc_ast_ir::Mutability::Not) => { + BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Not), _) => { Rvalue::Ref(BorrowKind::Shared, cond_place) } - BindingMode::Ref(rustc_ast_ir::Mutability::Mut) => { + BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => { Rvalue::Ref(BorrowKind::Mut { kind: MutBorrowKind::Default }, cond_place) } }, diff --git a/crates/hir-ty/src/mir/lower/tests.rs b/crates/hir-ty/src/mir/lower/tests.rs index 73399dab7f..8e10284cc1 100644 --- a/crates/hir-ty/src/mir/lower/tests.rs +++ b/crates/hir-ty/src/mir/lower/tests.rs @@ -78,7 +78,7 @@ fn check_borrowck(#[rust_analyzer::rust_fixture] ra_fixture: &str) { } for body in bodies { - let _ = db.borrowck(body); + let _ = db.borrowck(body.into()); } }) } diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index 41044f00c2..06871a3f18 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -7,15 +7,13 @@ //! //! So the monomorphization should be called even if the substitution is empty. -use hir_def::DefWithBodyId; use rustc_type_ir::inherent::IntoKind; use rustc_type_ir::{ FallibleTypeFolder, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeVisitableExt, }; -use triomphe::Arc; use crate::{ - ParamEnvAndCrate, + InferBodyId, ParamEnvAndCrate, next_solver::{ Allocation, AllocationData, Const, ConstKind, Region, RegionKind, StoredConst, StoredGenericArgs, StoredTy, @@ -65,6 +63,9 @@ impl<'db> FallibleTypeFolder<DbInterner<'db>> for Filler<'db> { ty, ) .map_err(|_| MirLowerError::NotSupported("can't normalize alias".to_owned()))?; + // Normalization could introduce infer vars (for example, if the alias cannot be normalized), + // and we must not have infer vars in the body. + let ty = ty.replace_infer_with_error(self.infcx.interner); ty.try_super_fold_with(self) } TyKind::Param(param) => Ok(self @@ -237,38 +238,50 @@ impl<'db> Filler<'db> { } } +#[salsa_macros::tracked(returns(ref), cycle_result = monomorphized_mir_body_cycle_result)] pub fn monomorphized_mir_body_query( db: &dyn HirDatabase, - owner: DefWithBodyId, + owner: InferBodyId, subst: StoredGenericArgs, trait_env: StoredParamEnvAndCrate, -) -> Result<Arc<MirBody>, MirLowerError> { +) -> Result<MirBody, MirLowerError> { let mut filler = Filler::new(db, trait_env.as_ref(), subst.as_ref()); let body = db.mir_body(owner)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; - Ok(Arc::new(body)) + Ok(body) } -pub(crate) fn monomorphized_mir_body_cycle_result( +fn monomorphized_mir_body_cycle_result( _db: &dyn HirDatabase, _: salsa::Id, - _: DefWithBodyId, + _: InferBodyId, _: StoredGenericArgs, _: StoredParamEnvAndCrate, -) -> Result<Arc<MirBody>, MirLowerError> { +) -> Result<MirBody, MirLowerError> { Err(MirLowerError::Loop) } +#[salsa_macros::tracked(returns(ref), cycle_result = monomorphized_mir_body_for_closure_cycle_result)] pub fn monomorphized_mir_body_for_closure_query( db: &dyn HirDatabase, closure: InternedClosureId, subst: StoredGenericArgs, trait_env: StoredParamEnvAndCrate, -) -> Result<Arc<MirBody>, MirLowerError> { +) -> Result<MirBody, MirLowerError> { let mut filler = Filler::new(db, trait_env.as_ref(), subst.as_ref()); let body = db.mir_body_for_closure(closure)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; - Ok(Arc::new(body)) + Ok(body) +} + +fn monomorphized_mir_body_for_closure_cycle_result( + _db: &dyn HirDatabase, + _: salsa::Id, + _: InternedClosureId, + _: StoredGenericArgs, + _: StoredParamEnvAndCrate, +) -> Result<MirBody, MirLowerError> { + Err(MirLowerError::Loop) } diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs index de5ee223a1..777cf170bc 100644 --- a/crates/hir-ty/src/mir/pretty.rs +++ b/crates/hir-ty/src/mir/pretty.rs @@ -7,7 +7,7 @@ use std::{ use either::Either; use hir_def::{ - expr_store::Body, + expr_store::ExpressionStore, hir::BindingId, signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature}, }; @@ -15,6 +15,7 @@ use hir_expand::{Lookup, name::Name}; use la_arena::ArenaMap; use crate::{ + InferBodyId, db::{HirDatabase, InternedClosureId}, display::{ClosureStyle, DisplayTarget, HirDisplay}, mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind}, @@ -42,18 +43,18 @@ macro_rules! wln { impl MirBody { pub fn pretty_print(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String { - let hir_body = Body::of(db, self.owner); + let hir_body = ExpressionStore::of(db, self.owner.expression_store_owner(db)); let mut ctx = MirPrettyCtx::new(self, hir_body, db, display_target); ctx.for_body(|this| match ctx.body.owner { - hir_def::DefWithBodyId::FunctionId(id) => { + InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::FunctionId(id)) => { let data = FunctionSignature::of(db, id); w!(this, "fn {}() ", data.name.display(db, this.display_target.edition)); } - hir_def::DefWithBodyId::StaticId(id) => { + InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::StaticId(id)) => { let data = StaticSignature::of(db, id); w!(this, "static {}: _ = ", data.name.display(db, this.display_target.edition)); } - hir_def::DefWithBodyId::ConstId(id) => { + InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::ConstId(id)) => { let data = ConstSignature::of(db, id); w!( this, @@ -64,7 +65,7 @@ impl MirBody { .display(db, this.display_target.edition) ); } - hir_def::DefWithBodyId::VariantId(id) => { + InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::VariantId(id)) => { let loc = id.lookup(db); let edition = this.display_target.edition; w!( @@ -78,6 +79,7 @@ impl MirBody { .display(db, edition), ) } + InferBodyId::AnonConstId(_) => w!(this, "{{const}}"), }); ctx.result } @@ -97,7 +99,7 @@ impl MirBody { struct MirPrettyCtx<'a, 'db> { body: &'a MirBody, - hir_body: &'a Body, + hir_body: &'a ExpressionStore, db: &'db dyn HirDatabase, result: String, indent: String, @@ -160,7 +162,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { let result = mem::take(&mut self.result); let indent = mem::take(&mut self.indent); let mut ctx = MirPrettyCtx { - body: &body, + body, local_to_binding: body.local_to_binding_map(), result, indent, @@ -184,7 +186,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { fn new( body: &'a MirBody, - hir_body: &'a Body, + hir_body: &'a ExpressionStore, db: &'db dyn HirDatabase, display_target: DisplayTarget, ) -> Self { diff --git a/crates/hir-ty/src/next_solver.rs b/crates/hir-ty/src/next_solver.rs index 161a3142df..47b4b1dc4a 100644 --- a/crates/hir-ty/src/next_solver.rs +++ b/crates/hir-ty/src/next_solver.rs @@ -39,6 +39,7 @@ pub use interner::*; pub use opaques::*; pub use predicate::*; pub use region::*; +use rustc_type_ir::MayBeErased; pub use solver::*; pub use ty::*; @@ -48,6 +49,7 @@ pub use rustc_ast_ir::Mutability; pub type Binder<'db, T> = rustc_type_ir::Binder<DbInterner<'db>, T>; pub type EarlyBinder<'db, T> = rustc_type_ir::EarlyBinder<DbInterner<'db>, T>; +pub type Unnormalized<'db, T> = rustc_type_ir::Unnormalized<DbInterner<'db>, T>; pub type Canonical<'db, T> = rustc_type_ir::Canonical<DbInterner<'db>, T>; pub type CanonicalVarValues<'db> = rustc_type_ir::CanonicalVarValues<DbInterner<'db>>; pub type CanonicalVarKind<'db> = rustc_type_ir::CanonicalVarKind<DbInterner<'db>>; @@ -55,7 +57,7 @@ pub type CanonicalQueryInput<'db, V> = rustc_type_ir::CanonicalQueryInput<DbInte pub type AliasTy<'db> = rustc_type_ir::AliasTy<DbInterner<'db>>; pub type FnSig<'db> = rustc_type_ir::FnSig<DbInterner<'db>>; pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig<DbInterner<'db>>>; -pub type TypingMode<'db> = rustc_type_ir::TypingMode<DbInterner<'db>>; +pub type TypingMode<'db, S = MayBeErased> = rustc_type_ir::TypingMode<DbInterner<'db>, S>; pub type TypeError<'db> = rustc_type_ir::error::TypeError<DbInterner<'db>>; pub type QueryResult<'db> = rustc_type_ir::solve::QueryResult<DbInterner<'db>>; pub type FxIndexMap<K, V> = rustc_type_ir::data_structures::IndexMap<K, V>; @@ -85,8 +87,13 @@ pub struct DefaultTypes<'db> { pub error: Ty<'db>, /// `&'static str` pub static_str_ref: Ty<'db>, + /// `[u8]` + pub u8_slice: Ty<'db>, + /// `&'static [u8]` + pub static_u8_slice: Ty<'db>, /// `*mut ()` pub mut_unit_ptr: Ty<'db>, + pub dyn_trait_dummy_self: Ty<'db>, } pub struct DefaultConsts<'db> { @@ -236,6 +243,8 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { let empty_tys = create_tys(&[]); let unit = create_ty(TyKind::Tuple(empty_tys)); let u8 = create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8)); + let u8_slice = create_ty(TyKind::Slice(u8)); + let static_u8_slice = create_ty(TyKind::Ref(statik, u8_slice, Mutability::Not)); DefaultAny { types: DefaultTypes { usize: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::Usize)), @@ -261,7 +270,11 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { never: create_ty(TyKind::Never), error: create_ty(TyKind::Error(ErrorGuaranteed)), static_str_ref: create_ty(TyKind::Ref(statik, str, rustc_ast_ir::Mutability::Not)), + u8_slice, + static_u8_slice, mut_unit_ptr: create_ty(TyKind::RawPtr(unit, rustc_ast_ir::Mutability::Mut)), + // This type must not appear anywhere except here. + dyn_trait_dummy_self: create_ty(TyKind::Infer(rustc_type_ir::InferTy::FreshTy(0))), }, consts: DefaultConsts { error: create_const(ConstKind::Error(ErrorGuaranteed)), diff --git a/crates/hir-ty/src/next_solver/abi.rs b/crates/hir-ty/src/next_solver/abi.rs index 1813abab86..3121d5631f 100644 --- a/crates/hir-ty/src/next_solver/abi.rs +++ b/crates/hir-ty/src/next_solver/abi.rs @@ -1,7 +1,10 @@ //! ABI-related things in the next-trait-solver. -use rustc_type_ir::{error::TypeError, relate::Relate}; - -use crate::FnAbi; +use rustc_abi::ExternAbi; +use rustc_ast_ir::visit::VisitorResult; +use rustc_type_ir::{ + FallibleTypeFolder, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, error::TypeError, + relate::Relate, +}; use super::interner::DbInterner; @@ -40,9 +43,32 @@ impl<'db> rustc_type_ir::inherent::Safety<DbInterner<'db>> for Safety { Self::Safe => "", } } + + fn unsafe_mode() -> Self { + Safety::Unsafe + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for ExternAbi { + fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, _visitor: &mut V) -> V::Result { + V::Result::output() + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for ExternAbi { + fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>( + self, + _folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(self) + } + + fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, _folder: &mut F) -> Self { + self + } } -impl<'db> Relate<DbInterner<'db>> for FnAbi { +impl<'db> Relate<DbInterner<'db>> for ExternAbi { fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( _relation: &mut R, a: Self, @@ -55,13 +81,3 @@ impl<'db> Relate<DbInterner<'db>> for FnAbi { } } } - -impl<'db> rustc_type_ir::inherent::Abi<DbInterner<'db>> for FnAbi { - fn rust() -> Self { - FnAbi::Rust - } - - fn is_rust(self) -> bool { - matches!(self, FnAbi::Rust) - } -} diff --git a/crates/hir-ty/src/next_solver/binder.rs b/crates/hir-ty/src/next_solver/binder.rs index 3645f8096c..95f437165d 100644 --- a/crates/hir-ty/src/next_solver/binder.rs +++ b/crates/hir-ty/src/next_solver/binder.rs @@ -1,9 +1,10 @@ -use crate::{ - FnAbi, - next_solver::{ - Binder, Clauses, EarlyBinder, FnSig, PolyFnSig, StoredBoundVarKinds, StoredClauses, - StoredTy, StoredTys, Ty, abi::Safety, - }, +use hir_def::TraitId; +use macros::{TypeFoldable, TypeVisitable}; + +use crate::next_solver::{ + Binder, Clauses, DbInterner, EarlyBinder, FnSig, FnSigKind, GenericArg, PolyFnSig, + StoredBoundVarKinds, StoredClauses, StoredGenericArg, StoredGenericArgs, StoredTy, StoredTys, + TraitRef, Ty, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -38,6 +39,13 @@ impl StoredEarlyBinder<StoredTy> { } } +impl StoredEarlyBinder<StoredGenericArg> { + #[inline] + pub fn get<'db>(&self) -> EarlyBinder<'db, GenericArg<'db>> { + self.get_with(|it| it.as_ref()) + } +} + impl StoredEarlyBinder<StoredClauses> { #[inline] pub fn get<'db>(&self) -> EarlyBinder<'db, Clauses<'db>> { @@ -45,13 +53,25 @@ impl StoredEarlyBinder<StoredClauses> { } } +impl StoredEarlyBinder<StoredPolyFnSig> { + #[inline] + pub fn get<'db>(&'db self) -> EarlyBinder<'db, PolyFnSig<'db>> { + self.get_with(|it| it.get()) + } +} + +impl StoredEarlyBinder<StoredTraitRef> { + #[inline] + pub fn get<'db>(&'db self, interner: DbInterner<'db>) -> EarlyBinder<'db, TraitRef<'db>> { + self.get_with(|it| it.get(interner)) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StoredPolyFnSig { bound_vars: StoredBoundVarKinds, inputs_and_output: StoredTys, - c_variadic: bool, - safety: Safety, - abi: FnAbi, + fn_sig_kind: FnSigKind<'static>, } impl StoredPolyFnSig { @@ -62,9 +82,11 @@ impl StoredPolyFnSig { Self { bound_vars, inputs_and_output: sig.inputs_and_output.store(), - c_variadic: sig.c_variadic, - safety: sig.safety, - abi: sig.abi, + fn_sig_kind: FnSigKind::new( + sig.fn_sig_kind.abi(), + sig.fn_sig_kind.safety(), + sig.fn_sig_kind.c_variadic(), + ), } } @@ -73,11 +95,28 @@ impl StoredPolyFnSig { Binder::bind_with_vars( FnSig { inputs_and_output: self.inputs_and_output.as_ref(), - c_variadic: self.c_variadic, - safety: self.safety, - abi: self.abi, + fn_sig_kind: self.fn_sig_kind, }, self.bound_vars.as_ref(), ) } } + +#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)] +pub struct StoredTraitRef { + #[type_visitable(ignore)] + def_id: TraitId, + args: StoredGenericArgs, +} + +impl StoredTraitRef { + #[inline] + pub fn new(trait_ref: TraitRef<'_>) -> Self { + Self { def_id: trait_ref.def_id.0, args: trait_ref.args.store() } + } + + #[inline] + pub fn get<'db>(&'db self, interner: DbInterner<'db>) -> TraitRef<'db> { + TraitRef::new_from_args(interner, self.def_id.into(), self.args.as_ref()) + } +} diff --git a/crates/hir-ty/src/next_solver/consts.rs b/crates/hir-ty/src/next_solver/consts.rs index fa90e3d8a0..2df9a5259d 100644 --- a/crates/hir-ty/src/next_solver/consts.rs +++ b/crates/hir-ty/src/next_solver/consts.rs @@ -11,13 +11,14 @@ use rustc_ast_ir::visit::VisitorResult; use rustc_type_ir::{ BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags, GenericTypeVisitable, InferConst, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, WithCachedTypeInfo, inherent::IntoKind, relate::Relate, + TypeVisitable, TypeVisitableExt, WithCachedTypeInfo, inherent::IntoKind, relate::Relate, }; use crate::{ ParamEnvAndCrate, next_solver::{ - AllocationData, impl_foldable_for_interned_slice, impl_stored_interned, interned_slice, + AllocationData, ClauseKind, ParamEnv, impl_foldable_for_interned_slice, + impl_stored_interned, interned_slice, }, }; @@ -146,6 +147,40 @@ impl std::fmt::Debug for ParamConst { } } +impl ParamConst { + pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> { + let mut candidates = env.clauses.iter().filter_map(|clause| { + // `ConstArgHasType` are never desugared to be higher ranked. + match clause.kind().skip_binder() { + ClauseKind::ConstArgHasType(param_ct, ty) => { + assert!(!(param_ct, ty).has_escaping_bound_vars()); + + match param_ct.kind() { + ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty), + _ => None, + } + } + _ => None, + } + }); + + // N.B. it may be tempting to fix ICEs by making this function return + // `Option<Ty<'db>>` instead of `Ty<'db>`; however, this is generally + // considered to be a bandaid solution, since it hides more important + // underlying issues with how we construct generics and predicates of + // items. It's advised to fix the underlying issue rather than trying + // to modify this function. + let ty = candidates.next().unwrap_or_else(|| { + panic!("cannot find `{self:?}` in param-env: {env:#?}"); + }); + assert!( + candidates.next().is_none(), + "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}" + ); + ty + } +} + #[derive( Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable, GenericTypeVisitable, )] diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs index 542eca3ded..63337b297d 100644 --- a/crates/hir-ty/src/next_solver/def_id.rs +++ b/crates/hir-ty/src/next_solver/def_id.rs @@ -1,9 +1,9 @@ //! Definition of `SolverDefId` use hir_def::{ - AdtId, AnonConstId, AttrDefId, BuiltinDeriveImplId, CallableDefId, ConstId, DefWithBodyId, - EnumId, EnumVariantId, ExpressionStoreOwnerId, FunctionId, GeneralConstId, GenericDefId, - ImplId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, + AdtId, AttrDefId, BuiltinDeriveImplId, CallableDefId, ConstId, DefWithBodyId, EnumId, + EnumVariantId, ExpressionStoreOwnerId, FunctionId, GenericDefId, ImplId, StaticId, StructId, + TraitId, TypeAliasId, UnionId, VariantId, signatures::{ ConstSignature, EnumSignature, FunctionSignature, StaticSignature, StructSignature, TraitSignature, TypeAliasSignature, UnionSignature, @@ -12,8 +12,12 @@ use hir_def::{ use rustc_type_ir::inherent; use stdx::impl_from; -use crate::db::{ - InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId, InternedOpaqueTyId, +use crate::{ + InferBodyId, + db::{ + AnonConstId, GeneralConstId, InternedClosureId, InternedCoroutineClosureId, + InternedCoroutineId, InternedOpaqueTyId, + }, }; use super::DbInterner; @@ -173,6 +177,16 @@ impl From<DefWithBodyId> for SolverDefId { } } +impl From<InferBodyId> for SolverDefId { + #[inline] + fn from(value: InferBodyId) -> Self { + match value { + InferBodyId::DefWithBodyId(id) => id.into(), + InferBodyId::AnonConstId(id) => id.into(), + } + } +} + impl From<VariantId> for SolverDefId { #[inline] fn from(value: VariantId) -> Self { @@ -246,6 +260,32 @@ impl TryFrom<SolverDefId> for DefWithBodyId { } } +impl TryFrom<SolverDefId> for InferBodyId { + type Error = (); + + #[inline] + fn try_from(value: SolverDefId) -> Result<Self, Self::Error> { + let id = match value { + SolverDefId::ConstId(id) => id.into(), + SolverDefId::FunctionId(id) => id.into(), + SolverDefId::StaticId(id) => id.into(), + SolverDefId::EnumVariantId(id) | SolverDefId::Ctor(Ctor::Enum(id)) => id.into(), + SolverDefId::AnonConstId(id) => id.into(), + SolverDefId::InternedOpaqueTyId(_) + | SolverDefId::TraitId(_) + | SolverDefId::TypeAliasId(_) + | SolverDefId::ImplId(_) + | SolverDefId::BuiltinDeriveImplId(_) + | SolverDefId::InternedClosureId(_) + | SolverDefId::InternedCoroutineId(_) + | SolverDefId::InternedCoroutineClosureId(_) + | SolverDefId::Ctor(Ctor::Struct(_)) + | SolverDefId::AdtId(_) => return Err(()), + }; + Ok(id) + } +} + impl TryFrom<SolverDefId> for GenericDefId { type Error = (); @@ -270,26 +310,6 @@ impl TryFrom<SolverDefId> for GenericDefId { } } -impl SolverDefId { - #[inline] - #[track_caller] - pub fn expect_opaque_ty(self) -> InternedOpaqueTyId { - match self { - SolverDefId::InternedOpaqueTyId(it) => it, - _ => panic!("expected opaque type, found {self:?}"), - } - } - - #[inline] - #[track_caller] - pub fn expect_type_alias(self) -> TypeAliasId { - match self { - SolverDefId::TypeAliasId(it) => it, - _ => panic!("expected type alias, found {self:?}"), - } - } -} - impl<'db> inherent::DefId<DbInterner<'db>> for SolverDefId { fn as_local(self) -> Option<SolverDefId> { Some(self) @@ -301,6 +321,26 @@ impl<'db> inherent::DefId<DbInterner<'db>> for SolverDefId { macro_rules! declare_id_wrapper { ($name:ident, $wraps:ident) => { + declare_id_wrapper!($name, $wraps, SolverDefId); + }; + + ($name:ident, $wraps:ident, $local:ident) => { + declare_id_wrapper!($name, $wraps, $local, no_try_from); + + impl TryFrom<SolverDefId> for $name { + type Error = (); + + #[inline] + fn try_from(value: SolverDefId) -> Result<Self, Self::Error> { + match value { + SolverDefId::$wraps(it) => Ok(Self(it)), + _ => Err(()), + } + } + } + }; + + ($name:ident, $wraps:ident, $local:ident, no_try_from) => { #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct $name(pub $wraps); @@ -331,35 +371,106 @@ macro_rules! declare_id_wrapper { } } - impl TryFrom<SolverDefId> for $name { + impl<'db> inherent::DefId<DbInterner<'db>, $local> for $name { + fn as_local(self) -> Option<$local> { + Some(self.into()) + } + fn is_local(self) -> bool { + true + } + } + }; +} + +declare_id_wrapper!(TraitIdWrapper, TraitId); +declare_id_wrapper!(TypeAliasIdWrapper, TypeAliasId); +declare_id_wrapper!(ClosureIdWrapper, InternedClosureId); +declare_id_wrapper!(CoroutineIdWrapper, InternedCoroutineId); +declare_id_wrapper!(CoroutineClosureIdWrapper, InternedCoroutineClosureId); +declare_id_wrapper!(AdtIdWrapper, AdtId); +declare_id_wrapper!(OpaqueTyIdWrapper, InternedOpaqueTyId, OpaqueTyIdWrapper); + +macro_rules! declare_ty_const_pair { + ( $ty_id_name:ident, $const_id_name:ident, $term_id_name:ident ) => { + declare_id_wrapper!($ty_id_name, TypeAliasId); + declare_id_wrapper!($const_id_name, ConstId); + declare_id_wrapper!($term_id_name, TermId, SolverDefId, no_try_from); + + impl TryFrom<SolverDefId> for $term_id_name { type Error = (); #[inline] fn try_from(value: SolverDefId) -> Result<Self, Self::Error> { match value { - SolverDefId::$wraps(it) => Ok(Self(it)), + SolverDefId::TypeAliasId(it) => Ok(Self(TermId::TypeAliasId(it))), + SolverDefId::ConstId(it) => Ok(Self(TermId::ConstId(it))), _ => Err(()), } } } - impl<'db> inherent::DefId<DbInterner<'db>> for $name { - fn as_local(self) -> Option<SolverDefId> { - Some(self.into()) + impl From<$ty_id_name> for $term_id_name { + fn from(value: $ty_id_name) -> Self { + $term_id_name(TermId::TypeAliasId(value.0)) } - fn is_local(self) -> bool { - true + } + + impl From<$const_id_name> for $term_id_name { + fn from(value: $const_id_name) -> Self { + $term_id_name(TermId::ConstId(value.0)) + } + } + + impl TryFrom<$term_id_name> for $ty_id_name { + type Error = (); + + fn try_from(value: $term_id_name) -> Result<Self, Self::Error> { + match value.0 { + TermId::TypeAliasId(id) => Ok($ty_id_name(id)), + TermId::ConstId(_) => Err(()), + } + } + } + + impl TryFrom<$term_id_name> for $const_id_name { + type Error = (); + + fn try_from(value: $term_id_name) -> Result<Self, Self::Error> { + match value.0 { + TermId::ConstId(id) => Ok($const_id_name(id)), + TermId::TypeAliasId(_) => Err(()), + } + } + } + + impl From<$const_id_name> for GeneralConstIdWrapper { + fn from(value: $const_id_name) -> Self { + GeneralConstIdWrapper(GeneralConstId::ConstId(value.0)) } } }; } -declare_id_wrapper!(TraitIdWrapper, TraitId); -declare_id_wrapper!(TypeAliasIdWrapper, TypeAliasId); -declare_id_wrapper!(ClosureIdWrapper, InternedClosureId); -declare_id_wrapper!(CoroutineIdWrapper, InternedCoroutineId); -declare_id_wrapper!(CoroutineClosureIdWrapper, InternedCoroutineClosureId); -declare_id_wrapper!(AdtIdWrapper, AdtId); +declare_ty_const_pair!(TraitAssocTyId, TraitAssocConstId, TraitAssocTermId); +declare_ty_const_pair!(ImplOrTraitAssocTyId, ImplOrTraitAssocConstId, ImplOrTraitAssocTermId); +declare_ty_const_pair!(FreeTyAliasId, FreeConstAliasId, FreeTermAliasId); +declare_ty_const_pair!(InherentAssocTyId, InherentAssocConstId, InherentAssocTermId); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum TermId { + TypeAliasId(TypeAliasId), + ConstId(ConstId), +} +impl_from!(TypeAliasId, ConstId for TermId); + +impl From<TermId> for SolverDefId { + fn from(value: TermId) -> Self { + match value { + TermId::TypeAliasId(id) => id.into(), + TermId::ConstId(id) => id.into(), + } + } +} #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct GeneralConstIdWrapper(pub GeneralConstId); diff --git a/crates/hir-ty/src/next_solver/format_proof_tree.rs b/crates/hir-ty/src/next_solver/format_proof_tree.rs index 66da6d5400..4b2f66a17d 100644 --- a/crates/hir-ty/src/next_solver/format_proof_tree.rs +++ b/crates/hir-ty/src/next_solver/format_proof_tree.rs @@ -3,7 +3,7 @@ use serde_derive::{Deserialize, Serialize}; use crate::next_solver::inspect::{InspectCandidate, InspectGoal}; use crate::next_solver::{AnyImplId, infer::InferCtxt}; -use crate::next_solver::{DbInterner, Span}; +use crate::{Span, next_solver::DbInterner}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProofTreeData { @@ -59,7 +59,7 @@ impl<'a, 'db> ProofTreeSerializer<'a, 'db> { let mut nested = Vec::new(); self.infcx.probe(|_| { - for nested_goal in candidate.instantiate_nested_goals() { + for nested_goal in candidate.instantiate_nested_goals(Span::Dummy) { nested.push(self.serialize_goal(&nested_goal)); } }); diff --git a/crates/hir-ty/src/next_solver/fulfill.rs b/crates/hir-ty/src/next_solver/fulfill.rs index 6739795a00..33dd33cb1d 100644 --- a/crates/hir-ty/src/next_solver/fulfill.rs +++ b/crates/hir-ty/src/next_solver/fulfill.rs @@ -9,17 +9,20 @@ use rustc_next_trait_solver::{ }; use rustc_type_ir::{ Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, - inherent::{IntoKind, Span as _}, + inherent::IntoKind, solve::{Certainty, NoSolution}, }; -use crate::next_solver::{ - DbInterner, SolverContext, SolverDefId, Span, Ty, TyKind, TypingMode, - infer::{ - InferCtxt, - traits::{PredicateObligation, PredicateObligations}, +use crate::{ + Span, + next_solver::{ + DbInterner, SolverContext, SolverDefId, Ty, TyKind, TypingMode, + infer::{ + InferCtxt, + traits::{PredicateObligation, PredicateObligations}, + }, + inspect::ProofTreeVisitor, }, - inspect::ProofTreeVisitor, }; type PendingObligations<'db> = @@ -36,7 +39,7 @@ type PendingObligations<'db> = /// /// It is also likely that we want to use slightly different datastructures /// here as this will have to deal with far more root goals than `evaluate_all`. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct FulfillmentCtxt<'db> { obligations: ObligationStorage<'db>, @@ -44,7 +47,6 @@ pub struct FulfillmentCtxt<'db> { /// outside of this snapshot leads to subtle bugs if the snapshot /// gets rolled back. Because of this we explicitly check that we only /// use the context in exactly this snapshot. - #[expect(unused)] usable_in_snapshot: usize, try_evaluate_obligations_scratch: PendingObligations<'db>, } @@ -98,7 +100,7 @@ impl<'db> ObligationStorage<'db> { let goal = o.as_goal(); let result = <&SolverContext<'db>>::from(infcx).evaluate_root_goal( goal, - Span::dummy(), + o.cause.span(), stalled_on.take(), ); matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. })) @@ -120,24 +122,22 @@ impl<'db> FulfillmentCtxt<'db> { } impl<'db> FulfillmentCtxt<'db> { - #[tracing::instrument(level = "trace", skip(self, _infcx))] + #[tracing::instrument(level = "trace", skip(self, infcx))] pub(crate) fn register_predicate_obligation( &mut self, - _infcx: &InferCtxt<'db>, + infcx: &InferCtxt<'db>, obligation: PredicateObligation<'db>, ) { - // FIXME: See the comment in `try_evaluate_obligations()`. - // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); self.obligations.register(obligation, None); } pub(crate) fn register_predicate_obligations( &mut self, - _infcx: &InferCtxt<'db>, + infcx: &InferCtxt<'db>, obligations: impl IntoIterator<Item = PredicateObligation<'db>>, ) { - // FIXME: See the comment in `try_evaluate_obligations()`. - // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); obligations.into_iter().for_each(|obligation| self.obligations.register(obligation, None)); } @@ -157,11 +157,7 @@ impl<'db> FulfillmentCtxt<'db> { &mut self, infcx: &InferCtxt<'db>, ) -> Vec<NextSolverError<'db>> { - // FIXME(next-solver): We should bring this assertion back. Currently it panics because - // there are places which use `InferenceTable` and open a snapshot and register obligations - // and select. They should use a different `ObligationCtxt` instead. Then we'll be also able - // to not put the obligations queue in `InferenceTable`'s snapshots. - // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); self.try_evaluate_obligations_scratch.clear(); let mut errors = Vec::new(); loop { @@ -176,7 +172,9 @@ impl<'db> FulfillmentCtxt<'db> { let goal = obligation.as_goal(); let delegate = <&SolverContext<'db>>::from(infcx); - if let Some(certainty) = delegate.compute_goal_fast_path(goal, Span::dummy()) { + if let Some(certainty) = + delegate.compute_goal_fast_path(goal, obligation.cause.span()) + { match certainty { Certainty::Yes => {} Certainty::Maybe { .. } => { @@ -186,9 +184,11 @@ impl<'db> FulfillmentCtxt<'db> { continue; } - let result = delegate.evaluate_root_goal(goal, Span::dummy(), stalled_on); + let result = delegate.evaluate_root_goal(goal, obligation.cause.span(), stalled_on); infcx.inspect_evaluated_obligation(&obligation, &result, || { - Some(delegate.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1) + Some( + delegate.evaluate_root_goal_for_proof_tree(goal, obligation.cause.span()).1, + ) }); let GoalEvaluation { goal: _, certainty, has_changed, stalled_on } = match result { Ok(result) => result, @@ -243,7 +243,7 @@ impl<'db> FulfillmentCtxt<'db> { &mut self, infcx: &InferCtxt<'db>, ) -> PredicateObligations<'db> { - let stalled_coroutines = match infcx.typing_mode() { + let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() { TypingMode::Analysis { defining_opaque_types_and_generators } => { defining_opaque_types_and_generators } @@ -266,6 +266,7 @@ impl<'db> FulfillmentCtxt<'db> { obl.as_goal(), &mut StalledOnCoroutines { stalled_coroutines, + span: obl.cause.span(), cache: Default::default(), }, ) @@ -287,12 +288,17 @@ impl<'db> FulfillmentCtxt<'db> { /// so we want to keep this visitor *precise* too. pub struct StalledOnCoroutines<'a, 'db> { pub stalled_coroutines: &'a [SolverDefId], + pub span: Span, pub cache: FxHashSet<Ty<'db>>, } impl<'db> ProofTreeVisitor<'db> for StalledOnCoroutines<'_, 'db> { type Result = ControlFlow<()>; + fn span(&self) -> Span { + self.span + } + fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'db>) -> Self::Result { inspect_goal.goal().predicate.visit_with(self)?; @@ -324,7 +330,7 @@ impl<'db> TypeVisitor<DbInterner<'db>> for StalledOnCoroutines<'_, 'db> { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum NextSolverError<'db> { TrueError(PredicateObligation<'db>), Ambiguity(PredicateObligation<'db>), diff --git a/crates/hir-ty/src/next_solver/generic_arg.rs b/crates/hir-ty/src/next_solver/generic_arg.rs index 72cf2f9f07..51f070cd64 100644 --- a/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/crates/hir-ty/src/next_solver/generic_arg.rs @@ -8,6 +8,7 @@ use std::{hint::unreachable_unchecked, marker::PhantomData, ptr::NonNull}; +use arrayvec::ArrayVec; use hir_def::{GenericDefId, GenericParamId}; use intern::InternedRef; use rustc_type_ir::{ @@ -18,7 +19,6 @@ use rustc_type_ir::{ relate::{Relate, VarianceDiagInfo}, walk::TypeWalker, }; -use smallvec::SmallVec; use crate::next_solver::{ ConstInterned, RegionInterned, TyInterned, impl_foldable_for_interned_slice, interned_slice, @@ -194,6 +194,25 @@ impl std::fmt::Debug for StoredGenericArg { } } +impl<'db> TypeVisitable<DbInterner<'db>> for StoredGenericArg { + fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, visitor: &mut V) -> V::Result { + self.as_ref().visit_with(visitor) + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for StoredGenericArg { + fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + + fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + self.as_ref().fold_with(folder).store() + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct GenericArg<'db> { ptr: GenericArgImpl<'db>, @@ -457,7 +476,66 @@ impl_foldable_for_interned_slice!(GenericArgs); impl<'db> rustc_type_ir::inherent::GenericArg<DbInterner<'db>> for GenericArg<'db> {} +impl<'db> TypeVisitable<DbInterner<'db>> for StoredGenericArgs { + fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, visitor: &mut V) -> V::Result { + self.as_ref().visit_with(visitor) + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for StoredGenericArgs { + fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + + fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + self.as_ref().fold_with(folder).store() + } +} + +trait GenericArgsBuilder<'db>: AsRef<[GenericArg<'db>]> { + fn push(&mut self, arg: GenericArg<'db>); +} + +impl<'db, const N: usize> GenericArgsBuilder<'db> for ArrayVec<GenericArg<'db>, N> { + fn push(&mut self, arg: GenericArg<'db>) { + self.push(arg); + } +} + +impl<'db> GenericArgsBuilder<'db> for Vec<GenericArg<'db>> { + fn push(&mut self, arg: GenericArg<'db>) { + self.push(arg); + } +} + impl<'db> GenericArgs<'db> { + #[inline(always)] + fn fill_builder<F>( + args: &mut impl GenericArgsBuilder<'db>, + defs: &Generics<'db>, + mut mk_kind: F, + ) where + F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + { + defs.iter_id().enumerate().for_each(|(idx, param_id)| { + let new_arg = mk_kind(idx as u32, param_id, args.as_ref()); + args.push(new_arg); + }); + } + + #[cold] + fn fill_vec_builder<F>(defs: &Generics<'db>, count: usize, mk_kind: F) -> GenericArgs<'db> + where + F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let mut args = Vec::with_capacity(count); + Self::fill_builder(&mut args, defs, mk_kind); + GenericArgs::new_from_slice(&args) + } + /// Creates an `GenericArgs` for generic parameter definitions, /// by calling closures to obtain each kind. /// The closures get to observe the `GenericArgs` as they're @@ -466,7 +544,7 @@ impl<'db> GenericArgs<'db> { pub fn for_item<F>( interner: DbInterner<'db>, def_id: SolverDefId, - mut mk_kind: F, + mk_kind: F, ) -> GenericArgs<'db> where F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, @@ -475,12 +553,14 @@ impl<'db> GenericArgs<'db> { let count = defs.count(); if count == 0 { - return Default::default(); + GenericArgs::default() + } else if count <= 10 { + let mut args = ArrayVec::<_, 10>::new(); + Self::fill_builder(&mut args, &defs, mk_kind); + GenericArgs::new_from_slice(&args) + } else { + Self::fill_vec_builder(&defs, count, mk_kind) } - - let mut args = SmallVec::with_capacity(count); - Self::fill_item(&mut args, interner, defs, &mut mk_kind); - interner.mk_args(&args) } /// Creates an all-error `GenericArgs`. @@ -499,7 +579,7 @@ impl<'db> GenericArgs<'db> { { let defaults = interner.db.generic_defaults(def_id); Self::for_item(interner, def_id.into(), |idx, id, prev| match defaults.get(idx as usize) { - Some(default) => default.instantiate(interner, prev), + Some(default) => default.instantiate(interner, prev).skip_norm_wip(), None => fallback(idx, id, prev), }) } @@ -534,37 +614,11 @@ impl<'db> GenericArgs<'db> { Self::fill_rest(interner, def_id.into(), first, |idx, id, prev| { defaults .get(idx as usize) - .map(|default| default.instantiate(interner, prev)) + .map(|default| default.instantiate(interner, prev).skip_norm_wip()) .unwrap_or_else(|| fallback(idx, id, prev)) }) } - fn fill_item<F>( - args: &mut SmallVec<[GenericArg<'db>; 8]>, - interner: DbInterner<'_>, - defs: Generics, - mk_kind: &mut F, - ) where - F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, - { - if let Some(def_id) = defs.parent { - let parent_defs = interner.generics_of(def_id.into()); - Self::fill_item(args, interner, parent_defs, mk_kind); - } - Self::fill_single(args, &defs, mk_kind); - } - - fn fill_single<F>(args: &mut SmallVec<[GenericArg<'db>; 8]>, defs: &Generics, mk_kind: &mut F) - where - F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, - { - args.reserve(defs.own_params.len()); - for param in &defs.own_params { - let kind = mk_kind(args.len() as u32, param.id, args); - args.push(kind); - } - } - pub fn types(self) -> impl Iterator<Item = Ty<'db>> { self.iter().filter_map(|it| it.as_type()) } diff --git a/crates/hir-ty/src/next_solver/generics.rs b/crates/hir-ty/src/next_solver/generics.rs index f31de21796..a798582cb9 100644 --- a/crates/hir-ty/src/next_solver/generics.rs +++ b/crates/hir-ty/src/next_solver/generics.rs @@ -1,117 +1,77 @@ //! Things related to generics in the next-trait-solver. -use hir_def::{ - ConstParamId, GenericDefId, GenericParamId, LifetimeParamId, TypeOrConstParamId, TypeParamId, - hir::generics::{GenericParams, TypeOrConstParamData}, -}; -use rustc_type_ir::inherent::GenericsOf; +use hir_def::{GenericDefId, GenericParamId}; -use crate::generics::parent_generic_def; +use crate::db::HirDatabase; use super::SolverDefId; use super::DbInterner; -pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics { - let mk_lt = |parent, index, local_id| { - let id = GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }); - GenericParamDef { index, id } - }; - let mk_ty = |parent, index, local_id, p: &TypeOrConstParamData| { - let id = TypeOrConstParamId { parent, local_id }; - let id = match p { - TypeOrConstParamData::TypeParamData(_) => { - GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)) - } - TypeOrConstParamData::ConstParamData(_) => { - GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)) - } - }; - GenericParamDef { index, id } - }; - let own_params_for_generic_params = |parent, params: &GenericParams| { - let mut result = Vec::with_capacity(params.len()); - let mut type_and_consts = params.iter_type_or_consts(); - let mut index = 0; - if let Some(self_param) = params.trait_self_param() { - result.push(mk_ty(parent, 0, self_param, ¶ms[self_param])); - type_and_consts.next(); - index += 1; - } - result.extend(params.iter_lt().map(|(local_id, _data)| { - let lt = mk_lt(parent, index, local_id); - index += 1; - lt - })); - result.extend(type_and_consts.map(|(local_id, data)| { - let ty = mk_ty(parent, index, local_id, data); - index += 1; - ty - })); - result - }; - +pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics<'_> { let db = interner.db; - let (parent, own_params) = match (def.try_into(), def) { - (Ok(def), _) => ( - parent_generic_def(db, def), - own_params_for_generic_params(def, GenericParams::of(db, def)), - ), - (_, SolverDefId::InternedOpaqueTyId(id)) => { - match db.lookup_intern_impl_trait_id(id) { - crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => { - // The opaque type itself does not have generics - only the parent function - (Some(GenericDefId::FunctionId(function_id)), vec![]) - } - crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => { - (Some(type_alias_id.into()), Vec::new()) - } - } - } + let def = match (def.try_into(), def) { + (Ok(def), _) => def, + (_, SolverDefId::InternedOpaqueTyId(id)) => match id.loc(db) { + crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => function_id.into(), + crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => type_alias_id.into(), + }, (_, SolverDefId::BuiltinDeriveImplId(id)) => { return crate::builtin_derive::generics_of(interner, id); } + (_, SolverDefId::AnonConstId(id)) => { + let loc = id.loc(db); + let generic_def = loc.owner.generic_def(db); + return if loc.allow_using_generic_params { + Generics::from_generic_def(db, generic_def) + } else { + #[expect( + deprecated, + reason = "`Generics` only exposes an iterator over `GenericParamId`, \ + so you cannot exploit the erroneous `crate::generics::Generics`" + )] + Generics { + generics: crate::generics::Generics::empty(generic_def), + additional_param: None, + } + }; + } _ => panic!("No generics for {def:?}"), }; - let parent_generics = parent.map(|def| Box::new(generics(interner, def.into()))); - Generics { - parent, - parent_count: parent_generics.map_or(0, |g| g.parent_count + g.own_params.len()), - own_params, - } + Generics::from_generic_def(db, def) } #[derive(Debug)] -pub struct Generics { - pub parent: Option<GenericDefId>, - pub parent_count: usize, - pub own_params: Vec<GenericParamDef>, +pub struct Generics<'db> { + generics: crate::generics::Generics<'db>, + /// This is used for builtin derives, specifically `CoercePointee`. + additional_param: Option<GenericParamId>, } -impl Generics { - pub(crate) fn push_param(&mut self, id: GenericParamId) { - let index = self.count() as u32; - self.own_params.push(GenericParamDef { index, id }); +impl<'db> Generics<'db> { + pub(crate) fn from_generic_def(db: &'db dyn HirDatabase, def: GenericDefId) -> Generics<'db> { + Generics { generics: crate::generics::generics(db, def), additional_param: None } } -} -#[derive(Debug)] -pub struct GenericParamDef { - index: u32, - pub(crate) id: GenericParamId, -} + pub(crate) fn from_generic_def_plus_one( + db: &'db dyn HirDatabase, + def: GenericDefId, + additional_param: GenericParamId, + ) -> Generics<'db> { + Generics { + generics: crate::generics::generics(db, def), + additional_param: Some(additional_param), + } + } -impl GenericParamDef { - /// Returns the index of the param on the self generics only - /// (i.e. not including parent generics) - pub fn index(&self) -> u32 { - self.index + pub(super) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> { + self.generics.iter_id().chain(self.additional_param) } } -impl<'db> rustc_type_ir::inherent::GenericsOf<DbInterner<'db>> for Generics { +impl<'db> rustc_type_ir::inherent::GenericsOf<DbInterner<'db>> for Generics<'db> { fn count(&self) -> usize { - self.parent_count + self.own_params.len() + self.generics.len() + usize::from(self.additional_param.is_some()) } } diff --git a/crates/hir-ty/src/next_solver/infer/at.rs b/crates/hir-ty/src/next_solver/infer/at.rs index dc0b584084..f63200a2e0 100644 --- a/crates/hir-ty/src/next_solver/infer/at.rs +++ b/crates/hir-ty/src/next_solver/infer/at.rs @@ -28,13 +28,12 @@ use rustc_type_ir::{ FnSig, GenericArgKind, TypeFoldable, TypingMode, Variance, error::ExpectedFound, - inherent::Span as _, relate::{Relate, TypeRelation, solver_relating::RelateExt}, }; use crate::next_solver::{ AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv, - PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term, + PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Term, TraitRef, Ty, fulfill::NextSolverError, infer::relate::lattice::{LatticeOp, LatticeOpKind}, @@ -109,7 +108,7 @@ impl<'a, 'db> At<'a, 'db> { expected, Variance::Contravariant, actual, - Span::dummy(), + self.cause.span(), ) .map(|goals| self.goals_to_obligations(goals)) } @@ -125,7 +124,7 @@ impl<'a, 'db> At<'a, 'db> { expected, Variance::Covariant, actual, - Span::dummy(), + self.cause.span(), ) .map(|goals| self.goals_to_obligations(goals)) } @@ -141,7 +140,7 @@ impl<'a, 'db> At<'a, 'db> { expected, Variance::Invariant, actual, - Span::dummy(), + self.cause.span(), ) .map(|goals| self.goals_to_obligations(goals)) } @@ -200,7 +199,7 @@ impl<'a, 'db> At<'a, 'db> { .map(|goal| { Obligation::new( self.infcx.interner, - self.cause.clone(), + *self.cause, goal.param_env, goal.predicate, ) @@ -213,7 +212,7 @@ impl<'a, 'db> At<'a, 'db> { impl<'db> ToTrace<'db> for Ty<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), } } @@ -221,14 +220,14 @@ impl<'db> ToTrace<'db> for Ty<'db> { impl<'db> ToTrace<'db> for Region<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::Regions(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::Regions(ExpectedFound::new(a, b)) } } } impl<'db> ToTrace<'db> for Const<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), } } @@ -237,7 +236,7 @@ impl<'db> ToTrace<'db> for Const<'db> { impl<'db> ToTrace<'db> for GenericArg<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: match (a.kind(), b.kind()) { (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => { ValuePairs::Regions(ExpectedFound::new(a, b)) @@ -256,20 +255,20 @@ impl<'db> ToTrace<'db> for GenericArg<'db> { impl<'db> ToTrace<'db> for Term<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::Terms(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a, b)) } } } impl<'db> ToTrace<'db> for TraitRef<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } } } impl<'db> ToTrace<'db> for AliasTy<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())), } } @@ -277,14 +276,14 @@ impl<'db> ToTrace<'db> for AliasTy<'db> { impl<'db> ToTrace<'db> for AliasTerm<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::Aliases(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::Aliases(ExpectedFound::new(a, b)) } } } impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))), } } @@ -292,14 +291,14 @@ impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> { impl<'db> ToTrace<'db> for PolyFnSig<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) } } } impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)), } } @@ -308,7 +307,7 @@ impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> { impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> { fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)), } } diff --git a/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs index 1738552a8e..6e20375500 100644 --- a/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs +++ b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs @@ -404,13 +404,17 @@ impl<'db> InferCtxt<'db> { if kind.universe() != UniverseIndex::ROOT { // A variable from inside a binder of the query. While ideally these shouldn't // exist at all, we have to deal with them for now. - self.instantiate_canonical_var(kind, var_values, |u| universe_map[u.as_usize()]) + self.instantiate_canonical_var(cause.span(), kind, var_values, |u| { + universe_map[u.as_usize()] + }) } else if kind.is_existential() { match opt_values[BoundVar::new(var_values.len())] { Some(k) => k, - None => self.instantiate_canonical_var(kind, var_values, |u| { - universe_map[u.as_usize()] - }), + None => { + self.instantiate_canonical_var(cause.span(), kind, var_values, |u| { + universe_map[u.as_usize()] + }) + } } } else { // For placeholders which were already part of the input, we simply map this @@ -433,7 +437,7 @@ impl<'db> InferCtxt<'db> { // the generic args of the opaque with the generic params of its hidden type version. obligations.extend( self.at(cause, param_env) - .eq(Ty::new_opaque(self.interner, a.def_id, a.args), b)? + .eq(Ty::new_opaque(self.interner, a.def_id.0, a.args), b)? .obligations, ); } diff --git a/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs index 1fefc0f265..a5e29e7836 100644 --- a/crates/hir-ty/src/next_solver/infer/canonical/mod.rs +++ b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -21,10 +21,13 @@ //! //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html -use crate::next_solver::{ - ArgOutlivesPredicate, Canonical, CanonicalVarValues, Const, DbInterner, GenericArg, - OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderType, Region, Ty, TyKind, - infer::InferCtxt, +use crate::{ + Span, + next_solver::{ + ArgOutlivesPredicate, Canonical, CanonicalVarValues, Const, DbInterner, GenericArg, + OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderType, Region, Ty, TyKind, + infer::InferCtxt, + }, }; use instantiate::CanonicalExt; use macros::{TypeFoldable, TypeVisitable}; @@ -50,6 +53,7 @@ impl<'db> InferCtxt<'db> { /// for each of the canonical inputs to your query. pub fn instantiate_canonical<T>( &self, + span: Span, canonical: &Canonical<'db, T>, ) -> (T, CanonicalVarValues<'db>) where @@ -71,7 +75,9 @@ impl<'db> InferCtxt<'db> { let var_values = CanonicalVarValues::instantiate( self.interner, canonical.var_kinds, - |var_values, info| self.instantiate_canonical_var(info, var_values, |ui| universes[ui]), + |var_values, info| { + self.instantiate_canonical_var(span, info, var_values, |ui| universes[ui]) + }, ); let result = canonical.instantiate(self.interner, &var_values); (result, var_values) @@ -87,13 +93,14 @@ impl<'db> InferCtxt<'db> { /// We should somehow deduplicate all of this. pub fn instantiate_canonical_var( &self, + span: Span, cv_info: CanonicalVarKind<DbInterner<'db>>, previous_var_values: &[GenericArg<'db>], universe_map: impl Fn(UniverseIndex) -> UniverseIndex, ) -> GenericArg<'db> { match cv_info { CanonicalVarKind::Ty { ui, sub_root } => { - let vid = self.next_ty_var_id_in_universe(universe_map(ui)); + let vid = self.next_ty_var_id_in_universe(universe_map(ui), span); // If this inference variable is related to an earlier variable // via subtyping, we need to add that info to the inference context. if let Some(prev) = previous_var_values.get(sub_root.as_usize()) { @@ -117,7 +124,7 @@ impl<'db> InferCtxt<'db> { } CanonicalVarKind::Region(ui) => { - self.next_region_var_in_universe(universe_map(ui)).into() + self.next_region_var_in_universe(universe_map(ui), span).into() } CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound, .. }) => { @@ -126,7 +133,9 @@ impl<'db> InferCtxt<'db> { Region::new_placeholder(self.interner, placeholder_mapped).into() } - CanonicalVarKind::Const(ui) => self.next_const_var_in_universe(universe_map(ui)).into(), + CanonicalVarKind::Const(ui) => { + self.next_const_var_in_universe(universe_map(ui), span).into() + } CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound, .. }) => { let universe_mapped = universe_map(universe); let placeholder_mapped = PlaceholderConst::new(universe_mapped, bound); diff --git a/crates/hir-ty/src/next_solver/infer/context.rs b/crates/hir-ty/src/next_solver/infer/context.rs index 397986e2ed..1e2a3ff0c2 100644 --- a/crates/hir-ty/src/next_solver/infer/context.rs +++ b/crates/hir-ty/src/next_solver/infer/context.rs @@ -5,12 +5,16 @@ use rustc_type_ir::{ RegionVid, TyVid, TypeFoldable, TypingMode, UniverseIndex, inherent::{Const as _, IntoKind, Ty as _}, relate::combine::PredicateEmittingRelation, + solve::VisibleForLeakCheck, }; -use crate::next_solver::{ - Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, Region, - SolverDefId, Span, Ty, TyKind, - infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries}, +use crate::{ + Span, + next_solver::{ + Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, Region, + SolverDefId, Ty, TyKind, + infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries}, + }, }; use super::{BoundRegionConversionTime, InferCtxt, relate::RelateResult}; @@ -26,8 +30,12 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { true } - fn typing_mode(&self) -> TypingMode<DbInterner<'db>> { - self.typing_mode() + fn disable_trait_solver_fast_paths(&self) -> bool { + false + } + + fn typing_mode_raw(&self) -> TypingMode<DbInterner<'db>> { + self.typing_mode_raw() } fn universe(&self) -> UniverseIndex { @@ -139,26 +147,30 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { } fn next_ty_infer(&self) -> Ty<'db> { - self.next_ty_var() + self.next_ty_var(Span::Dummy) } fn next_region_infer(&self) -> <Self::Interner as rustc_type_ir::Interner>::Region { - self.next_region_var() + self.next_region_var(Span::Dummy) } fn next_const_infer(&self) -> Const<'db> { - self.next_const_var() + self.next_const_var(Span::Dummy) } fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> { - self.fresh_args_for_item(def_id) + self.fresh_args_for_item(Span::Dummy, def_id) } fn instantiate_binder_with_infer<T: TypeFoldable<DbInterner<'db>> + Clone>( &self, value: Binder<'db, T>, ) -> T { - self.instantiate_binder_with_fresh_vars(BoundRegionConversionTime::HigherRankedType, value) + self.instantiate_binder_with_fresh_vars( + Span::Dummy, + BoundRegionConversionTime::HigherRankedType, + value, + ) } fn enter_forall<T: TypeFoldable<DbInterner<'db>> + Clone, U>( @@ -250,11 +262,23 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { self.probe(|_| probe()) } - fn sub_regions(&self, sub: Region<'db>, sup: Region<'db>, _span: Span) { + fn sub_regions( + &self, + sub: Region<'db>, + sup: Region<'db>, + _vis: VisibleForLeakCheck, + _span: Span, + ) { self.inner.borrow_mut().unwrap_region_constraints().make_subregion(sub, sup); } - fn equate_regions(&self, a: Region<'db>, b: Region<'db>, _span: Span) { + fn equate_regions( + &self, + a: Region<'db>, + b: Region<'db>, + _vis: VisibleForLeakCheck, + _span: Span, + ) { self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(a, b); } diff --git a/crates/hir-ty/src/next_solver/infer/errors.rs b/crates/hir-ty/src/next_solver/infer/errors.rs new file mode 100644 index 0000000000..7d3f111f66 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/errors.rs @@ -0,0 +1,702 @@ +use std::{fmt, ops::ControlFlow}; + +use hir_def::attrs::AttrFlags; +use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt}; +use rustc_type_ir::{ + AliasRelationDirection, AliasTermKind, PredicatePolarity, + error::ExpectedFound, + inherent::IntoKind as _, + solve::{CandidateSource, Certainty, GoalSource, MaybeCause, MaybeInfo, NoSolution}, +}; +use tracing::{instrument, trace}; + +use crate::{ + Span, + db::GeneralConstId, + next_solver::{ + AliasTerm, AnyImplId, Binder, ClauseKind, Const, ConstKind, DbInterner, + HostEffectPredicate, PolyTraitPredicate, PredicateKind, SolverContext, Term, + TraitPredicate, Ty, TyKind, TypeError, + fulfill::NextSolverError, + infer::{ + InferCtxt, + select::SelectionError, + traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations}, + }, + inspect::{self, ProofTreeVisitor}, + normalize::deeply_normalize_for_diagnostics, + }, +}; + +#[derive(Debug)] +pub struct FulfillmentError<'db> { + pub obligation: PredicateObligation<'db>, + pub code: FulfillmentErrorCode<'db>, + /// Diagnostics only: the 'root' obligation which resulted in + /// the failure to process `obligation`. This is the obligation + /// that was initially passed to `register_predicate_obligation` + pub root_obligation: PredicateObligation<'db>, +} + +impl<'db> FulfillmentError<'db> { + pub fn new( + obligation: PredicateObligation<'db>, + code: FulfillmentErrorCode<'db>, + root_obligation: PredicateObligation<'db>, + ) -> FulfillmentError<'db> { + FulfillmentError { obligation, code, root_obligation } + } + + pub fn is_true_error(&self) -> bool { + match self.code { + FulfillmentErrorCode::Select(_) + | FulfillmentErrorCode::Project(_) + | FulfillmentErrorCode::Subtype(_, _) + | FulfillmentErrorCode::ConstEquate(_, _) => true, + FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => { + false + } + } + } +} + +#[derive(Clone)] +pub enum FulfillmentErrorCode<'db> { + /// Inherently impossible to fulfill; this trait is implemented if and only + /// if it is already implemented. + Cycle(PredicateObligations<'db>), + Select(SelectionError<'db>), + Project(MismatchedProjectionTypes<'db>), + Subtype(ExpectedFound<Ty<'db>>, TypeError<'db>), // always comes from a SubtypePredicate + ConstEquate(ExpectedFound<Const<'db>>, TypeError<'db>), + Ambiguity { + /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation + /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by + /// emitting a fatal error instead. + overflow: Option<bool>, + }, +} + +impl<'db> fmt::Debug for FulfillmentErrorCode<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"), + FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"), + FulfillmentErrorCode::Subtype(ref a, ref b) => { + write!(f, "CodeSubtypeError({a:?}, {b:?})") + } + FulfillmentErrorCode::ConstEquate(ref a, ref b) => { + write!(f, "CodeConstEquateError({a:?}, {b:?})") + } + FulfillmentErrorCode::Ambiguity { overflow: None } => write!(f, "Ambiguity"), + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => { + write!(f, "Overflow({suggest_increasing_limit})") + } + FulfillmentErrorCode::Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"), + } + } +} + +#[derive(Clone)] +pub struct MismatchedProjectionTypes<'db> { + pub err: TypeError<'db>, +} + +impl<'db> fmt::Debug for MismatchedProjectionTypes<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MismatchedProjectionTypes({:?})", self.err) + } +} + +impl<'db> NextSolverError<'db> { + pub fn into_fulfillment_error(self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> { + match self { + NextSolverError::TrueError(obligation) => { + fulfillment_error_for_no_solution(infcx, obligation) + } + NextSolverError::Ambiguity(obligation) => { + fulfillment_error_for_stalled(infcx, obligation) + } + NextSolverError::Overflow(obligation) => { + fulfillment_error_for_overflow(infcx, obligation) + } + } + } +} + +fn fulfillment_error_for_no_solution<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + let interner = infcx.interner; + let db = interner.db; + let obligation = find_best_leaf_obligation(infcx, &root_obligation, false); + + let code = match obligation.predicate.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Projection(_)) => { + FulfillmentErrorCode::Project( + // FIXME: This could be a `Sorts` if the term is a type + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, expected_ty)) => { + let ct_ty = match ct.kind() { + ConstKind::Unevaluated(uv) => { + let ct_ty = match uv.def.0 { + GeneralConstId::ConstId(konst) => db.value_ty(konst.into()).unwrap(), + GeneralConstId::StaticId(statik) => db.value_ty(statik.into()).unwrap(), + GeneralConstId::AnonConstId(konst) => konst.loc(db).ty.get(), + }; + ct_ty.instantiate(interner, uv.args).skip_norm_wip() + } + ConstKind::Param(param_ct) => param_ct.find_const_ty_from_env(obligation.param_env), + ConstKind::Value(cv) => cv.ty, + kind => panic!( + "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}" + ), + }; + FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType { + ct, + ct_ty, + expected_ty, + }) + } + PredicateKind::NormalizesTo(..) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + PredicateKind::AliasRelate(_, _, _) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + PredicateKind::Subtype(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(a, b); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + PredicateKind::Coerce(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(b, a); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + PredicateKind::Clause(_) | PredicateKind::DynCompatible(_) | PredicateKind::Ambiguous => { + FulfillmentErrorCode::Select(SelectionError::Unimplemented) + } + PredicateKind::ConstEquate(..) => { + panic!("unexpected goal: {obligation:?}") + } + }; + + FulfillmentError { obligation, code, root_obligation } +} + +fn fulfillment_error_for_stalled<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + let (code, refine_obligation) = infcx.probe(|_| { + match <&SolverContext<'db>>::from(infcx).evaluate_root_goal( + root_obligation.as_goal(), + root_obligation.cause.span(), + None, + ) { + Ok(GoalEvaluation { + certainty: Certainty::Maybe(MaybeInfo { cause: MaybeCause::Ambiguity, .. }), + .. + }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true), + Ok(GoalEvaluation { + certainty: + Certainty::Maybe(MaybeInfo { + cause: + MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, + .. + }), + .. + }) => ( + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, + // Don't look into overflows because we treat overflows weirdly anyways. + // We discard the inference constraints from overflowing goals, so + // recomputing the goal again during `find_best_leaf_obligation` may apply + // inference guidance that makes other goals go from ambig -> pass, for example. + // + // FIXME: We should probably just look into overflows here. + false, + ), + Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => { + panic!( + "did not expect successful goal when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) + } + Err(_) => { + panic!( + "did not expect selection error when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) + } + } + }); + + FulfillmentError { + obligation: if refine_obligation { + find_best_leaf_obligation(infcx, &root_obligation, true) + } else { + root_obligation.clone() + }, + code, + root_obligation, + } +} + +fn fulfillment_error_for_overflow<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + FulfillmentError { + obligation: find_best_leaf_obligation(infcx, &root_obligation, true), + code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, + root_obligation, + } +} + +#[instrument(level = "debug", skip(infcx), ret)] +fn find_best_leaf_obligation<'db>( + infcx: &InferCtxt<'db>, + obligation: &PredicateObligation<'db>, + consider_ambiguities: bool, +) -> PredicateObligation<'db> { + let obligation = infcx.resolve_vars_if_possible(obligation.clone()); + // FIXME: we use a probe here as the `BestObligation` visitor does not + // check whether it uses candidates which get shadowed by where-bounds. + // + // We should probably fix the visitor to not do so instead, as this also + // means the leaf obligation may be incorrect. + let obligation = infcx + .fudge_inference_if_ok(|| { + infcx + .visit_proof_tree( + obligation.as_goal(), + &mut BestObligation { obligation: obligation.clone(), consider_ambiguities }, + ) + .break_value() + .ok_or(()) + // walk around the fact that the cause in `Obligation` is ignored by folders so that + // we can properly fudge the infer vars in cause code. + .map(|o| (o.cause, o)) + }) + .map(|(cause, o)| PredicateObligation { cause, ..o }) + .unwrap_or(obligation); + deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation) +} + +struct BestObligation<'db> { + obligation: PredicateObligation<'db>, + consider_ambiguities: bool, +} + +impl<'db> BestObligation<'db> { + fn with_derived_obligation( + &mut self, + derived_obligation: PredicateObligation<'db>, + and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'db>>::Result, + ) -> <Self as ProofTreeVisitor<'db>>::Result { + let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation); + let res = and_then(self); + self.obligation = old_obligation; + res + } + + /// Filter out the candidates that aren't interesting to visit for the + /// purposes of reporting errors. For ambiguities, we only consider + /// candidates that may hold. For errors, we only consider candidates that + /// *don't* hold and which have impl-where clauses that also don't hold. + fn non_trivial_candidates<'a>( + &self, + goal: &'a inspect::InspectGoal<'a, 'db>, + ) -> Vec<inspect::InspectCandidate<'a, 'db>> { + let mut candidates = goal.candidates(); + match self.consider_ambiguities { + true => { + // If we have an ambiguous obligation, we must consider *all* candidates + // that hold, or else we may guide inference causing other goals to go + // from ambig -> pass/fail. + candidates.retain(|candidate| candidate.result().is_ok()); + } + false => { + // We always handle rigid alias candidates separately as we may not add them for + // aliases whose trait bound doesn't hold. + candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. })); + // If we have >1 candidate, one may still be due to "boring" reasons, like + // an alias-relate that failed to hold when deeply evaluated. We really + // don't care about reasons like this. + if candidates.len() > 1 { + candidates.retain(|candidate| { + goal.infcx().probe(|_| { + candidate.instantiate_nested_goals(self.span()).iter().any( + |nested_goal| { + matches!( + nested_goal.source(), + GoalSource::ImplWhereBound + | GoalSource::AliasBoundConstCondition + | GoalSource::AliasWellFormed + ) && nested_goal.result().is_err() + }, + ) + }) + }); + } + } + } + + candidates + } + + /// HACK: We walk the nested obligations for a well-formed arg manually, + /// since there's nontrivial logic in `wf.rs` to set up an obligation cause. + /// Ideally we'd be able to track this better. + fn visit_well_formed_goal( + &mut self, + candidate: &inspect::InspectCandidate<'_, 'db>, + term: Term<'db>, + ) -> ControlFlow<PredicateObligation<'db>> { + let _ = (candidate, term); + // FIXME: rustc does this, but we don't process WF obligations yet: + // let infcx = candidate.goal().infcx(); + // let param_env = candidate.goal().goal().param_env; + // let body_id = self.obligation.cause.body_id; + + // for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id) + // .into_iter() + // .flatten() + // { + // let nested_goal = candidate.instantiate_proof_tree_for_nested_goal( + // GoalSource::Misc, + // obligation.as_goal(), + // self.span(), + // ); + // // Skip nested goals that aren't the *reason* for our goal's failure. + // match (self.consider_ambiguities, nested_goal.result()) { + // (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) + // | (false, Err(_)) => {} + // _ => continue, + // } + + // self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + // } + + ControlFlow::Break(self.obligation.clone()) + } + + /// If a normalization of an associated item or a trait goal fails without trying any + /// candidates it's likely that normalizing its self type failed. We manually detect + /// such cases here. + fn detect_error_in_self_ty_normalization( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + self_ty: Ty<'db>, + ) -> ControlFlow<PredicateObligation<'db>> { + assert!(!self.consider_ambiguities); + let interner = goal.infcx().interner; + if let TyKind::Alias(..) = self_ty.kind() { + let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span()); + let pred = PredicateKind::AliasRelate( + self_ty.into(), + infer_term.into(), + AliasRelationDirection::Equate, + ); + let obligation = + Obligation::new(interner, self.obligation.cause, goal.goal().param_env, pred); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, pred), + goal.depth() + 1, + this, + ) + }) + } else { + ControlFlow::Continue(()) + } + } + + /// When a higher-ranked projection goal fails, check that the corresponding + /// higher-ranked trait goal holds or not. This is because the process of + /// instantiating and then re-canonicalizing the binder of the projection goal + /// forces us to be unable to see that the leak check failed in the nested + /// `NormalizesTo` goal, so we don't fall back to the rigid projection check + /// that should catch when a projection goal fails due to an unsatisfied trait + /// goal. + fn detect_trait_error_in_higher_ranked_projection( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + ) -> ControlFlow<PredicateObligation<'db>> { + let interner = goal.infcx().interner; + if let Some(projection_clause) = goal.goal().predicate.as_projection_clause() + && !projection_clause.bound_vars().is_empty() + { + let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(interner)); + let obligation = Obligation::new( + interner, + self.obligation.cause, + goal.goal().param_env, + deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred), + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, pred), + goal.depth() + 1, + this, + ) + }) + } else { + ControlFlow::Continue(()) + } + } + + /// It is likely that `NormalizesTo` failed without any applicable candidates + /// because the alias is not well-formed. + /// + /// As we only enter `RigidAlias` candidates if the trait bound of the associated type + /// holds, we discard these candidates in `non_trivial_candidates` and always manually + /// check this here. + fn detect_non_well_formed_assoc_item( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + alias: AliasTerm<'db>, + ) -> ControlFlow<PredicateObligation<'db>> { + let interner = goal.infcx().interner; + let obligation = Obligation::new( + interner, + self.obligation.cause, + goal.goal().param_env, + alias.trait_ref(interner), + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, alias.trait_ref(interner)), + goal.depth() + 1, + this, + ) + }) + } + + /// If we have no candidates, then it's likely that there is a + /// non-well-formed alias in the goal. + fn detect_error_from_empty_candidates( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + ) -> ControlFlow<PredicateObligation<'db>> { + let interner = goal.infcx().interner; + let pred_kind = goal.goal().predicate.kind(); + + match pred_kind.no_bound_vars() { + Some(PredicateKind::Clause(ClauseKind::Trait(pred))) => { + self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?; + } + Some(PredicateKind::NormalizesTo(pred)) + if let AliasTermKind::ProjectionTy { .. } + | AliasTermKind::ProjectionConst { .. } = pred.alias.kind(interner) => + { + self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?; + self.detect_non_well_formed_assoc_item(goal, pred.alias)?; + } + Some(_) | None => {} + } + + ControlFlow::Break(self.obligation.clone()) + } +} + +impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> { + type Result = ControlFlow<PredicateObligation<'db>>; + + fn span(&self) -> Span { + self.obligation.cause.span() + } + + #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))] + fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'db>) -> Self::Result { + let interner = goal.infcx().interner; + // Skip goals that aren't the *reason* for our goal's failure. + match (self.consider_ambiguities, goal.result()) { + (true, Ok(Certainty::Maybe(MaybeInfo { cause: MaybeCause::Ambiguity, .. }))) + | (false, Err(_)) => {} + _ => return ControlFlow::Continue(()), + } + + let pred = goal.goal().predicate; + + let candidates = self.non_trivial_candidates(goal); + let candidate = match candidates.as_slice() { + [candidate] => candidate, + [] => return self.detect_error_from_empty_candidates(goal), + _ => return ControlFlow::Break(self.obligation.clone()), + }; + + // Don't walk into impls that have `do_not_recommend`. + if let inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(impl_def_id), + result: _, + } = candidate.kind() + && let AnyImplId::ImplId(impl_def_id) = impl_def_id + && AttrFlags::query(interner.db, impl_def_id.into()) + .contains(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND) + { + trace!("#[diagnostic::do_not_recommend] -> exit"); + return ControlFlow::Break(self.obligation.clone()); + } + + // FIXME: Also, what about considering >1 layer up the stack? May be necessary + // for normalizes-to. + let child_mode = match pred.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => { + ChildMode::Trait(pred.kind().rebind(trait_pred)) + } + PredicateKind::Clause(ClauseKind::HostEffect(host_pred)) => { + ChildMode::Host(pred.kind().rebind(host_pred)) + } + PredicateKind::NormalizesTo(normalizes_to) + if matches!( + normalizes_to.alias.kind(interner), + AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. } + ) => + { + ChildMode::Trait(pred.kind().rebind(TraitPredicate { + trait_ref: normalizes_to.alias.trait_ref(interner), + polarity: PredicatePolarity::Positive, + })) + } + PredicateKind::Clause(ClauseKind::WellFormed(term)) => { + return self.visit_well_formed_goal(candidate, term); + } + _ => ChildMode::PassThrough, + }; + + let nested_goals = candidate.instantiate_nested_goals(self.span()); + + // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as + // an actual candidate, instead we should treat them as if the impl was never considered to + // have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written + // instead of `impl<T: FnPtr> Trait for T`. + // + // We do this as a separate loop so that we do not choose to tell the user about some nested + // goal before we encounter a `T: FnPtr` nested goal. + for nested_goal in &nested_goals { + if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() + && Some(poly_trait_pred.def_id().0) == interner.lang_items().FnPtrTrait + && let Err(NoSolution) = nested_goal.result() + { + return ControlFlow::Break(self.obligation.clone()); + } + } + + let mut impl_where_bound_count = 0; + for nested_goal in nested_goals { + trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); + + let nested_pred = nested_goal.goal().predicate; + + let make_obligation = |cause| Obligation { + cause, + param_env: nested_goal.goal().param_env, + predicate: nested_pred, + recursion_depth: self.obligation.recursion_depth + 1, + }; + + let obligation; + match (child_mode, nested_goal.source()) { + ( + ChildMode::Trait(_) | ChildMode::Host(_), + GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_), + ) => { + continue; + } + (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => { + obligation = make_obligation(derive_cause( + interner, + candidate.kind(), + self.obligation.cause, + impl_where_bound_count, + parent_trait_pred, + )); + impl_where_bound_count += 1; + } + ( + ChildMode::Host(parent_host_pred), + GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, + ) => { + obligation = make_obligation(derive_host_cause( + interner, + candidate.kind(), + self.obligation.cause, + impl_where_bound_count, + parent_host_pred, + )); + impl_where_bound_count += 1; + } + (ChildMode::PassThrough, _) + | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { + obligation = make_obligation(self.obligation.cause); + } + } + + self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + } + + // alias-relate may fail because the lhs or rhs can't be normalized, + // and therefore is treated as rigid. + if let Some(PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, ClauseKind::WellFormed(lhs)), + goal.depth() + 1, + self, + )?; + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, ClauseKind::WellFormed(rhs)), + goal.depth() + 1, + self, + )?; + } + + self.detect_trait_error_in_higher_ranked_projection(goal)?; + + ControlFlow::Break(self.obligation.clone()) + } +} + +#[derive(Debug, Copy, Clone)] +enum ChildMode<'db> { + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Trait(PolyTraitPredicate<'db>), + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Host(Binder<'db, HostEffectPredicate<'db>>), + // Skip trying to derive an `ObligationCause` from this obligation, and + // report *all* sub-obligations as if they came directly from the parent + // obligation. + PassThrough, +} + +fn derive_cause<'db>( + _interner: DbInterner<'db>, + _candidate_kind: inspect::ProbeKind<DbInterner<'db>>, + cause: ObligationCause, + _idx: usize, + _parent_trait_pred: PolyTraitPredicate<'db>, +) -> ObligationCause { + cause +} + +fn derive_host_cause<'db>( + _interner: DbInterner<'db>, + _candidate_kind: inspect::ProbeKind<DbInterner<'db>>, + cause: ObligationCause, + _idx: usize, + _parent_host_pred: Binder<'db, HostEffectPredicate<'db>>, +) -> ObligationCause { + cause +} diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs index a6352c7899..839bdf17e7 100644 --- a/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/crates/hir-ty/src/next_solver/infer/mod.rs @@ -7,12 +7,10 @@ use std::sync::Arc; pub use BoundRegionConversionTime::*; use ena::unify as ut; -use hir_def::GenericParamId; +use hir_def::{GenericParamId, TraitId}; use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage}; use region_constraints::{RegionConstraintCollector, RegionConstraintStorage}; use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt}; -use rustc_pattern_analysis::Captures; -use rustc_type_ir::solve::{NoSolution, inspect}; use rustc_type_ir::{ ClosureKind, ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy, IntVarValue, IntVid, OutlivesPredicate, RegionVid, TermKind, TyVid, TypeFoldable, TypeFolder, @@ -22,19 +20,25 @@ use rustc_type_ir::{ Const as _, GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _, }, }; +use rustc_type_ir::{ + Upcast, + solve::{NoSolution, inspect}, +}; use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use traits::{ObligationCause, PredicateObligations}; -use type_variable::TypeVariableOrigin; -use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; +use unify_key::{ConstVariableValue, ConstVidKey}; pub use crate::next_solver::infer::traits::ObligationInspector; -use crate::next_solver::{ - ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVariableKind, Goal, Predicate, - SolverContext, - fold::BoundVarReplacerDelegate, - infer::{at::ToTrace, select::EvaluationResult, traits::PredicateObligation}, - obligation_ctxt::ObligationCtxt, +use crate::{ + Span, + next_solver::{ + ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVariableKind, Goal, Predicate, + SolverContext, + fold::BoundVarReplacerDelegate, + infer::{at::ToTrace, select::EvaluationResult, traits::PredicateObligation}, + obligation_ctxt::ObligationCtxt, + }, }; use super::{ @@ -48,6 +52,7 @@ use super::{ pub mod at; pub mod canonical; mod context; +pub mod errors; pub mod opaque_types; mod outlives; pub mod region_constraints; @@ -55,7 +60,7 @@ pub mod relate; pub mod resolve; pub mod select; pub(crate) mod snapshot; -pub(crate) mod traits; +pub mod traits; mod type_variable; mod unify_key; @@ -361,13 +366,14 @@ impl<'db> InferCtxtBuilder<'db> { /// (in other words, `S(C) = V`). pub fn build_with_canonical<T>( mut self, + span: Span, input: &CanonicalQueryInput<'db, T>, ) -> (InferCtxt<'db>, T, CanonicalVarValues<'db>) where T: TypeFoldable<DbInterner<'db>>, { let infcx = self.build(input.typing_mode.0); - let (value, args) = infcx.instantiate_canonical(&input.canonical); + let (value, args) = infcx.instantiate_canonical(span, &input.canonical); (infcx, value, args) } @@ -396,7 +402,7 @@ impl<'db> InferOk<'db, ()> { impl<'db> InferCtxt<'db> { #[inline(always)] - pub fn typing_mode(&self) -> TypingMode<'db> { + pub fn typing_mode_raw(&self) -> TypingMode<'db> { self.typing_mode } @@ -441,7 +447,11 @@ impl<'db> InferCtxt<'db> { return ty; } - if ty.is_ty_error() { self.infcx.next_ty_var() } else { ty.super_fold_with(self) } + if ty.is_ty_error() { + self.infcx.next_ty_var(Span::Dummy) + } else { + ty.super_fold_with(self) + } } fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { @@ -450,14 +460,14 @@ impl<'db> InferCtxt<'db> { } if ct.is_ct_error() { - self.infcx.next_const_var() + self.infcx.next_const_var(Span::Dummy) } else { ct.super_fold_with(self) } } fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if r.is_error() { self.infcx.next_region_var() } else { r } + if r.is_error() { self.infcx.next_region_var(Span::Dummy) } else { r } } } @@ -491,8 +501,7 @@ impl<'db> InferCtxt<'db> { /// check::<&'_ T>(); /// } /// ``` - #[expect(dead_code, reason = "this is used in rustc")] - fn predicate_must_hold_considering_regions( + pub fn predicate_must_hold_considering_regions( &self, obligation: &PredicateObligation<'db>, ) -> bool { @@ -504,11 +513,59 @@ impl<'db> InferCtxt<'db> { /// not entirely accurate if inference variables are involved. /// /// This version ignores all outlives constraints. - #[expect(dead_code, reason = "this is used in rustc")] - fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'db>) -> bool { + pub fn predicate_must_hold_modulo_regions( + &self, + obligation: &PredicateObligation<'db>, + ) -> bool { self.evaluate_obligation(obligation).must_apply_modulo_regions() } + /// Check whether a `ty` implements given trait(trait_def_id) without side-effects. + /// + /// The inputs are: + /// + /// - the def-id of the trait + /// - the type parameters of the trait, including the self-type + /// - the parameter environment + /// + /// Invokes `evaluate_obligation`, so in the event that evaluating + /// `Ty: Trait` causes overflow, EvaluatedToAmbigStackDependent will be returned. + /// + /// `type_implements_trait` is a convenience function for simple cases like + /// + /// ```ignore (illustrative) + /// let copy_trait = infcx.tcx.require_lang_item(LangItem::Copy, span); + /// let implements_copy = infcx.type_implements_trait(copy_trait, [ty], param_env) + /// .must_apply_modulo_regions(); + /// ``` + /// + /// In most cases you should instead create an [Obligation] and check whether + /// it holds via [`evaluate_obligation`] or one of its helper functions like + /// [`predicate_must_hold_modulo_regions`], because it properly handles higher ranked traits + /// and it is more convenient and safer when your `params` are inside a [`Binder`]. + /// + /// [Obligation]: traits::Obligation + /// [`evaluate_obligation`]: InferCtxt::evaluate_obligation + /// [`predicate_must_hold_modulo_regions`]: InferCtxt::predicate_must_hold_modulo_regions + /// [`Binder`]: rustc_type_ir::Binder + #[instrument(level = "debug", skip(self, params), ret)] + pub fn type_implements_trait( + &self, + trait_def_id: TraitId, + params: impl IntoIterator<Item: Into<GenericArg<'db>>>, + param_env: ParamEnv<'db>, + ) -> EvaluationResult { + let trait_ref = TraitRef::new(self.interner, trait_def_id.into(), params); + + let obligation = traits::Obligation { + cause: traits::ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: trait_ref.upcast(self.interner), + }; + self.evaluate_obligation(&obligation) + } + /// Evaluate a given predicate, capturing overflow and propagating it back. fn evaluate_obligation(&self, obligation: &PredicateObligation<'db>) -> EvaluationResult { self.probe(|snapshot| { @@ -561,6 +618,13 @@ impl<'db> InferCtxt<'db> { traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) } + pub fn type_is_sized_modulo_regions(&self, param_env: ParamEnv<'db>, ty: Ty<'db>) -> bool { + let Some(sized_def_id) = self.interner.lang_items().Sized else { + return true; + }; + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, sized_def_id) + } + pub fn type_is_use_cloned_modulo_regions(&self, param_env: ParamEnv<'db>, ty: Ty<'db>) -> bool { let ty = self.resolve_vars_if_possible(ty); @@ -680,80 +744,49 @@ impl<'db> InferCtxt<'db> { self.inner.borrow_mut().type_variables().num_vars() } - pub fn next_var_for_param(&self, id: GenericParamId) -> GenericArg<'db> { - match id { - GenericParamId::TypeParamId(_) => self.next_ty_var().into(), - GenericParamId::ConstParamId(_) => self.next_const_var().into(), - GenericParamId::LifetimeParamId(_) => self.next_region_var().into(), - } + pub fn next_ty_var(&self, span: Span) -> Ty<'db> { + let vid = self.next_ty_vid(span); + Ty::new_var(self.interner, vid) } - pub fn next_ty_var(&self) -> Ty<'db> { - self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None }) + pub fn next_ty_vid(&self, span: Span) -> TyVid { + self.next_ty_var_id_in_universe(self.universe(), span) } - pub fn next_ty_vid(&self) -> TyVid { - self.inner - .borrow_mut() - .type_variables() - .new_var(self.universe(), TypeVariableOrigin { param_def_id: None }) + pub fn next_ty_var_id_in_universe(&self, universe: UniverseIndex, span: Span) -> TyVid { + self.inner.borrow_mut().type_variables().new_var(universe, span) } - pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> { - let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin); + pub fn next_ty_var_in_universe(&self, universe: UniverseIndex, span: Span) -> Ty<'db> { + let vid = self.next_ty_var_id_in_universe(universe, span); Ty::new_var(self.interner, vid) } - pub fn next_ty_var_id_in_universe(&self, universe: UniverseIndex) -> TyVid { - let origin = TypeVariableOrigin { param_def_id: None }; - self.inner.borrow_mut().type_variables().new_var(universe, origin) - } - - pub fn next_ty_var_in_universe(&self, universe: UniverseIndex) -> Ty<'db> { - let vid = self.next_ty_var_id_in_universe(universe); - Ty::new_var(self.interner, vid) + pub fn next_const_var(&self, span: Span) -> Const<'db> { + let vid = self.next_const_vid(span); + Const::new_var(self.interner, vid) } - pub fn next_const_var(&self) -> Const<'db> { - self.next_const_var_with_origin(ConstVariableOrigin {}) + pub fn next_const_vid(&self, span: Span) -> ConstVid { + self.next_const_vid_in_universe(self.universe(), span) } - pub fn next_const_vid(&self) -> ConstVid { + pub fn next_const_vid_in_universe(&self, universe: UniverseIndex, span: Span) -> ConstVid { self.inner .borrow_mut() .const_unification_table() - .new_key(ConstVariableValue::Unknown { - origin: ConstVariableOrigin {}, - universe: self.universe(), - }) + .new_key(ConstVariableValue::Unknown { span, universe }) .vid } - pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> Const<'db> { - let vid = self - .inner - .borrow_mut() - .const_unification_table() - .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) - .vid; - Const::new_var(self.interner, vid) - } - - pub fn next_const_var_in_universe(&self, universe: UniverseIndex) -> Const<'db> { - let origin = ConstVariableOrigin {}; - let vid = self - .inner - .borrow_mut() - .const_unification_table() - .new_key(ConstVariableValue::Unknown { origin, universe }) - .vid; + pub fn next_const_var_in_universe(&self, universe: UniverseIndex, span: Span) -> Const<'db> { + let vid = self.next_const_vid_in_universe(universe, span); Const::new_var(self.interner, vid) } pub fn next_int_var(&self) -> Ty<'db> { - let next_int_var_id = - self.inner.borrow_mut().int_unification_table().new_key(IntVarValue::Unknown); - Ty::new_int_var(self.interner, next_int_var_id) + let vid = self.next_int_vid(); + Ty::new_int_var(self.interner, vid) } pub fn next_int_vid(&self) -> IntVid { @@ -771,27 +804,27 @@ impl<'db> InferCtxt<'db> { /// Creates a fresh region variable with the next available index. /// The variable will be created in the maximum universe created /// thus far, allowing it to name any region created thus far. - pub fn next_region_var(&self) -> Region<'db> { - self.next_region_var_in_universe(self.universe()) + pub fn next_region_var(&self, span: Span) -> Region<'db> { + self.next_region_var_in_universe(self.universe(), span) } - pub fn next_region_vid(&self) -> RegionVid { - self.inner.borrow_mut().unwrap_region_constraints().new_region_var(self.universe()) + pub fn next_region_vid(&self, span: Span) -> RegionVid { + self.inner.borrow_mut().unwrap_region_constraints().new_region_var(self.universe(), span) } /// Creates a fresh region variable with the next available index /// in the given universe; typically, you can use /// `next_region_var` and just use the maximal universe. - pub fn next_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> { + pub fn next_region_var_in_universe(&self, universe: UniverseIndex, span: Span) -> Region<'db> { let region_var = - self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe); + self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe, span); Region::new_var(self.interner, region_var) } - pub fn next_term_var_of_kind(&self, term: Term<'db>) -> Term<'db> { + pub fn next_term_var_of_kind(&self, term: Term<'db>, span: Span) -> Term<'db> { match term.kind() { - TermKind::Ty(_) => self.next_ty_var().into(), - TermKind::Const(_) => self.next_const_var().into(), + TermKind::Ty(_) => self.next_ty_var(span).into(), + TermKind::Const(_) => self.next_const_var(span).into(), } } @@ -809,24 +842,12 @@ impl<'db> InferCtxt<'db> { self.inner.borrow_mut().unwrap_region_constraints().num_region_vars() } - /// Just a convenient wrapper of `next_region_var` for using during NLL. - #[instrument(skip(self), level = "debug")] - pub fn next_nll_region_var(&self) -> Region<'db> { - self.next_region_var() - } - - /// Just a convenient wrapper of `next_region_var` for using during NLL. - #[instrument(skip(self), level = "debug")] - pub fn next_nll_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> { - self.next_region_var_in_universe(universe) - } - - fn var_for_def(&self, id: GenericParamId) -> GenericArg<'db> { + pub fn var_for_def(&self, id: GenericParamId, span: Span) -> GenericArg<'db> { match id { GenericParamId::LifetimeParamId(_) => { // Create a region inference variable for the given // region parameter definition. - self.next_region_var().into() + self.next_region_var(span).into() } GenericParamId::TypeParamId(_) => { // Create a type inference variable for the given @@ -837,41 +858,27 @@ impl<'db> InferCtxt<'db> { // used in a path such as `Foo::<T, U>::new()` will // use an inference variable for `C` with `[T, U]` // as the generic parameters for the default, `(T, U)`. - let ty_var_id = self - .inner - .borrow_mut() - .type_variables() - .new_var(self.universe(), TypeVariableOrigin { param_def_id: None }); - - Ty::new_var(self.interner, ty_var_id).into() - } - GenericParamId::ConstParamId(_) => { - let origin = ConstVariableOrigin {}; - let const_var_id = self - .inner - .borrow_mut() - .const_unification_table() - .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) - .vid; - Const::new_var(self.interner, const_var_id).into() + self.next_ty_var(span).into() } + GenericParamId::ConstParamId(_) => self.next_const_var(span).into(), } } /// Given a set of generics defined on a type or impl, returns the generic parameters mapping /// each type/region parameter to a fresh inference variable. - pub fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> { - GenericArgs::for_item(self.interner, def_id, |_index, kind, _| self.var_for_def(kind)) + pub fn fresh_args_for_item(&self, span: Span, def_id: SolverDefId) -> GenericArgs<'db> { + GenericArgs::for_item(self.interner, def_id, |_index, kind, _| self.var_for_def(kind, span)) } /// Like `fresh_args_for_item()`, but first uses the args from `first`. pub fn fill_rest_fresh_args( &self, + span: Span, def_id: SolverDefId, first: impl IntoIterator<Item = GenericArg<'db>>, ) -> GenericArgs<'db> { GenericArgs::fill_rest(self.interner, def_id, first, |_index, kind, _| { - self.var_for_def(kind) + self.var_for_def(kind, span) }) } @@ -922,7 +929,7 @@ impl<'db> InferCtxt<'db> { #[inline(always)] pub fn can_define_opaque_ty(&self, id: impl Into<SolverDefId>) -> bool { - match self.typing_mode_unchecked() { + match self.typing_mode_raw().assert_not_erased() { TypingMode::Analysis { defining_opaque_types_and_generators } => { defining_opaque_types_and_generators.contains(&id.into()) } @@ -938,8 +945,8 @@ impl<'db> InferCtxt<'db> { use self::type_variable::TypeVariableValue; match self.inner.borrow_mut().type_variables().probe(vid) { - TypeVariableValue::Known { value } => Ok(value), - TypeVariableValue::Unknown { universe } => Err(universe), + TypeVariableValue::Known { value, .. } => Ok(value), + TypeVariableValue::Unknown { universe, .. } => Err(universe), } } @@ -1007,6 +1014,13 @@ impl<'db> InferCtxt<'db> { } } + pub fn shallow_resolve_term(&self, term: Term<'db>) -> Term<'db> { + match term.kind() { + TermKind::Ty(ty) => self.shallow_resolve(ty).into(), + TermKind::Const(ct) => self.shallow_resolve_const(ct).into(), + } + } + pub fn root_var(&self, var: TyVid) -> TyVid { self.inner.borrow_mut().type_variables().root_var(var) } @@ -1084,7 +1098,22 @@ impl<'db> InferCtxt<'db> { pub fn probe_const_var(&self, vid: ConstVid) -> Result<Const<'db>, UniverseIndex> { match self.inner.borrow_mut().const_unification_table().probe_value(vid) { ConstVariableValue::Known { value } => Ok(value), - ConstVariableValue::Unknown { origin: _, universe } => Err(universe), + ConstVariableValue::Unknown { span: _, universe } => Err(universe), + } + } + + /// Returns the span of the type variable identified by `vid`. + /// + /// No attempt is made to resolve `vid` to its root variable. + pub fn type_var_span(&self, vid: TyVid) -> Span { + self.inner.borrow_mut().type_variables().var_span(vid) + } + + /// Returns the span of the const variable identified by `vid` + pub fn const_var_span(&self, vid: ConstVid) -> Option<Span> { + match self.inner.borrow_mut().const_unification_table().probe_value(vid) { + ConstVariableValue::Known { .. } => None, + ConstVariableValue::Unknown { span, .. } => Some(span), } } @@ -1097,6 +1126,7 @@ impl<'db> InferCtxt<'db> { // use [`InferCtxt::enter_forall`] instead. pub fn instantiate_binder_with_fresh_vars<T>( &self, + span: Span, _lbrct: BoundRegionConversionTime, value: Binder<'db, T>, ) -> T @@ -1112,9 +1142,9 @@ impl<'db> InferCtxt<'db> { for bound_var_kind in bound_vars { let arg: GenericArg<'db> = match bound_var_kind { - BoundVariableKind::Ty(_) => self.next_ty_var().into(), - BoundVariableKind::Region(_) => self.next_region_var().into(), - BoundVariableKind::Const => self.next_const_var().into(), + BoundVariableKind::Ty(_) => self.next_ty_var(span).into(), + BoundVariableKind::Region(_) => self.next_region_var(span).into(), + BoundVariableKind::Const => self.next_const_var(span).into(), }; args.push(arg); } @@ -1169,7 +1199,7 @@ impl<'db> InferCtxt<'db> { #[inline] pub fn is_ty_infer_var_definitely_unchanged<'a>( &'a self, - ) -> impl Fn(TyOrConstInferVar) -> bool + Captures<'db> + 'a { + ) -> impl Fn(TyOrConstInferVar) -> bool + use<'a, 'db> { // This hoists the borrow/release out of the loop body. let inner = self.inner.try_borrow(); @@ -1321,7 +1351,7 @@ impl TyOrConstInferVar { impl<'db> TypeTrace<'db> { pub fn types(cause: &ObligationCause, a: Ty<'db>, b: Ty<'db>) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), } } @@ -1331,12 +1361,12 @@ impl<'db> TypeTrace<'db> { a: TraitRef<'db>, b: TraitRef<'db>, ) -> TypeTrace<'db> { - TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } + TypeTrace { cause: *cause, values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } } pub fn consts(cause: &ObligationCause, a: Const<'db>, b: Const<'db>) -> TypeTrace<'db> { TypeTrace { - cause: cause.clone(), + cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), } } diff --git a/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs index 7bb39519f5..544d79daf0 100644 --- a/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs +++ b/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs @@ -15,9 +15,12 @@ use self::CombineMapType::*; use self::UndoLog::*; use super::MemberConstraint; use super::unify_key::RegionVidKey; -use crate::next_solver::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot}; use crate::next_solver::infer::unify_key::RegionVariableValue; use crate::next_solver::{AliasTy, Binder, DbInterner, ParamTy, PlaceholderType, Region, Ty}; +use crate::{ + Span, + next_solver::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot}, +}; #[derive(Debug, Clone, Default)] pub struct RegionConstraintStorage<'db> { @@ -281,6 +284,7 @@ pub struct RegionVariableInfo { // This would be currently unsound as it would cause us to drop the universe // changes in `lexical_region_resolve`. pub universe: UniverseIndex, + pub span: Span, } pub(crate) struct RegionSnapshot { @@ -350,8 +354,12 @@ impl<'db> RegionConstraintCollector<'db, '_> { *any_unifications = false; // Manually inlined `self.unification_table_mut()` as `self` is used in the closure. ut::UnificationTable::with_log(&mut self.storage.unification_table, &mut self.undo_log) - .reset_unifications(|key| RegionVariableValue::Unknown { - universe: self.storage.var_infos[key.vid].universe, + .reset_unifications(|key| { + let var_info = &self.storage.var_infos[key.vid]; + RegionVariableValue::Unknown { + universe: var_info.universe, + span: var_info.span, + } }); } @@ -372,10 +380,11 @@ impl<'db> RegionConstraintCollector<'db, '_> { self.storage.any_unifications = snapshot.any_unifications; } - pub(super) fn new_region_var(&mut self, universe: UniverseIndex) -> RegionVid { - let vid = self.storage.var_infos.push(RegionVariableInfo { universe }); + pub(super) fn new_region_var(&mut self, universe: UniverseIndex, span: Span) -> RegionVid { + let vid = self.storage.var_infos.push(RegionVariableInfo { universe, span }); - let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe }); + let u_vid = + self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe, span }); assert_eq!(vid, u_vid.vid); self.undo_log.push(AddVar(vid)); debug!("created new region variable {:?} in {:?}", vid, universe); @@ -409,7 +418,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { debug!("make_eqregion: unifying {:?} with {:?}", vid, b); if self .unification_table_mut() - .unify_var_value(vid, RegionVariableValue::Known { value: b }) + .unify_var_value(vid, RegionVariableValue::Known { value: b, span: None }) .is_ok() { self.storage.any_unifications = true; @@ -419,7 +428,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { debug!("make_eqregion: unifying {:?} with {:?}", a, vid); if self .unification_table_mut() - .unify_var_value(vid, RegionVariableValue::Known { value: a }) + .unify_var_value(vid, RegionVariableValue::Known { value: a, span: None }) .is_ok() { self.storage.any_unifications = true; @@ -459,6 +468,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { pub(super) fn lub_regions( &mut self, db: DbInterner<'db>, + origin: Span, a: Region<'db>, b: Region<'db>, ) -> Region<'db> { @@ -470,13 +480,14 @@ impl<'db> RegionConstraintCollector<'db, '_> { } else if a == b { a // LUB(a,a) = a } else { - self.combine_vars(db, Lub, a, b) + self.combine_vars(db, Lub, a, b, origin) } } pub(super) fn glb_regions( &mut self, db: DbInterner<'db>, + origin: Span, a: Region<'db>, b: Region<'db>, ) -> Region<'db> { @@ -490,7 +501,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { } else if a == b { a // GLB(a,a) = a } else { - self.combine_vars(db, Glb, a, b) + self.combine_vars(db, Glb, a, b, origin) } } @@ -504,15 +515,15 @@ impl<'db> RegionConstraintCollector<'db, '_> { let mut ut = self.unification_table_mut(); let root_vid = ut.find(vid).vid; match ut.probe_value(root_vid) { - RegionVariableValue::Known { value } => value, + RegionVariableValue::Known { value, .. } => value, RegionVariableValue::Unknown { .. } => Region::new_var(cx, root_vid), } } pub fn probe_value(&mut self, vid: RegionVid) -> Result<Region<'db>, UniverseIndex> { match self.unification_table_mut().probe_value(vid) { - RegionVariableValue::Known { value } => Ok(value), - RegionVariableValue::Unknown { universe } => Err(universe), + RegionVariableValue::Known { value, .. } => Ok(value), + RegionVariableValue::Unknown { universe, .. } => Err(universe), } } @@ -529,6 +540,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { t: CombineMapType, a: Region<'db>, b: Region<'db>, + origin: Span, ) -> Region<'db> { let vars = TwoRegions { a, b }; if let Some(c) = self.combine_map(t.clone()).get(&vars) { @@ -537,7 +549,7 @@ impl<'db> RegionConstraintCollector<'db, '_> { let a_universe = self.universe(a); let b_universe = self.universe(b); let c_universe = cmp::max(a_universe, b_universe); - let c = self.new_region_var(c_universe); + let c = self.new_region_var(c_universe, origin); self.combine_map(t.clone()).insert(vars.clone(), c); self.undo_log.push(AddCombination(t.clone(), vars)); let new_r = Region::new_var(cx, c); @@ -567,8 +579,15 @@ impl<'db> RegionConstraintCollector<'db, '_> { } } - pub fn vars_since_snapshot(&self, value_count: usize) -> Range<RegionVid> { - RegionVid::from(value_count)..RegionVid::from(self.storage.unification_table.len()) + pub fn vars_since_snapshot(&self, value_count: usize) -> (Range<RegionVid>, Vec<Span>) { + let range = + RegionVid::from(value_count)..RegionVid::from(self.storage.unification_table.len()); + ( + range.clone(), + (range.start.as_usize()..range.end.as_usize()) + .map(|index| self.storage.var_infos[RegionVid::from_usize(index)].span) + .collect(), + ) } /// See `InferCtxt::region_constraints_added_in_snapshot`. diff --git a/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs index d621dd4906..e55e43a4cd 100644 --- a/crates/hir-ty/src/next_solver/infer/relate/generalize.rs +++ b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs @@ -16,7 +16,6 @@ use tracing::{debug, instrument, warn}; use super::{ PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation, }; -use crate::next_solver::infer::unify_key::ConstVariableValue; use crate::next_solver::infer::{InferCtxt, relate}; use crate::next_solver::util::MaxUniverse; use crate::next_solver::{ @@ -24,6 +23,7 @@ use crate::next_solver::{ Term, TermVid, Ty, TyKind, TypingMode, UnevaluatedConst, }; use crate::next_solver::{GenericArgs, infer::type_variable::TypeVariableValue}; +use crate::{Span, next_solver::infer::unify_key::ConstVariableValue}; impl<'db> InferCtxt<'db> { /// The idea is that we should ensure that the type variable `target_vid` @@ -60,6 +60,7 @@ impl<'db> InferCtxt<'db> { // `?1 <: ?3`. let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = self .generalize( + relation.span(), relation.structurally_relate_aliases(), target_vid, instantiation_variance, @@ -179,6 +180,7 @@ impl<'db> InferCtxt<'db> { // constants and generic expressions are not yet handled correctly. let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } = self .generalize( + relation.span(), relation.structurally_relate_aliases(), target_vid, Variance::Invariant, @@ -220,6 +222,7 @@ impl<'db> InferCtxt<'db> { /// This checks for cycles -- that is, whether `source_term` references `target_vid`. fn generalize<T: Into<Term<'db>> + Relate<DbInterner<'db>>>( &self, + span: Span, structurally_relate_aliases: StructurallyRelateAliases, target_vid: impl Into<TermVid>, ambient_variance: Variance, @@ -238,6 +241,7 @@ impl<'db> InferCtxt<'db> { let mut generalizer = Generalizer { infcx: self, + span, structurally_relate_aliases, root_vid, for_universe, @@ -270,6 +274,8 @@ impl<'db> InferCtxt<'db> { struct Generalizer<'me, 'db> { infcx: &'me InferCtxt<'db>, + span: Span, + /// Whether aliases should be related structurally. If not, we have to /// be careful when generalizing aliases. structurally_relate_aliases: StructurallyRelateAliases, @@ -318,7 +324,7 @@ impl<'db> Generalizer<'_, 'db> { /// if we're currently in a bivariant context. fn next_ty_var_for_alias(&mut self) -> Ty<'db> { self.has_unconstrained_ty_var |= self.ambient_variance == Variance::Bivariant; - self.infcx.next_ty_var_in_universe(self.for_universe) + self.infcx.next_ty_var_in_universe(self.for_universe, self.span) } /// An occurs check failure inside of an alias does not mean @@ -453,11 +459,11 @@ impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> { } else { let probe = inner.type_variables().probe(vid); match probe { - TypeVariableValue::Known { value: u } => { + TypeVariableValue::Known { value: u, .. } => { drop(inner); self.relate(u, u) } - TypeVariableValue::Unknown { universe } => { + TypeVariableValue::Unknown { universe, .. } => { match self.ambient_variance { // Invariant: no need to make a fresh type variable // if we can name the universe. @@ -477,7 +483,7 @@ impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> { Variance::Covariant | Variance::Contravariant => (), } - let origin = inner.type_variables().var_origin(vid); + let origin = inner.type_variables().var_span(vid); let new_var_id = inner.type_variables().new_var(self.for_universe, origin); // If we're in the new solver and create a new inference @@ -579,7 +585,7 @@ impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> { } } - Ok(self.infcx.next_region_var_in_universe(self.for_universe)) + Ok(self.infcx.next_region_var_in_universe(self.for_universe, self.span)) } #[instrument(level = "debug", skip(self, c2), ret)] @@ -605,13 +611,13 @@ impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> { drop(inner); self.relate(u, u) } - ConstVariableValue::Unknown { origin, universe } => { + ConstVariableValue::Unknown { span, universe } => { if self.for_universe.can_name(universe) { Ok(c) } else { let new_var_id = variable_table .new_key(ConstVariableValue::Unknown { - origin, + span, universe: self.for_universe, }) .vid; diff --git a/crates/hir-ty/src/next_solver/infer/relate/lattice.rs b/crates/hir-ty/src/next_solver/infer/relate/lattice.rs index 3522827a9e..f3af697feb 100644 --- a/crates/hir-ty/src/next_solver/infer/relate/lattice.rs +++ b/crates/hir-ty/src/next_solver/infer/relate/lattice.rs @@ -19,7 +19,7 @@ use rustc_type_ir::{ AliasRelationDirection, Interner, TypeVisitableExt, Upcast, Variance, - inherent::{IntoKind, Span as _}, + inherent::IntoKind, relate::{ Relate, StructurallyRelateAliases, TypeRelation, VarianceDiagInfo, combine::{ @@ -28,13 +28,16 @@ use rustc_type_ir::{ }, }; -use crate::next_solver::{ - AliasTy, Binder, Const, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, PredicateKind, - Region, SolverDefId, Span, Ty, TyKind, - infer::{ - InferCtxt, TypeTrace, - relate::RelateResult, - traits::{Obligation, PredicateObligations}, +use crate::{ + Span, + next_solver::{ + AliasTy, Binder, Const, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, PredicateKind, + Region, SolverDefId, Ty, TyKind, + infer::{ + InferCtxt, TypeTrace, + relate::RelateResult, + traits::{Obligation, PredicateObligations}, + }, }, }; @@ -154,12 +157,12 @@ impl<'db> TypeRelation<DbInterner<'db>> for LatticeOp<'_, 'db> { // iterate on the subtype obligations that are returned, but I // think this suffices. -nmatsakis (TyKind::Infer(rustc_type_ir::TyVar(..)), _) => { - let v = infcx.next_ty_var(); + let v = infcx.next_ty_var(self.span()); self.relate_bound(v, b, a)?; Ok(v) } (_, TyKind::Infer(rustc_type_ir::TyVar(..))) => { - let v = infcx.next_ty_var(); + let v = infcx.next_ty_var(self.span()); self.relate_bound(v, a, b)?; Ok(v) } @@ -178,10 +181,10 @@ impl<'db> TypeRelation<DbInterner<'db>> for LatticeOp<'_, 'db> { let mut constraints = inner.unwrap_region_constraints(); Ok(match self.kind { // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 - LatticeOpKind::Glb => constraints.lub_regions(self.cx(), a, b), + LatticeOpKind::Glb => constraints.lub_regions(self.cx(), self.span(), a, b), // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 - LatticeOpKind::Lub => constraints.glb_regions(self.cx(), a, b), + LatticeOpKind::Lub => constraints.glb_regions(self.cx(), self.span(), a, b), }) } @@ -239,7 +242,7 @@ impl<'infcx, 'db> LatticeOp<'infcx, 'db> { impl<'db> PredicateEmittingRelation<InferCtxt<'db>> for LatticeOp<'_, 'db> { fn span(&self) -> Span { - Span::dummy() + self.trace.cause.span() } fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { @@ -255,18 +258,13 @@ impl<'db> PredicateEmittingRelation<InferCtxt<'db>> for LatticeOp<'_, 'db> { preds: impl IntoIterator<Item: Upcast<DbInterner<'db>, Predicate<'db>>>, ) { self.obligations.extend(preds.into_iter().map(|pred| { - Obligation::new(self.infcx.interner, self.trace.cause.clone(), self.param_env, pred) + Obligation::new(self.infcx.interner, self.trace.cause, self.param_env, pred) })) } fn register_goals(&mut self, goals: impl IntoIterator<Item = Goal<'db, Predicate<'db>>>) { self.obligations.extend(goals.into_iter().map(|goal| { - Obligation::new( - self.infcx.interner, - self.trace.cause.clone(), - goal.param_env, - goal.predicate, - ) + Obligation::new(self.infcx.interner, self.trace.cause, goal.param_env, goal.predicate) })) } diff --git a/crates/hir-ty/src/next_solver/infer/select.rs b/crates/hir-ty/src/next_solver/infer/select.rs index bd407fd157..d6f0379c11 100644 --- a/crates/hir-ty/src/next_solver/infer/select.rs +++ b/crates/hir-ty/src/next_solver/infer/select.rs @@ -10,6 +10,7 @@ use rustc_type_ir::{ }; use crate::{ + Span, db::InternedOpaqueTyId, next_solver::{ AnyImplId, Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError, @@ -63,7 +64,7 @@ pub enum NotConstEvaluatable { /// so they are noops when unioned with a definite error, and within /// the categories it's easy to see that the unions are correct. #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] -pub(crate) enum EvaluationResult { +pub enum EvaluationResult { /// Evaluation successful. EvaluatedToOk, /// Evaluation successful, but there were unevaluated region obligations. @@ -263,18 +264,24 @@ impl<'db> InferCtxt<'db> { ) -> SelectionResult<'db, Selection<'db>> { self.visit_proof_tree( Goal::new(self.interner, obligation.param_env, obligation.predicate), - &mut Select {}, + &mut Select { span: obligation.cause.span() }, ) .break_value() .unwrap() } } -struct Select {} +struct Select { + span: Span, +} impl<'db> ProofTreeVisitor<'db> for Select { type Result = ControlFlow<SelectionResult<'db, Selection<'db>>>; + fn span(&self) -> Span { + self.span + } + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result { let mut candidates = goal.candidates(); candidates.retain(|cand| cand.result().is_ok()); @@ -286,7 +293,10 @@ impl<'db> ProofTreeVisitor<'db> for Select { // One candidate, no need to winnow. if candidates.len() == 1 { - return ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap()))); + return ControlFlow::Break(Ok(to_selection( + self.span, + candidates.into_iter().next().unwrap(), + ))); } // Don't winnow until `Certainty::Yes` -- we don't need to winnow until @@ -311,7 +321,7 @@ impl<'db> ProofTreeVisitor<'db> for Select { } } - ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap()))) + ControlFlow::Break(Ok(to_selection(self.span, candidates.into_iter().next().unwrap()))) } } @@ -368,7 +378,7 @@ fn candidate_should_be_dropped_in_favor_of<'db>( } } -fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>> { +fn to_selection<'db>(span: Span, cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>> { if let Certainty::Maybe { .. } = cand.shallow_certainty() { return None; } @@ -376,7 +386,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>> let nested = match cand.result().expect("expected positive result") { Certainty::Yes => Vec::new(), Certainty::Maybe { .. } => cand - .instantiate_nested_goals() + .instantiate_nested_goals(span) .into_iter() .map(|nested| { Obligation::new( @@ -396,7 +406,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>> // For impl candidates, we do the rematch manually to compute the args. ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, - args: cand.instantiate_impl_args(), + args: cand.instantiate_impl_args(span), nested, }) } diff --git a/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs b/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs index 5902f8043b..7cb3ab09d4 100644 --- a/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs +++ b/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs @@ -5,17 +5,19 @@ use ena::{ unify::{self as ut, UnifyKey}, }; use rustc_type_ir::{ - ConstVid, FloatVid, IntVid, RegionKind, RegionVid, TyVid, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitableExt, inherent::IntoKind, + ConstVid, FloatVid, IntVid, RegionVid, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, inherent::IntoKind, }; -use crate::next_solver::{ - Const, ConstKind, DbInterner, Region, Ty, TyKind, - infer::{ - InferCtxt, UnificationTable, iter_idx_range, - snapshot::VariableLengths, - type_variable::TypeVariableOrigin, - unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}, +use crate::{ + Span, + next_solver::{ + Const, ConstKind, DbInterner, Region, RegionKind, Ty, TyKind, + infer::{ + InferCtxt, UnificationTable, iter_idx_range, + snapshot::VariableLengths, + unify_key::{ConstVariableValue, ConstVidKey}, + }, }, }; @@ -33,7 +35,7 @@ where fn const_vars_since_snapshot<'db>( table: &mut UnificationTable<'_, 'db, ConstVidKey<'db>>, snapshot_var_len: usize, -) -> (Range<ConstVid>, Vec<ConstVariableOrigin>) { +) -> (Range<ConstVid>, Vec<Span>) { let range = vars_since_snapshot(table, snapshot_var_len); let range = range.start.vid..range.end.vid; @@ -41,8 +43,8 @@ fn const_vars_since_snapshot<'db>( range.clone(), iter_idx_range(range) .map(|index| match table.probe_value(index) { - ConstVariableValue::Known { value: _ } => ConstVariableOrigin {}, - ConstVariableValue::Unknown { origin, universe: _ } => origin, + ConstVariableValue::Known { value: _ } => Span::Dummy, + ConstVariableValue::Unknown { span, universe: _ } => span, }) .collect(), ) @@ -128,11 +130,11 @@ impl<'db> InferCtxt<'db> { } struct SnapshotVarData { - region_vars: Range<RegionVid>, - type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>), + region_vars: (Range<RegionVid>, Vec<Span>), + type_vars: (Range<TyVid>, Vec<Span>), int_vars: Range<IntVid>, float_vars: Range<FloatVid>, - const_vars: (Range<ConstVid>, Vec<ConstVariableOrigin>), + const_vars: (Range<ConstVid>, Vec<Span>), } impl SnapshotVarData { @@ -156,7 +158,7 @@ impl SnapshotVarData { fn is_empty(&self) -> bool { let SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } = self; - region_vars.is_empty() + region_vars.0.is_empty() && type_vars.0.is_empty() && int_vars.is_empty() && float_vars.is_empty() @@ -182,8 +184,8 @@ impl<'a, 'db> TypeFolder<DbInterner<'db>> for InferenceFudger<'a, 'db> { // This variable was created during the fudging. // Recreate it with a fresh variable here. let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize(); - let origin = self.snapshot_vars.type_vars.1[idx]; - self.infcx.next_ty_var_with_origin(origin) + let span = self.snapshot_vars.type_vars.1[idx]; + self.infcx.next_ty_var(span) } else { // This variable was created before the // "fudging". Since we refresh all type @@ -225,8 +227,10 @@ impl<'a, 'db> TypeFolder<DbInterner<'db>> for InferenceFudger<'a, 'db> { fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { if let RegionKind::ReVar(vid) = r.kind() { - if self.snapshot_vars.region_vars.contains(&vid) { - self.infcx.next_region_var() + if self.snapshot_vars.region_vars.0.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.region_vars.0.start.index(); + let span = self.snapshot_vars.region_vars.1[idx]; + self.infcx.next_region_var(span) } else { r } @@ -241,8 +245,8 @@ impl<'a, 'db> TypeFolder<DbInterner<'db>> for InferenceFudger<'a, 'db> { rustc_type_ir::InferConst::Var(vid) => { if self.snapshot_vars.const_vars.0.contains(&vid) { let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index(); - let origin = self.snapshot_vars.const_vars.1[idx]; - self.infcx.next_const_var_with_origin(origin) + let span = self.snapshot_vars.const_vars.1[idx]; + self.infcx.next_const_var(span) } else { ct } diff --git a/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs index 705aa43fb1..39c8a37adb 100644 --- a/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs +++ b/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -47,7 +47,7 @@ impl<'db> InferCtxt<'db> { UndoLogs::<UndoLog<'db>>::num_open_snapshots(&self.inner.borrow_mut().undo_log) } - pub(crate) fn start_snapshot(&self) -> CombinedSnapshot { + pub fn start_snapshot(&self) -> CombinedSnapshot { debug!("start_snapshot()"); let mut inner = self.inner.borrow_mut(); @@ -60,7 +60,7 @@ impl<'db> InferCtxt<'db> { } #[instrument(skip(self, snapshot), level = "debug")] - pub(crate) fn rollback_to(&self, snapshot: CombinedSnapshot) { + pub fn rollback_to(&self, snapshot: CombinedSnapshot) { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot; self.universe.set(universe); @@ -71,7 +71,7 @@ impl<'db> InferCtxt<'db> { } #[instrument(skip(self, snapshot), level = "debug")] - fn commit_from(&self, snapshot: CombinedSnapshot) { + pub fn commit_from(&self, snapshot: CombinedSnapshot) { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } = snapshot; diff --git a/crates/hir-ty/src/next_solver/infer/traits.rs b/crates/hir-ty/src/next_solver/infer/traits.rs index dde6234836..4584b35796 100644 --- a/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/crates/hir-ty/src/next_solver/infer/traits.rs @@ -16,49 +16,37 @@ use rustc_type_ir::{ }; use tracing::debug; -use crate::next_solver::{ - Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, Span, TraitPredicate, - TraitRef, Ty, +use crate::{ + Span, + next_solver::{ + Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, TraitPredicate, + TraitRef, Ty, + }, }; use super::InferCtxt; /// The reason why we incurred this obligation; used for error reporting. -/// -/// Non-misc `ObligationCauseCode`s are stored on the heap. This gives the -/// best trade-off between keeping the type small (which makes copies cheaper) -/// while not doing too many heap allocations. -/// -/// We do not want to intern this as there are a lot of obligation causes which -/// only live for a short period of time. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeVisitable, TypeFoldable)] pub struct ObligationCause { - // FIXME: This should contain an `ExprId`/`PatId` etc., and a cause code. But for now we - // don't report trait solving diagnostics, so this is irrelevant. - _private: (), + #[type_visitable(ignore)] + span: Span, } impl ObligationCause { #[inline] - pub fn new() -> ObligationCause { - ObligationCause { _private: () } + pub fn new<S: Into<Span>>(span: S) -> ObligationCause { + ObligationCause { span: span.into() } } #[inline] pub fn dummy() -> ObligationCause { - ObligationCause::new() + ObligationCause::new(Span::Dummy) } #[inline] - pub fn misc() -> ObligationCause { - ObligationCause::new() - } -} - -impl Default for ObligationCause { - #[inline] - fn default() -> Self { - Self::new() + pub(crate) fn span(&self) -> Span { + self.span } } @@ -107,7 +95,7 @@ impl<'db> Elaboratable<DbInterner<'db>> for PredicateObligation<'db> { fn child(&self, clause: Clause<'db>) -> Self { Obligation { - cause: self.cause.clone(), + cause: self.cause, param_env: self.param_env, recursion_depth: 0, predicate: clause.as_predicate(), @@ -117,11 +105,11 @@ impl<'db> Elaboratable<DbInterner<'db>> for PredicateObligation<'db> { fn child_with_derived_cause( &self, clause: Clause<'db>, - _span: Span, + span: Span, _parent_trait_pred: PolyTraitPredicate<'db>, _index: usize, ) -> Self { - let cause = ObligationCause::new(); + let cause = ObligationCause::new(span); Obligation { cause, param_env: self.param_env, @@ -175,7 +163,7 @@ impl<'db> PredicateObligation<'db> { /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. pub fn flip_polarity(&self, _interner: DbInterner<'db>) -> Option<PredicateObligation<'db>> { Some(PredicateObligation { - cause: self.cause.clone(), + cause: self.cause, param_env: self.param_env, predicate: self.predicate.flip_polarity()?, recursion_depth: self.recursion_depth, @@ -185,12 +173,12 @@ impl<'db> PredicateObligation<'db> { impl<'db, O> Obligation<'db, O> { pub fn new( - tcx: DbInterner<'db>, + interner: DbInterner<'db>, cause: ObligationCause, param_env: ParamEnv<'db>, predicate: impl Upcast<DbInterner<'db>, O>, ) -> Obligation<'db, O> { - Self::with_depth(tcx, cause, 0, param_env, predicate) + Self::with_depth(interner, cause, 0, param_env, predicate) } /// We often create nested obligations without setting the correct depth. @@ -202,13 +190,13 @@ impl<'db, O> Obligation<'db, O> { } pub fn with_depth( - tcx: DbInterner<'db>, + interner: DbInterner<'db>, cause: ObligationCause, recursion_depth: usize, param_env: ParamEnv<'db>, predicate: impl Upcast<DbInterner<'db>, O>, ) -> Obligation<'db, O> { - let predicate = predicate.upcast(tcx); + let predicate = predicate.upcast(interner); Obligation { cause, param_env, recursion_depth, predicate } } @@ -217,7 +205,7 @@ impl<'db, O> Obligation<'db, O> { tcx: DbInterner<'db>, value: impl Upcast<DbInterner<'db>, P>, ) -> Obligation<'db, P> { - Obligation::with_depth(tcx, self.cause.clone(), self.recursion_depth, self.param_env, value) + Obligation::with_depth(tcx, self.cause, self.recursion_depth, self.param_env, value) } } diff --git a/crates/hir-ty/src/next_solver/infer/type_variable.rs b/crates/hir-ty/src/next_solver/infer/type_variable.rs index 29e7b883c9..6b3936ba80 100644 --- a/crates/hir-ty/src/next_solver/infer/type_variable.rs +++ b/crates/hir-ty/src/next_solver/infer/type_variable.rs @@ -7,13 +7,12 @@ use std::ops::Range; use ena::snapshot_vec as sv; use ena::undo_log::Rollback; use ena::unify as ut; -use rustc_index::IndexVec; use rustc_type_ir::TyVid; use rustc_type_ir::UniverseIndex; use rustc_type_ir::inherent::Ty as _; use tracing::debug; -use crate::next_solver::SolverDefId; +use crate::Span; use crate::next_solver::Ty; use crate::next_solver::infer::{InferCtxtUndoLogs, iter_idx_range}; @@ -61,8 +60,6 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for TypeVariableStorage<'tcx> { #[derive(Debug, Clone, Default)] pub(crate) struct TypeVariableStorage<'db> { - /// The origins of each type variable. - values: IndexVec<TyVid, TypeVariableData>, /// Two variables are unified in `eq_relations` when we have a /// constraint `?X == ?Y`. This table also stores, for each key, /// the known value. @@ -94,20 +91,7 @@ pub(crate) struct TypeVariableTable<'a, 'db> { undo_log: &'a mut InferCtxtUndoLogs<'db>, } -#[derive(Copy, Clone, Debug)] -pub struct TypeVariableOrigin { - /// `DefId` of the type parameter this was instantiated for, if any. - /// - /// This should only be used for diagnostics. - pub param_def_id: Option<SolverDefId>, -} - -#[derive(Debug, Clone)] -pub(crate) struct TypeVariableData { - origin: TypeVariableOrigin, -} - -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub(crate) enum TypeVariableValue<'db> { Known { value: Ty<'db> }, Unknown { universe: UniverseIndex }, @@ -119,7 +103,7 @@ impl<'db> TypeVariableValue<'db> { pub(crate) fn known(&self) -> Option<Ty<'db>> { match self { TypeVariableValue::Unknown { .. } => None, - TypeVariableValue::Known { value } => Some(*value), + TypeVariableValue::Known { value, .. } => Some(*value), } } @@ -145,19 +129,14 @@ impl<'db> TypeVariableStorage<'db> { &self.eq_relations } - pub(super) fn finalize_rollback(&mut self) { - debug_assert!(self.values.len() >= self.eq_relations.len()); - self.values.truncate(self.eq_relations.len()); - } + pub(super) fn finalize_rollback(&mut self) {} } impl<'db> TypeVariableTable<'_, 'db> { - /// Returns the origin that was given when `vid` was created. - /// - /// Note that this function does not return care whether - /// `vid` has been unified with something else or not. - pub(crate) fn var_origin(&self, vid: TyVid) -> TypeVariableOrigin { - self.storage.values[vid].origin + pub(crate) fn var_span(&mut self, vid: TyVid) -> Span { + // We return the span from unification and not equation, since when equating we also unify, + // and we want to prevent duplicate diagnostics from vars that were unified. + self.sub_unification_table().probe_value(vid).span } /// Records that `a == b`, depending on `dir`. @@ -195,33 +174,20 @@ impl<'db> TypeVariableTable<'_, 'db> { self.eq_relations().union_value(vid, TypeVariableValue::Known { value: ty }); } - /// Creates a new type variable. - /// - /// - `diverging`: indicates if this is a "diverging" type - /// variable, e.g., one created as the type of a `return` - /// expression. The code in this module doesn't care if a - /// variable is diverging, but the main Rust type-checker will - /// sometimes "unify" such variables with the `!` or `()` types. - /// - `origin`: indicates *why* the type variable was created. - /// The code in this module doesn't care, but it can be useful - /// for improving error messages. - pub(crate) fn new_var(&mut self, universe: UniverseIndex, origin: TypeVariableOrigin) -> TyVid { + pub(crate) fn new_var(&mut self, universe: UniverseIndex, span: Span) -> TyVid { let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); - let sub_key = self.sub_unification_table().new_key(()); + let sub_key = self.sub_unification_table().new_key(TypeVariableSubValue { span }); debug_assert_eq!(eq_key.vid, sub_key.vid); - let index = self.storage.values.push(TypeVariableData { origin }); - debug_assert_eq!(eq_key.vid, index); - - debug!("new_var(index={:?}, universe={:?}, origin={:?})", eq_key.vid, universe, origin); + debug!("new_var(index={:?}, universe={:?}, span={:?})", eq_key.vid, universe, span); - index + eq_key.vid } /// Returns the number of type variables created thus far. pub(crate) fn num_vars(&self) -> usize { - self.storage.values.len() + self.storage.eq_relations.len() } /// Returns the "root" variable of `vid` in the `eq_relations` @@ -268,12 +234,9 @@ impl<'db> TypeVariableTable<'_, 'db> { } /// Returns a range of the type variables created during the snapshot. - pub(crate) fn vars_since_snapshot( - &mut self, - value_count: usize, - ) -> (Range<TyVid>, Vec<TypeVariableOrigin>) { + pub(crate) fn vars_since_snapshot(&mut self, value_count: usize) -> (Range<TyVid>, Vec<Span>) { let range = TyVid::from_usize(value_count)..TyVid::from_usize(self.num_vars()); - (range.clone(), iter_idx_range(range).map(|index| self.var_origin(index)).collect()) + (range.clone(), iter_idx_range(range).map(|index| self.var_span(index)).collect()) } /// Returns indices of all variables that are not yet @@ -324,9 +287,6 @@ impl<'db> ut::UnifyKey for TyVidEqKey<'db> { fn tag() -> &'static str { "TyVidEqKey" } - fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> { - if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) } - } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -341,8 +301,13 @@ impl From<TyVid> for TyVidSubKey { } } +#[derive(Debug, Clone, Copy)] +pub(crate) struct TypeVariableSubValue { + span: Span, +} + impl ut::UnifyKey for TyVidSubKey { - type Value = (); + type Value = TypeVariableSubValue; #[inline] fn index(&self) -> u32 { self.vid.as_u32() @@ -356,6 +321,14 @@ impl ut::UnifyKey for TyVidSubKey { } } +impl ut::UnifyValue for TypeVariableSubValue { + type Error = ut::NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> { + Ok(TypeVariableSubValue { span: Span::pick_best(value1.span, value2.span) }) + } +} + impl<'db> ut::UnifyValue for TypeVariableValue<'db> { type Error = ut::NoError; @@ -369,11 +342,9 @@ impl<'db> ut::UnifyValue for TypeVariableValue<'db> { } // If one side is known, prefer that one. - (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => { - Ok(value1.clone()) - } - (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => { - Ok(value2.clone()) + (&TypeVariableValue::Known { value }, &TypeVariableValue::Unknown { .. }) + | (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { value }) => { + Ok(TypeVariableValue::Known { value }) } // If both sides are *unknown*, it hardly matters, does it? diff --git a/crates/hir-ty/src/next_solver/infer/unify_key.rs b/crates/hir-ty/src/next_solver/infer/unify_key.rs index a09f65f082..061b8531d3 100644 --- a/crates/hir-ty/src/next_solver/infer/unify_key.rs +++ b/crates/hir-ty/src/next_solver/infer/unify_key.rs @@ -6,12 +6,15 @@ use std::marker::PhantomData; use ena::unify::{NoError, UnifyKey, UnifyValue}; use rustc_type_ir::{ConstVid, RegionKind, RegionVid, UniverseIndex, inherent::IntoKind}; -use crate::next_solver::{Const, Region}; +use crate::{ + Span, + next_solver::{Const, Region}, +}; -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub(crate) enum RegionVariableValue<'db> { - Known { value: Region<'db> }, - Unknown { universe: UniverseIndex }, + Known { value: Region<'db>, span: Option<Span> }, + Unknown { universe: UniverseIndex, span: Span }, } #[derive(PartialEq, Copy, Clone, Debug)] @@ -51,9 +54,15 @@ impl<'db> UnifyValue for RegionVariableValue<'db> { Err(RegionUnificationError) } - (RegionVariableValue::Known { value }, RegionVariableValue::Unknown { universe }) - | (RegionVariableValue::Unknown { universe }, RegionVariableValue::Known { value }) => { - let universe_of_value = match (*value).kind() { + ( + &RegionVariableValue::Known { value, span: span_known }, + &RegionVariableValue::Unknown { universe, span: span_unknown }, + ) + | ( + &RegionVariableValue::Unknown { universe, span: span_unknown }, + &RegionVariableValue::Known { value, span: span_known }, + ) => { + let universe_of_value = match value.kind() { RegionKind::ReStatic | RegionKind::ReErased | RegionKind::ReLateParam(..) @@ -65,23 +74,28 @@ impl<'db> UnifyValue for RegionVariableValue<'db> { } }; + let span = match span_known { + Some(span_known) => Span::pick_best(span_known, span_unknown), + None => span_unknown, + }; if universe.can_name(universe_of_value) { - Ok(RegionVariableValue::Known { value: *value }) + Ok(RegionVariableValue::Known { value, span: Some(span) }) } else { Err(RegionUnificationError) } } ( - RegionVariableValue::Unknown { universe: a }, - RegionVariableValue::Unknown { universe: b }, + &RegionVariableValue::Unknown { universe: a, span: span1 }, + &RegionVariableValue::Unknown { universe: b, span: span2 }, ) => { // If we unify two unconstrained regions then whatever // value they wind up taking (which must be the same value) must // be nameable by both universes. Therefore, the resulting // universe is the minimum of the two universes, because that is // the one which contains the fewest names in scope. - Ok(RegionVariableValue::Unknown { universe: (*a).min(*b) }) + let span = Span::pick_best(span1, span2); + Ok(RegionVariableValue::Unknown { universe: a.min(b), span }) } } } @@ -89,13 +103,10 @@ impl<'db> UnifyValue for RegionVariableValue<'db> { // Generic consts. -#[derive(Copy, Clone, Debug)] -pub struct ConstVariableOrigin {} - -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub(crate) enum ConstVariableValue<'db> { Known { value: Const<'db> }, - Unknown { origin: ConstVariableOrigin, universe: UniverseIndex }, + Unknown { span: Span, universe: UniverseIndex }, } impl<'db> ConstVariableValue<'db> { @@ -134,9 +145,6 @@ impl<'db> UnifyKey for ConstVidKey<'db> { fn tag() -> &'static str { "ConstVidKey" } - fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> { - if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) } - } } impl<'db> UnifyValue for ConstVariableValue<'db> { @@ -149,25 +157,22 @@ impl<'db> UnifyValue for ConstVariableValue<'db> { } // If one side is known, prefer that one. - (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => { - Ok(value1.clone()) - } - (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => { - Ok(value2.clone()) - } + (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => Ok(*value1), + (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => Ok(*value2), // If both sides are *unknown*, it hardly matters, does it? ( - ConstVariableValue::Unknown { origin, universe: universe1 }, - ConstVariableValue::Unknown { origin: _, universe: universe2 }, + &ConstVariableValue::Unknown { span: span1, universe: universe1 }, + &ConstVariableValue::Unknown { span: span2, universe: universe2 }, ) => { // If we unify two unbound variables, ?T and ?U, then whatever // value they wind up taking (which must be the same value) must // be nameable by both universes. Therefore, the resulting // universe is the minimum of the two universes, because that is // the one which contains the fewest names in scope. - let universe = cmp::min(*universe1, *universe2); - Ok(ConstVariableValue::Unknown { origin: *origin, universe }) + let universe = cmp::min(universe1, universe2); + let span = Span::pick_best(span1, span2); + Ok(ConstVariableValue::Unknown { span, universe }) } } } diff --git a/crates/hir-ty/src/next_solver/inspect.rs b/crates/hir-ty/src/next_solver/inspect.rs index 63a225b98f..7e2dfb7112 100644 --- a/crates/hir-ty/src/next_solver/inspect.rs +++ b/crates/hir-ty/src/next_solver/inspect.rs @@ -6,21 +6,26 @@ use rustc_next_trait_solver::{ }; use rustc_type_ir::{ VisitorResult, - inherent::{IntoKind, Span as _}, - solve::{Certainty, GoalSource, MaybeCause, NoSolution}, + inherent::IntoKind, + solve::{Certainty, GoalSource, MaybeCause, MaybeInfo, NoSolution}, }; -use crate::next_solver::{ - DbInterner, GenericArg, GenericArgs, Goal, NormalizesTo, ParamEnv, Predicate, PredicateKind, - QueryResult, SolverContext, Span, Term, - fulfill::NextSolverError, - infer::{ - InferCtxt, - traits::{Obligation, ObligationCause}, +use crate::{ + Span, + next_solver::{ + DbInterner, GenericArg, GenericArgs, Goal, NormalizesTo, ParamEnv, Predicate, + PredicateKind, QueryResult, SolverContext, Term, + fulfill::NextSolverError, + infer::{ + InferCtxt, + traits::{Obligation, ObligationCause}, + }, + obligation_ctxt::ObligationCtxt, }, - obligation_ctxt::ObligationCtxt, }; +pub(crate) use rustc_next_trait_solver::solve::inspect::*; + pub(crate) struct InspectConfig { pub(crate) max_depth: usize, } @@ -142,7 +147,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { &self, visitor: &mut V, ) -> V::Result { - for goal in self.instantiate_nested_goals() { + for goal in self.instantiate_nested_goals(visitor.span()) { try_visit!(goal.visit_with(visitor)); } @@ -153,7 +158,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { /// inference constraints. This function modifies the state of the `infcx`. /// /// See [`Self::instantiate_impl_args`] if you need the impl args too. - pub(crate) fn instantiate_nested_goals(&self) -> Vec<InspectGoal<'a, 'db>> { + pub(crate) fn instantiate_nested_goals(&self, span: Span) -> Vec<InspectGoal<'a, 'db>> { let infcx = self.goal.infcx; let param_env = self.goal.goal.param_env; let mut orig_values = self.goal.orig_values.to_vec(); @@ -163,13 +168,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { match **step { inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push(( source, - instantiate_canonical_state( - infcx, - Span::dummy(), - param_env, - &mut orig_values, - goal, - ), + instantiate_canonical_state(infcx, span, param_env, &mut orig_values, goal), )), inspect::ProbeStep::RecordImplArgs { .. } => {} inspect::ProbeStep::MakeCanonicalResponse { .. } @@ -177,13 +176,8 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { } } - let () = instantiate_canonical_state( - infcx, - Span::dummy(), - param_env, - &mut orig_values, - self.final_state, - ); + let () = + instantiate_canonical_state(infcx, span, param_env, &mut orig_values, self.final_state); if let Some(term_hack) = &self.goal.normalizes_to_term_hack { // FIXME: We ignore the expected term of `NormalizesTo` goals @@ -194,14 +188,14 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { instantiated_goals .into_iter() - .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal)) + .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span)) .collect() } /// Instantiate the args of an impl if this candidate came from a /// `CandidateSource::Impl`. This function modifies the state of the /// `infcx`. - pub(crate) fn instantiate_impl_args(&self) -> GenericArgs<'db> { + pub(crate) fn instantiate_impl_args(&self, span: Span) -> GenericArgs<'db> { let infcx = self.goal.infcx; let param_env = self.goal.goal.param_env; let mut orig_values = self.goal.orig_values.to_vec(); @@ -211,7 +205,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { inspect::ProbeStep::RecordImplArgs { impl_args } => { let impl_args = instantiate_canonical_state( infcx, - Span::dummy(), + span, param_env, &mut orig_values, impl_args, @@ -219,7 +213,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { let () = instantiate_canonical_state( infcx, - Span::dummy(), + span, param_env, &mut orig_values, self.final_state, @@ -246,11 +240,12 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { &self, source: GoalSource, goal: Goal<'db, Predicate<'db>>, + span: Span, ) -> InspectGoal<'a, 'db> { let infcx = self.goal.infcx; match goal.predicate.kind().no_bound_vars() { Some(PredicateKind::NormalizesTo(NormalizesTo { alias, term })) => { - let unconstrained_term = infcx.next_term_var_of_kind(term); + let unconstrained_term = infcx.next_term_var_of_kind(term, span); let goal = goal.with(infcx.interner, NormalizesTo { alias, term: unconstrained_term }); // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the @@ -265,8 +260,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { // considering the constrained RHS, and pass the resulting certainty to // `InspectGoal::new` so that the goal has the right result (and maintains // the impression that we don't do this normalizes-to infer hack at all). - let (nested, proof_tree) = - infcx.evaluate_root_goal_for_proof_tree(goal, Span::dummy()); + let (nested, proof_tree) = infcx.evaluate_root_goal_for_proof_tree(goal, span); let nested_goals_result = nested.and_then(|nested| { normalizes_to_term_hack.constrain_and( infcx, @@ -300,7 +294,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> { // constraints, we get an ICE if we already applied the constraints // from the chosen candidate. let proof_tree = - infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1); + infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, span).1); InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source) } } @@ -327,6 +321,10 @@ impl<'a, 'db> InspectGoal<'a, 'db> { self.result } + pub(crate) fn source(&self) -> GoalSource { + self.source + } + pub(crate) fn depth(&self) -> usize { self.depth } @@ -346,7 +344,10 @@ impl<'a, 'db> InspectGoal<'a, 'db> { inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { assert!(matches!( shallow_certainty.replace(c), - None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }) + None | Some(Certainty::Maybe(MaybeInfo { + cause: MaybeCause::Ambiguity, + .. + })) )); } inspect::ProbeStep::NestedProbe(ref probe) => { @@ -469,6 +470,8 @@ impl<'a, 'db> InspectGoal<'a, 'db> { pub(crate) trait ProofTreeVisitor<'db> { type Result: VisitorResult; + fn span(&self) -> Span; + fn config(&self) -> InspectConfig { InspectConfig { max_depth: 10 } } @@ -496,7 +499,7 @@ impl<'db> InferCtxt<'db> { visitor: &mut V, ) -> V::Result { let (_, proof_tree) = <&SolverContext<'db>>::from(self) - .evaluate_root_goal_for_proof_tree(goal, Span::dummy()); + .evaluate_root_goal_for_proof_tree(goal, visitor.span()); visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc)) } } diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs index cfb55e2e00..b3d31dd40b 100644 --- a/crates/hir-ty/src/next_solver/interner.rs +++ b/crates/hir-ty/src/next_solver/interner.rs @@ -4,49 +4,53 @@ use std::{fmt, ops::ControlFlow}; use intern::{Interned, InternedRef, InternedSliceRef, impl_internable}; use macros::GenericTypeVisitable; +use rustc_abi::ReprOptions; use rustc_ast_ir::{FloatTy, IntTy, UintTy}; pub use tls_cache::clear_tls_solver_cache; pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db}; use base_db::Crate; use hir_def::{ - AdtId, CallableDefId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, HasModule, - ItemContainerId, StructId, UnionId, VariantId, + AdtId, CallableDefId, EnumId, HasModule, ItemContainerId, StructId, TraitId, TypeAliasId, + UnionId, VariantId, attrs::AttrFlags, - expr_store::{Body, ExpressionStore}, + expr_store::{ExpressionStore, StoreVisitor}, + hir::{ClosureKind as HirClosureKind, CoroutineKind as HirCoroutineKind, ExprId, PatId}, lang_item::LangItems, signatures::{ - EnumSignature, FieldData, FnFlags, FunctionSignature, ImplFlags, ImplSignature, + EnumFlags, EnumSignature, FnFlags, FunctionSignature, ImplFlags, ImplSignature, StructFlags, StructSignature, TraitFlags, TraitSignature, UnionSignature, }, }; -use la_arena::Idx; -use rustc_abi::{ReprFlags, ReprOptions}; +use rustc_abi::ExternAbi; use rustc_hash::FxHashSet; use rustc_index::bit_set::DenseBitSet; use rustc_type_ir::{ - AliasTermKind, AliasTy, AliasTyKind, BoundVar, CoroutineWitnessTypes, DebruijnIndex, - EarlyBinder, FlagComputation, Flags, GenericArgKind, GenericTypeVisitable, ImplPolarity, - InferTy, Interner, TraitRef, TypeFlags, TypeVisitableExt, Upcast, Variance, + AliasTy, BoundVar, CoroutineWitnessTypes, DebruijnIndex, EarlyBinder, FlagComputation, Flags, + FnSigKind, GenericArgKind, GenericTypeVisitable, ImplPolarity, InferTy, Interner, TraitRef, + TypeFlags, TypeVisitableExt, Upcast, Variance, elaborate::elaborate, error::TypeError, fast_reject, inherent::{self, Const as _, GenericsOf, IntoKind, SliceLike as _, Span as _, Ty as _}, - lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}, + lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}, solve::{AdtDestructorKind, SizedTraitKind}, }; use crate::{ - FnAbi, + InferBodyId, Span, db::{HirDatabase, InternedClosure, InternedCoroutineId}, lower::GenericPredicates, method_resolution::TraitImpls, next_solver::{ - AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, - Consts, CoroutineClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, - GeneralConstIdWrapper, LateParamRegion, OpaqueTypeKey, RegionAssumptions, ScalarInt, - SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, - UnevaluatedConst, + AdtIdWrapper, AliasTermKind, AliasTyKind, AnyImplId, BoundConst, CallableIdWrapper, + CanonicalVarKind, ClosureIdWrapper, Consts, CoroutineClosureIdWrapper, CoroutineIdWrapper, + Ctor, FnSig, FreeConstAliasId, FreeTermAliasId, FreeTyAliasId, FxIndexMap, + GeneralConstIdWrapper, ImplOrTraitAssocConstId, ImplOrTraitAssocTermId, + ImplOrTraitAssocTyId, InherentAssocConstId, InherentAssocTermId, InherentAssocTyId, + LateParamRegion, OpaqueTyIdWrapper, OpaqueTypeKey, RegionAssumptions, ScalarInt, + SimplifiedType, SolverContext, SolverDefIds, TermId, TraitAssocConstId, TraitAssocTermId, + TraitAssocTyId, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, Unnormalized, util::{explicit_item_bounds, explicit_item_self_bounds}, }, }; @@ -382,13 +386,9 @@ impl<'db> DbInterner<'db> { } } -// This is intentionally left as `()` -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub struct Span(()); - impl<'db> inherent::Span<DbInterner<'db>> for Span { fn dummy() -> Self { - Span(()) + Span::Dummy } } @@ -422,266 +422,167 @@ pub struct AllocId; interned_slice!(VariancesOfStorage, VariancesOf, StoredVariancesOf, variances, Variance, Variance); -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct VariantIdx(usize); - -// FIXME: could/should store actual data? -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub enum VariantDef { - Struct(StructId), - Union(UnionId), - Enum(EnumVariantId), -} - -impl VariantDef { - pub fn id(&self) -> VariantId { - match self { - VariantDef::Struct(struct_id) => VariantId::StructId(*struct_id), - VariantDef::Union(union_id) => VariantId::UnionId(*union_id), - VariantDef::Enum(enum_variant_id) => VariantId::EnumVariantId(*enum_variant_id), - } - } - - pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Idx<FieldData>, FieldData)> { - let id: VariantId = match self { - VariantDef::Struct(it) => (*it).into(), - VariantDef::Union(it) => (*it).into(), - VariantDef::Enum(it) => (*it).into(), - }; - id.fields(db).fields().iter().map(|(id, data)| (id, data.clone())).collect() +bitflags::bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + struct AdtFlags: u8 { + const IS_FUNDAMENTAL = 1 << 0; + const IS_PACKED = 1 << 1; + const HAS_REPR = 1 << 2; + const IS_PHANTOM_DATA = 1 << 3; + const IS_MANUALLY_DROP = 1 << 4; + const IS_BOX = 1 << 5; } } -/* -/// Definition of a variant -- a struct's fields or an enum variant. -#[derive(Debug, StableHash, TyEncodable, TyDecodable)] -pub struct VariantDef { - /// `DefId` that identifies the variant itself. - /// If this variant belongs to a struct or union, then this is a copy of its `DefId`. - pub def_id: DefId, - /// `DefId` that identifies the variant's constructor. - /// If this variant is a struct variant, then this is `None`. - pub ctor: Option<(CtorKind, DefId)>, - /// Variant or struct name, maybe empty for anonymous adt (struct or union). - pub name: Symbol, - /// Discriminant of this variant. - pub discr: VariantDiscr, - /// Fields of this variant. - pub fields: IndexVec<FieldIdx, FieldDef>, - /// The error guarantees from parser, if any. - tainted: Option<ErrorGuaranteed>, - /// Flags of the variant (e.g. is field list non-exhaustive)? - flags: VariantFlags, +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum AdtDefInner { + Struct { id: StructId, flags: AdtFlags }, + Union { id: UnionId, flags: AdtFlags }, + Enum { id: EnumId, flags: AdtFlags }, } -*/ -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct AdtFlags { - is_enum: bool, - is_union: bool, - is_struct: bool, - is_phantom_data: bool, - is_fundamental: bool, - is_box: bool, - is_manually_drop: bool, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AdtDefInner { - pub id: AdtId, - variants: Vec<(VariantIdx, VariantDef)>, - flags: AdtFlags, - repr: ReprOptions, -} - -// We're gonna cheat a little bit and implement `Hash` on only the `DefId` and -// accept there might be collisions for def ids from different crates (or across -// different tests, oh my). -impl std::hash::Hash for AdtDefInner { - #[inline] - fn hash<H: std::hash::Hasher>(&self, s: &mut H) { - self.id.hash(s) - } -} +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct AdtDef(AdtDefInner); -#[salsa::interned(no_lifetime, constructor = new_)] -pub struct AdtDef { - #[returns(ref)] - data_: AdtDefInner, -} +const _: () = assert!(size_of::<AdtDef>() == 12); impl AdtDef { pub fn new<'db>(def_id: AdtId, interner: DbInterner<'db>) -> Self { let db = interner.db(); - let (flags, variants, repr) = match def_id { - AdtId::StructId(struct_id) => { - let data = StructSignature::of(db, struct_id); - - let flags = AdtFlags { - is_enum: false, - is_union: false, - is_struct: true, - is_phantom_data: data.flags.contains(StructFlags::IS_PHANTOM_DATA), - is_fundamental: data.flags.contains(StructFlags::FUNDAMENTAL), - is_box: data.flags.contains(StructFlags::IS_BOX), - is_manually_drop: data.flags.contains(StructFlags::IS_MANUALLY_DROP), - }; - - let variants = vec![(VariantIdx(0), VariantDef::Struct(struct_id))]; - - let data_repr = data.repr(db, struct_id); - let mut repr_flags = ReprFlags::empty(); - if flags.is_box { - repr_flags.insert(ReprFlags::IS_LINEAR); + let inner = match def_id { + AdtId::StructId(id) => { + let data = StructSignature::of(db, id); + let mut flags = AdtFlags::empty(); + if data.flags.contains(StructFlags::FUNDAMENTAL) { + flags.insert(AdtFlags::IS_FUNDAMENTAL); } - if data_repr.is_some_and(|r| r.c()) { - repr_flags.insert(ReprFlags::IS_C); + if data.flags.contains(StructFlags::IS_PHANTOM_DATA) { + flags.insert(AdtFlags::IS_PHANTOM_DATA); } - if data_repr.is_some_and(|r| r.simd()) { - repr_flags.insert(ReprFlags::IS_SIMD); + if data.flags.contains(StructFlags::IS_MANUALLY_DROP) { + flags.insert(AdtFlags::IS_MANUALLY_DROP); } - let repr = ReprOptions { - align: data_repr.and_then(|r| r.align), - pack: data_repr.and_then(|r| r.pack), - int: data_repr.and_then(|r| r.int), - flags: repr_flags, - ..ReprOptions::default() - }; - - (flags, variants, repr) - } - AdtId::UnionId(union_id) => { - let flags = AdtFlags { - is_enum: false, - is_union: true, - is_struct: false, - is_phantom_data: false, - is_fundamental: false, - is_box: false, - is_manually_drop: false, - }; - - let variants = vec![(VariantIdx(0), VariantDef::Union(union_id))]; - - let data_repr = AttrFlags::repr(db, union_id.into()); - let mut repr_flags = ReprFlags::empty(); - if flags.is_box { - repr_flags.insert(ReprFlags::IS_LINEAR); + if data.flags.contains(StructFlags::IS_BOX) { + flags.insert(AdtFlags::IS_BOX); } - if data_repr.is_some_and(|r| r.c()) { - repr_flags.insert(ReprFlags::IS_C); + if data.flags.contains(StructFlags::HAS_REPR) { + flags.insert(AdtFlags::HAS_REPR); + if data.repr(db, id).is_some_and(|repr| repr.packed()) { + flags.insert(AdtFlags::IS_PACKED); + } } - if data_repr.is_some_and(|r| r.simd()) { - repr_flags.insert(ReprFlags::IS_SIMD); + AdtDefInner::Struct { id, flags } + } + AdtId::UnionId(id) => { + let data = UnionSignature::of(db, id); + let mut flags = AdtFlags::empty(); + if data.flags.contains(StructFlags::FUNDAMENTAL) { + flags.insert(AdtFlags::IS_FUNDAMENTAL); } - let repr = ReprOptions { - align: data_repr.and_then(|r| r.align), - pack: data_repr.and_then(|r| r.pack), - int: data_repr.and_then(|r| r.int), - flags: repr_flags, - ..ReprOptions::default() - }; - - (flags, variants, repr) - } - AdtId::EnumId(enum_id) => { - let flags = AdtFlags { - is_enum: true, - is_union: false, - is_struct: false, - is_phantom_data: false, - is_fundamental: false, - is_box: false, - is_manually_drop: false, - }; - - let variants = enum_id - .enum_variants(db) - .variants - .iter() - .enumerate() - .map(|(idx, v)| (VariantIdx(idx), v)) - .map(|(idx, v)| (idx, VariantDef::Enum(v.0))) - .collect(); - - let data_repr = AttrFlags::repr(db, enum_id.into()); - - let mut repr_flags = ReprFlags::empty(); - if flags.is_box { - repr_flags.insert(ReprFlags::IS_LINEAR); + if data.flags.contains(StructFlags::HAS_REPR) { + flags.insert(AdtFlags::HAS_REPR); + if data.repr(db, id).is_some_and(|repr| repr.packed()) { + flags.insert(AdtFlags::IS_PACKED); + } } - if data_repr.is_some_and(|r| r.c()) { - repr_flags.insert(ReprFlags::IS_C); + AdtDefInner::Union { id, flags } + } + AdtId::EnumId(id) => { + let data = EnumSignature::of(db, id); + let mut flags = AdtFlags::empty(); + if data.flags.contains(EnumFlags::FUNDAMENTAL) { + flags.insert(AdtFlags::IS_FUNDAMENTAL); } - if data_repr.is_some_and(|r| r.simd()) { - repr_flags.insert(ReprFlags::IS_SIMD); + if data.flags.contains(EnumFlags::HAS_REPR) { + flags.insert(AdtFlags::HAS_REPR); + if data.repr(db, id).is_some_and(|repr| repr.packed()) { + flags.insert(AdtFlags::IS_PACKED); + } } - - let repr = ReprOptions { - align: data_repr.and_then(|r| r.align), - pack: data_repr.and_then(|r| r.pack), - int: data_repr.and_then(|r| r.int), - flags: repr_flags, - ..ReprOptions::default() - }; - - (flags, variants, repr) + AdtDefInner::Enum { id, flags } } }; + AdtDef(inner) + } - AdtDef::new_(db, AdtDefInner { id: def_id, variants, flags, repr }) + #[inline] + pub fn def_id(self) -> AdtId { + match self.0 { + AdtDefInner::Struct { id, .. } => AdtId::StructId(id), + AdtDefInner::Union { id, .. } => AdtId::UnionId(id), + AdtDefInner::Enum { id, .. } => AdtId::EnumId(id), + } } - pub fn inner(&self) -> &AdtDefInner { - crate::with_attached_db(|db| { - let inner = self.data_(db); - // SAFETY: ¯\_(ツ)_/¯ - unsafe { std::mem::transmute(inner) } - }) + #[inline] + fn flags(self) -> AdtFlags { + match self.0 { + AdtDefInner::Struct { flags, .. } + | AdtDefInner::Union { flags, .. } + | AdtDefInner::Enum { flags, .. } => flags, + } } - pub fn is_enum(&self) -> bool { - self.inner().flags.is_enum + #[inline] + pub fn is_struct(self) -> bool { + matches!(self.0, AdtDefInner::Struct { .. }) } - pub fn is_box(&self) -> bool { - self.inner().flags.is_box + #[inline] + pub fn is_union(self) -> bool { + matches!(self.0, AdtDefInner::Union { .. }) + } + + #[inline] + pub fn is_enum(self) -> bool { + matches!(self.0, AdtDefInner::Enum { .. }) } #[inline] - pub fn repr(self) -> ReprOptions { - self.inner().repr + pub fn is_box(self) -> bool { + matches!(self.0, AdtDefInner::Struct { flags, .. } if flags.contains(AdtFlags::IS_BOX)) } - /// Asserts this is a struct or union and returns its unique variant. - pub fn non_enum_variant(self) -> VariantDef { - assert!(self.inner().flags.is_struct || self.inner().flags.is_union); - self.inner().variants[0].1.clone() + #[inline] + pub fn repr(self, db: &dyn HirDatabase) -> ReprOptions { + if self.flags().contains(AdtFlags::HAS_REPR) { + AttrFlags::repr_assume_has(db, self.def_id()).unwrap_or_default() + } else { + ReprOptions::default() + } } } impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef { fn def_id(self) -> AdtIdWrapper { - self.inner().id.into() + self.def_id().into() } fn is_struct(self) -> bool { - self.inner().flags.is_struct + self.is_struct() } fn is_phantom_data(self) -> bool { - self.inner().flags.is_phantom_data + matches!(self.0, AdtDefInner::Struct { flags, .. } if flags.contains(AdtFlags::IS_PHANTOM_DATA)) + } + + fn is_manually_drop(self) -> bool { + matches!(self.0, AdtDefInner::Struct { flags, .. } if flags.contains(AdtFlags::IS_MANUALLY_DROP)) + } + + fn is_packed(self) -> bool { + self.flags().contains(AdtFlags::IS_PACKED) } fn is_fundamental(self) -> bool { - self.inner().flags.is_fundamental + self.flags().contains(AdtFlags::IS_FUNDAMENTAL) } fn struct_tail_ty( self, interner: DbInterner<'db>, ) -> Option<EarlyBinder<DbInterner<'db>, Ty<'db>>> { - let hir_def::AdtId::StructId(struct_id) = self.inner().id else { + let hir_def::AdtId::StructId(struct_id) = self.def_id() else { return None; }; let id: VariantId = struct_id.into(); @@ -700,7 +601,7 @@ impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef { db.field_types(id).iter().map(|(_, ty)| ty.get().skip_binder()).collect::<Vec<_>>() }; let field_tys = |_id: VariantId| vec![]; - let tys: Vec<_> = match self.inner().id { + let tys: Vec<_> = match self.def_id() { hir_def::AdtId::StructId(id) => field_tys(id.into()), hir_def::AdtId::UnionId(id) => field_tys(id.into()), hir_def::AdtId::EnumId(id) => id @@ -726,15 +627,7 @@ impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef { } fn destructor(self, interner: DbInterner<'db>) -> Option<AdtDestructorKind> { - crate::drop::destructor(interner.db, self.def_id().0).map(|_| AdtDestructorKind::NotConst) - } - - fn is_manually_drop(self) -> bool { - self.inner().flags.is_manually_drop - } - - fn is_packed(self) -> bool { - self.repr().packed() + crate::drop::destructor(interner.db, self.def_id()).map(|_| AdtDestructorKind::NotConst) } fn field_representing_type_info( @@ -749,17 +642,17 @@ impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef { impl fmt::Debug for AdtDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - crate::with_attached_db(|db| match self.inner().id { - AdtId::StructId(struct_id) => { - let data = StructSignature::of(db, struct_id); + crate::with_attached_db(|db| match self.0 { + AdtDefInner::Struct { id, .. } => { + let data = StructSignature::of(db, id); f.write_str(data.name.as_str()) } - AdtId::UnionId(union_id) => { - let data = UnionSignature::of(db, union_id); + AdtDefInner::Union { id, .. } => { + let data = UnionSignature::of(db, id); f.write_str(data.name.as_str()) } - AdtId::EnumId(enum_id) => { - let data = EnumSignature::of(db, enum_id); + AdtDefInner::Enum { id, .. } => { + let data = EnumSignature::of(db, id); f.write_str(data.name.as_str()) } }) @@ -778,6 +671,10 @@ impl<'db> inherent::Features<DbInterner<'db>> for Features { false } + fn generic_const_args(self) -> bool { + false + } + fn feature_bound_holds_in_crate(self, _symbol: Symbol) -> bool { false } @@ -949,11 +846,7 @@ impl_foldable_for_interned_slice!(PatList); macro_rules! as_lang_item { ( - $solver_enum:ident, $self:ident, $def_id:expr; - - ignore = { - $( $ignore:ident ),* $(,)? - } + $solver_enum:ident, $self:ident, $def_id:expr, $id_ty:ty; $( $variant:ident ),* $(,)? ) => {{ @@ -962,11 +855,10 @@ macro_rules! as_lang_item { if let Some(it) = None::<$solver_enum> { match it { $( $solver_enum::$variant => {} )* - $( $solver_enum::$ignore => {} )* } } match $def_id { - $( def_id if lang_items.$variant.is_some_and(|it| it == def_id) => Some($solver_enum::$variant), )* + $( def_id if let Some(it) = lang_items.$variant && <$id_ty>::from(it) == def_id => Some($solver_enum::$variant), )* _ => None } }}; @@ -976,17 +868,12 @@ macro_rules! is_lang_item { ( $solver_enum:ident, $self:ident, $def_id:expr, $expected_variant:ident; - ignore = { - $( $ignore:ident ),* $(,)? - } - $( $variant:ident ),* $(,)? ) => {{ let lang_items = $self.lang_items(); let def_id = $def_id; match $expected_variant { $( $solver_enum::$variant => lang_items.$variant.is_some_and(|it| it == def_id), )* - $( $solver_enum::$ignore => false, )* } }}; } @@ -1004,6 +891,20 @@ impl<'db> Interner for DbInterner<'db> { type AdtId = AdtIdWrapper; type ImplId = AnyImplId; type UnevaluatedConstId = GeneralConstIdWrapper; + type TraitAssocTyId = TraitAssocTyId; + type TraitAssocConstId = TraitAssocConstId; + type TraitAssocTermId = TraitAssocTermId; + type OpaqueTyId = OpaqueTyIdWrapper; + type LocalOpaqueTyId = OpaqueTyIdWrapper; + type FreeTyAliasId = FreeTyAliasId; + type FreeConstAliasId = FreeConstAliasId; + type FreeTermAliasId = FreeTermAliasId; + type ImplOrTraitAssocTyId = ImplOrTraitAssocTyId; + type ImplOrTraitAssocConstId = ImplOrTraitAssocConstId; + type ImplOrTraitAssocTermId = ImplOrTraitAssocTermId; + type InherentAssocTyId = InherentAssocTyId; + type InherentAssocConstId = InherentAssocConstId; + type InherentAssocTermId = InherentAssocTermId; type Span = Span; type GenericArgs = GenericArgs<'db>; @@ -1057,7 +958,6 @@ impl<'db> Interner for DbInterner<'db> { type Pat = Pattern<'db>; type PatList = PatList<'db>; type Safety = Safety; - type Abi = FnAbi; type Const = Const<'db>; type ParamConst = ParamConst; @@ -1078,7 +978,7 @@ impl<'db> Interner for DbInterner<'db> { type Clause = Clause<'db>; type Clauses = Clauses<'db>; - type GenericsOf = Generics; + type GenericsOf = Generics<'db>; type VariancesOf = VariancesOf<'db>; @@ -1187,7 +1087,9 @@ impl<'db> Interner for DbInterner<'db> { // // We currently always use the type from HIR typeck which ignores regions. This // should be fine. - SolverDefId::InternedOpaqueTyId(_) => self.type_of_opaque_hir_typeck(def_id), + SolverDefId::InternedOpaqueTyId(def_id) => { + self.type_of_opaque_hir_typeck(def_id.into()) + } SolverDefId::FunctionId(id) => self.db.value_ty(id.into()).unwrap(), SolverDefId::Ctor(id) => { let id = match id { @@ -1204,43 +1106,45 @@ impl<'db> Interner for DbInterner<'db> { AdtDef::new(def_id.0, self) } - fn alias_term_kind( - self, - alias: rustc_type_ir::AliasTerm<Self>, - ) -> rustc_type_ir::AliasTermKind { - match alias.def_id { - SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy, + fn alias_term_kind_from_def_id(self, def_id: SolverDefId) -> AliasTermKind<'db> { + match def_id { + SolverDefId::InternedOpaqueTyId(def_id) => { + AliasTermKind::OpaqueTy { def_id: def_id.into() } + } SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container { ItemContainerId::ImplId(impl_) if ImplSignature::of(self.db, impl_).target_trait.is_none() => { - AliasTermKind::InherentTy + AliasTermKind::InherentTy { def_id: type_alias.into() } } ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => { - AliasTermKind::ProjectionTy + AliasTermKind::ProjectionTy { def_id: type_alias.into() } } - _ => AliasTermKind::FreeTy, + _ => AliasTermKind::FreeTy { def_id: type_alias.into() }, }, // rustc creates an `AnonConst` for consts, and evaluates them with CTFE (normalizing projections // via selection, similar to ours `find_matching_impl()`, and not with the trait solver), so mimic it. - SolverDefId::ConstId(_) | SolverDefId::AnonConstId(_) => { - AliasTermKind::UnevaluatedConst + SolverDefId::ConstId(def_id) => { + AliasTermKind::UnevaluatedConst { def_id: GeneralConstIdWrapper(def_id.into()) } + } + SolverDefId::AnonConstId(def_id) => { + AliasTermKind::UnevaluatedConst { def_id: GeneralConstIdWrapper(def_id.into()) } } - _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), + _ => unimplemented!("Unexpected alias: {:?}", def_id), } } fn trait_ref_and_own_args_for_alias( self, - def_id: Self::DefId, + def_id: Self::TraitAssocTermId, args: Self::GenericArgs, ) -> (rustc_type_ir::TraitRef<Self>, Self::GenericArgsSlice) { - let trait_def_id = self.parent(def_id); - let trait_generics = self.generics_of(trait_def_id); - let trait_args = - GenericArgs::new_from_slice(&args.as_slice()[0..trait_generics.own_params.len()]); - let alias_args = &args.as_slice()[trait_generics.own_params.len()..]; - (TraitRef::new_from_args(self, trait_def_id.try_into().unwrap(), trait_args), alias_args) + let trait_def_id = self.projection_parent(def_id).0; + let trait_generics = crate::generics::generics(self.db, trait_def_id.into()); + let trait_generics_len = trait_generics.len(); + let trait_args = GenericArgs::new_from_slice(&args.as_slice()[..trait_generics_len]); + let alias_args = &args.as_slice()[trait_generics_len..]; + (TraitRef::new_from_args(self, trait_def_id.into(), trait_args), alias_args) } fn check_args_compatible(self, _def_id: Self::DefId, _args: Self::GenericArgs) -> bool { @@ -1265,37 +1169,43 @@ impl<'db> Interner for DbInterner<'db> { Tys::new_from_iter(self, args) } - fn parent(self, def_id: Self::DefId) -> Self::DefId { - use hir_def::Lookup; + fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId { + let container = match def_id.0 { + TermId::TypeAliasId(def_id) => def_id.loc(self.db).container, + TermId::ConstId(def_id) => def_id.loc(self.db).container, + }; + let ItemContainerId::TraitId(trait_) = container else { + panic!("a TraitAssocTermId can only come from a trait") + }; + trait_.into() + } - let container = match def_id { - SolverDefId::FunctionId(it) => it.lookup(self.db()).container, - SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container, - SolverDefId::ConstId(it) => it.lookup(self.db()).container, - SolverDefId::InternedClosureId(it) => { - return it.loc(self.db).0.generic_def(self.db()).into(); - } - SolverDefId::InternedCoroutineId(it) => { - return it.loc(self.db).0.generic_def(self.db()).into(); - } - SolverDefId::InternedCoroutineClosureId(it) => { - return it.loc(self.db).0.generic_def(self.db()).into(); - } - SolverDefId::StaticId(_) - | SolverDefId::AdtId(_) - | SolverDefId::TraitId(_) - | SolverDefId::ImplId(_) - | SolverDefId::BuiltinDeriveImplId(_) - | SolverDefId::EnumVariantId(..) - | SolverDefId::Ctor(..) - | SolverDefId::InternedOpaqueTyId(..) - | SolverDefId::AnonConstId(_) => panic!(), + fn impl_or_trait_assoc_term_parent(self, def_id: Self::ImplOrTraitAssocTermId) -> Self::DefId { + let container = match def_id.0 { + TermId::TypeAliasId(def_id) => def_id.loc(self.db).container, + TermId::ConstId(def_id) => def_id.loc(self.db).container, }; + match container { + ItemContainerId::ImplId(impl_) => impl_.into(), + ItemContainerId::TraitId(trait_) => trait_.into(), + ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => { + panic!("only impl or trait can be the parent of ImplOrTraitAssocTermId") + } + } + } + fn inherent_alias_term_parent(self, def_id: Self::InherentAssocTermId) -> Self::ImplId { + let container = match def_id.0 { + TermId::TypeAliasId(def_id) => def_id.loc(self.db).container, + TermId::ConstId(def_id) => def_id.loc(self.db).container, + }; match container { - ItemContainerId::ImplId(it) => it.into(), - ItemContainerId::TraitId(it) => it.into(), - ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => panic!(), + ItemContainerId::ImplId(impl_) => impl_.into(), + ItemContainerId::ExternBlockId(_) + | ItemContainerId::ModuleId(_) + | ItemContainerId::TraitId(_) => { + panic!("only impl can be the parent of InherentAliasTermId") + } } } @@ -1303,6 +1213,10 @@ impl<'db> Interner for DbInterner<'db> { 50 } + fn is_type_const(self, _def_id: Self::DefId) -> bool { + false + } + fn features(self) -> Features { Features } @@ -1315,28 +1229,35 @@ impl<'db> Interner for DbInterner<'db> { } fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability { - // FIXME: Make this a query? I don't believe this can be accessed from bodies other than - // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); - let store = ExpressionStore::of(self.db, owner); - let expr = &store[expr_id]; - match *expr { - hir_def::hir::Expr::Closure { closure_kind, .. } => match closure_kind { - hir_def::hir::ClosureKind::Coroutine(movability) => match movability { - hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static, - hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable, - }, - hir_def::hir::ClosureKind::AsyncBlock { .. } => rustc_ast_ir::Movability::Static, - _ => panic!("unexpected expression for a coroutine: {expr:?}"), + match def_id.0.loc(self.db).kind { + hir_def::hir::ClosureKind::OldCoroutine(movability) => match movability { + hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static, + hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable, }, - _ => panic!("unexpected expression for a coroutine: {expr:?}"), + hir_def::hir::ClosureKind::Coroutine { .. } => rustc_ast_ir::Movability::Static, + kind => panic!("unexpected kind for a coroutine: {kind:?}"), } } fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId { - let InternedClosure(owner, coroutine_closure_expr) = def_id.0.loc(self.db); + let InternedClosure { owner, expr: coroutine_closure_expr, kind: coroutine_closure_kind } = + def_id.0.loc(self.db); + let coroutine_closure_kind = match coroutine_closure_kind { + HirClosureKind::CoroutineClosure(it) => it, + _ => { + panic!("invalid kind closure kind {coroutine_closure_kind:?} for coroutine closure") + } + }; let coroutine_expr = ExpressionStore::coroutine_for_closure(coroutine_closure_expr); - InternedCoroutineId::new(self.db, InternedClosure(owner, coroutine_expr)).into() + let coroutine_kind = hir_def::hir::ClosureKind::Coroutine { + kind: coroutine_closure_kind, + source: hir_def::hir::CoroutineSource::Closure, + }; + InternedCoroutineId::new( + self.db, + InternedClosure { owner, expr: coroutine_expr, kind: coroutine_kind }, + ) + .into() } fn generics_require_sized_self(self, def_id: Self::DefId) -> bool { @@ -1348,22 +1269,24 @@ impl<'db> Interner for DbInterner<'db> { // Search for a predicate like `Self : Sized` amongst the trait bounds. let predicates = self.predicates_of(def_id); - elaborate(self, predicates.iter_identity()).any(|pred| match pred.kind().skip_binder() { - ClauseKind::Trait(ref trait_pred) => { - trait_pred.def_id() == sized_def_id - && matches!( - trait_pred.self_ty().kind(), - TyKind::Param(ParamTy { index: 0, .. }) - ) + elaborate(self, predicates.iter_identity().map(Unnormalized::skip_norm_wip)).any(|pred| { + match pred.kind().skip_binder() { + ClauseKind::Trait(ref trait_pred) => { + trait_pred.def_id() == sized_def_id + && matches!( + trait_pred.self_ty().kind(), + TyKind::Param(ParamTy { index: 0, .. }) + ) + } + ClauseKind::RegionOutlives(_) + | ClauseKind::TypeOutlives(_) + | ClauseKind::Projection(_) + | ClauseKind::ConstArgHasType(_, _) + | ClauseKind::WellFormed(_) + | ClauseKind::ConstEvaluatable(_) + | ClauseKind::HostEffect(..) + | ClauseKind::UnstableFeature(_) => false, } - ClauseKind::RegionOutlives(_) - | ClauseKind::TypeOutlives(_) - | ClauseKind::Projection(_) - | ClauseKind::ConstArgHasType(_, _) - | ClauseKind::WellFormed(_) - | ClauseKind::ConstEvaluatable(_) - | ClauseKind::HostEffect(..) - | ClauseKind::UnstableFeature(_) => false, }) } @@ -1485,22 +1408,22 @@ impl<'db> Interner for DbInterner<'db> { false } - fn require_lang_item(self, lang_item: SolverLangItem) -> Self::DefId { + fn require_projection_lang_item( + self, + lang_item: SolverProjectionLangItem, + ) -> Self::TraitAssocTyId { let lang_items = self.lang_items(); let lang_item = match lang_item { - SolverLangItem::AsyncFnKindUpvars => lang_items.AsyncFnKindUpvars, - SolverLangItem::AsyncFnOnceOutput => lang_items.AsyncFnOnceOutput, - SolverLangItem::CallOnceFuture => lang_items.CallOnceFuture, - SolverLangItem::CallRefFuture => lang_items.CallRefFuture, - SolverLangItem::CoroutineReturn => lang_items.CoroutineReturn, - SolverLangItem::CoroutineYield => lang_items.CoroutineYield, - SolverLangItem::FutureOutput => lang_items.FutureOutput, - SolverLangItem::Metadata => lang_items.Metadata, - SolverLangItem::DynMetadata => { - return lang_items.DynMetadata.expect("Lang item required but not found.").into(); - } - SolverLangItem::FieldBase => lang_items.FieldBase, - SolverLangItem::FieldType => lang_items.FieldType, + SolverProjectionLangItem::AsyncFnKindUpvars => lang_items.AsyncFnKindUpvars, + SolverProjectionLangItem::AsyncFnOnceOutput => lang_items.AsyncFnOnceOutput, + SolverProjectionLangItem::CallOnceFuture => lang_items.CallOnceFuture, + SolverProjectionLangItem::CallRefFuture => lang_items.CallRefFuture, + SolverProjectionLangItem::CoroutineReturn => lang_items.CoroutineReturn, + SolverProjectionLangItem::CoroutineYield => lang_items.CoroutineYield, + SolverProjectionLangItem::FutureOutput => lang_items.FutureOutput, + SolverProjectionLangItem::Metadata => lang_items.Metadata, + SolverProjectionLangItem::FieldBase => lang_items.FieldBase, + SolverProjectionLangItem::FieldType => lang_items.FieldType, }; lang_item.expect("Lang item required but not found.").into() } @@ -1512,9 +1435,6 @@ impl<'db> Interner for DbInterner<'db> { SolverTraitLangItem::AsyncFnKindHelper => lang_items.AsyncFnKindHelper, SolverTraitLangItem::AsyncFnMut => lang_items.AsyncFnMut, SolverTraitLangItem::AsyncFnOnce => lang_items.AsyncFnOnce, - SolverTraitLangItem::AsyncFnOnceOutput => unimplemented!( - "This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver." - ), SolverTraitLangItem::AsyncIterator => lang_items.AsyncIterator, SolverTraitLangItem::Clone => lang_items.Clone, SolverTraitLangItem::Copy => lang_items.Copy, @@ -1547,14 +1467,19 @@ impl<'db> Interner for DbInterner<'db> { fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> AdtIdWrapper { let lang_items = self.lang_items(); let lang_item = match lang_item { - SolverAdtLangItem::Option => lang_items.Option, - SolverAdtLangItem::Poll => lang_items.Poll, + SolverAdtLangItem::Option => lang_items.Option.map(Into::into), + SolverAdtLangItem::Poll => lang_items.Poll.map(Into::into), + SolverAdtLangItem::DynMetadata => lang_items.DynMetadata.map(Into::into), }; - AdtIdWrapper(lang_item.expect("Lang item required but not found.").into()) + AdtIdWrapper(lang_item.expect("Lang item required but not found.")) } - fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool { - self.as_lang_item(def_id) + fn is_projection_lang_item( + self, + def_id: Self::TraitAssocTyId, + lang_item: SolverProjectionLangItem, + ) -> bool { + self.as_projection_lang_item(def_id) .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) } @@ -1562,15 +1487,6 @@ impl<'db> Interner for DbInterner<'db> { is_lang_item!( SolverTraitLangItem, self, def_id.0, lang_item; - ignore = { - AsyncFnKindHelper, - AsyncIterator, - BikeshedGuaranteedNoDrop, - FusedIterator, - Field, - AsyncFnOnceOutput, // This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver. - } - Sized, MetaSized, PointeeSized, @@ -1595,6 +1511,11 @@ impl<'db> Interner for DbInterner<'db> { AsyncFnMut, AsyncFnOnce, TrivialClone, + AsyncFnKindHelper, + AsyncIterator, + BikeshedGuaranteedNoDrop, + FusedIterator, + Field, ) } @@ -1604,64 +1525,29 @@ impl<'db> Interner for DbInterner<'db> { .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) } - fn as_lang_item(self, def_id: Self::DefId) -> Option<SolverLangItem> { - match def_id { - SolverDefId::TypeAliasId(id) => { - as_lang_item!( - SolverLangItem, self, id; - - ignore = { - AsyncFnKindUpvars, - DynMetadata, - FieldBase, - FieldType, - } - - Metadata, - CoroutineReturn, - CoroutineYield, - FutureOutput, - CallRefFuture, - CallOnceFuture, - AsyncFnOnceOutput, - ) - } - SolverDefId::AdtId(AdtId::StructId(id)) => { - as_lang_item!( - SolverLangItem, self, id; - - ignore = { - AsyncFnKindUpvars, - Metadata, - CoroutineReturn, - CoroutineYield, - FutureOutput, - CallRefFuture, - CallOnceFuture, - AsyncFnOnceOutput, - FieldBase, - FieldType, - } - - DynMetadata, - ) - } - _ => panic!("Unexpected SolverDefId in as_lang_item"), - } + fn as_projection_lang_item( + self, + def_id: Self::TraitAssocTyId, + ) -> Option<SolverProjectionLangItem> { + as_lang_item!( + SolverProjectionLangItem, self, def_id.0, TypeAliasId; + + Metadata, + CoroutineReturn, + CoroutineYield, + FutureOutput, + CallRefFuture, + CallOnceFuture, + AsyncFnOnceOutput, + AsyncFnKindUpvars, + FieldBase, + FieldType, + ) } fn as_trait_lang_item(self, def_id: Self::TraitId) -> Option<SolverTraitLangItem> { as_lang_item!( - SolverTraitLangItem, self, def_id.0; - - ignore = { - AsyncFnKindHelper, - AsyncIterator, - BikeshedGuaranteedNoDrop, - FusedIterator, - Field, - AsyncFnOnceOutput, // This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver. - } + SolverTraitLangItem, self, def_id.0, TraitId; Sized, MetaSized, @@ -1687,20 +1573,21 @@ impl<'db> Interner for DbInterner<'db> { AsyncFnMut, AsyncFnOnce, TrivialClone, + AsyncFnKindHelper, + AsyncIterator, + BikeshedGuaranteedNoDrop, + FusedIterator, + Field, ) } fn as_adt_lang_item(self, def_id: Self::AdtId) -> Option<SolverAdtLangItem> { - let AdtId::EnumId(def_id) = def_id.0 else { - panic!("Unexpected SolverDefId in as_adt_lang_item"); - }; as_lang_item!( - SolverAdtLangItem, self, def_id; - - ignore = {} + SolverAdtLangItem, self, def_id.0, AdtId; Option, Poll, + DynMetadata, ) } @@ -1869,7 +1756,7 @@ impl<'db> Interner for DbInterner<'db> { }); } - fn has_item_definition(self, _def_id: Self::DefId) -> bool { + fn has_item_definition(self, _def_id: Self::ImplOrTraitAssocTermId) -> bool { // FIXME(next-solver): should check if the associated item has a value. true } @@ -1940,41 +1827,28 @@ impl<'db> Interner for DbInterner<'db> { } fn is_general_coroutine(self, def_id: Self::CoroutineId) -> bool { - // FIXME: Make this a query? I don't believe this can be accessed from bodies other than - // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); - let store = ExpressionStore::of(self.db, owner); - matches!( - store[expr_id], - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine(_), - .. - } - ) + matches!(def_id.0.loc(self.db).kind, HirClosureKind::OldCoroutine(_)) } fn coroutine_is_async(self, def_id: Self::CoroutineId) -> bool { - // FIXME: Make this a query? I don't believe this can be accessed from bodies other than - // the current infer query, except with revealed opaques - is it rare enough to not matter? - let InternedClosure(owner, expr_id) = def_id.0.loc(self.db); - let store = ExpressionStore::of(self.db, owner); matches!( - store[expr_id], - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. }, - .. - } + def_id.0.loc(self.db).kind, + HirClosureKind::Coroutine { kind: HirCoroutineKind::Async, .. } ) } - fn coroutine_is_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool { - // We don't handle gen coroutines yet. - false + fn coroutine_is_gen(self, def_id: Self::CoroutineId) -> bool { + matches!( + def_id.0.loc(self.db).kind, + HirClosureKind::Coroutine { kind: HirCoroutineKind::Gen, .. } + ) } - fn coroutine_is_async_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool { - // We don't handle gen coroutines yet. - false + fn coroutine_is_async_gen(self, def_id: Self::CoroutineId) -> bool { + matches!( + def_id.0.loc(self.db).kind, + HirClosureKind::Coroutine { kind: HirCoroutineKind::AsyncGen, .. } + ) } fn unsizing_params_for_adt(self, id: Self::AdtId) -> Self::UnsizingParams { @@ -1994,16 +1868,21 @@ impl<'db> Interner for DbInterner<'db> { }; // The last field of the structure has to exist and contain type/const parameters. - let variant = def.non_enum_variant(); + let variant = match def.def_id() { + AdtId::StructId(id) => VariantId::from(id), + AdtId::UnionId(id) => id.into(), + AdtId::EnumId(_) => panic!("expected a struct or a union"), + }; let fields = variant.fields(self.db()); - let Some((tail_field, prefix_fields)) = fields.split_last() else { + let mut prefix_fields = fields.fields().iter(); + let Some(tail_field) = prefix_fields.next_back() else { return UnsizingParams(DenseBitSet::new_empty(num_params)); }; - let field_types = self.db().field_types(variant.id()); + let field_types = self.db().field_types(variant); let mut unsizing_params = DenseBitSet::new_empty(num_params); let ty = field_types[tail_field.0].get(); - for arg in ty.instantiate_identity().walk() { + for arg in ty.instantiate_identity().skip_norm_wip().walk() { if let Some(i) = maybe_unsizing_param_idx(arg) { unsizing_params.insert(i); } @@ -2012,7 +1891,7 @@ impl<'db> Interner for DbInterner<'db> { // Ensure none of the other fields mention the parameters used // in unsizing. for field in prefix_fields { - for arg in field_types[field.0].get().instantiate_identity().walk() { + for arg in field_types[field.0].get().instantiate_identity().skip_norm_wip().walk() { if let Some(i) = maybe_unsizing_param_idx(arg) { unsizing_params.remove(i); } @@ -2066,7 +1945,7 @@ impl<'db> Interner for DbInterner<'db> { } fn opaque_types_defined_by(self, def_id: Self::LocalDefId) -> Self::LocalDefIds { - let Ok(def_id) = DefWithBodyId::try_from(def_id) else { + let Ok(def_id) = InferBodyId::try_from(def_id) else { return SolverDefIds::default(); }; let mut result = Vec::new(); @@ -2075,33 +1954,54 @@ impl<'db> Interner for DbInterner<'db> { } fn opaque_types_and_coroutines_defined_by(self, def_id: Self::LocalDefId) -> Self::LocalDefIds { - let Ok(def_id) = DefWithBodyId::try_from(def_id) else { + let db = self.db; + + let Ok(def_id) = InferBodyId::try_from(def_id) else { return SolverDefIds::default(); }; let mut result = Vec::new(); - crate::opaques::opaque_types_defined_by(self.db, def_id, &mut result); + crate::opaques::opaque_types_defined_by(db, def_id, &mut result); // Collect coroutines. - let body = Body::of(self.db, def_id); - body.exprs().for_each(|(expr_id, expr)| { - if matches!( - expr, - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. } - | hir_def::hir::ClosureKind::Coroutine(_), + let (store, root_expr) = def_id.store_and_root_expr(db); + // We can't just visit all exprs, since this may end up in unrelated anon consts. + CoroutinesVisitor { db: self.db, owner: def_id, store, coroutines: &mut result } + .on_expr(root_expr); + + return SolverDefIds::new_from_slice(&result); + + struct CoroutinesVisitor<'a> { + db: &'a dyn HirDatabase, + owner: InferBodyId, + store: &'a ExpressionStore, + coroutines: &'a mut Vec<SolverDefId>, + } + + impl StoreVisitor for CoroutinesVisitor<'_> { + fn on_expr(&mut self, expr: ExprId) { + if let hir_def::hir::Expr::Closure { + closure_kind: + kind @ (hir_def::hir::ClosureKind::Coroutine { .. } + | hir_def::hir::ClosureKind::OldCoroutine(_)), .. + } = self.store[expr] + { + let coroutine = InternedCoroutineId::new( + self.db, + InternedClosure { owner: self.owner, expr, kind }, + ); + self.coroutines.push(coroutine.into()); } - ) { - let coroutine = InternedCoroutineId::new( - self.db, - InternedClosure(ExpressionStoreOwnerId::Body(def_id), expr_id), - ); - result.push(coroutine.into()); - } - }); - SolverDefIds::new_from_slice(&result) + self.store.visit_expr_children(expr, self); + } + fn on_pat(&mut self, pat: PatId) { + self.store.visit_pat_children(pat, self); + } + // Do not visit anon consts, they're separate bodies. + fn on_anon_const_expr(&mut self, _expr: ExprId) {} + } } fn alias_has_const_conditions(self, _def_id: Self::DefId) -> bool { @@ -2134,26 +2034,23 @@ impl<'db> Interner for DbInterner<'db> { fn opt_alias_variances( self, - _kind: impl Into<rustc_type_ir::AliasTermKind>, - _def_id: Self::DefId, + _kind: impl Into<AliasTermKind<'db>>, ) -> Option<Self::VariancesOf> { None } - fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) -> EarlyBinder<Self, Self::Ty> { - match def_id { - SolverDefId::InternedOpaqueTyId(opaque) => { - let impl_trait_id = self.db().lookup_intern_impl_trait_id(opaque); - match impl_trait_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - crate::opaques::rpit_hidden_types(self.db, func)[idx].get() - } - crate::ImplTraitId::TypeAliasImplTrait(type_alias, idx) => { - crate::opaques::tait_hidden_types(self.db, type_alias)[idx].get() - } - } + fn type_of_opaque_hir_typeck( + self, + opaque: Self::LocalOpaqueTyId, + ) -> EarlyBinder<Self, Self::Ty> { + let impl_trait_id = opaque.0.loc(self.db); + match impl_trait_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + crate::opaques::rpit_hidden_types(self.db, func)[idx].get() + } + crate::ImplTraitId::TypeAliasImplTrait(type_alias, idx) => { + crate::opaques::tait_hidden_types(self.db, type_alias)[idx].get() } - _ => panic!("Unexpected SolverDefId in type_of_opaque_hir_typeck"), } } @@ -2240,16 +2137,20 @@ impl<'db> Interner for DbInterner<'db> { rustc_type_ir::AnonConstKind::GCE } - fn alias_ty_kind_from_def_id(self, def_id: Self::DefId) -> AliasTyKind<DbInterner<'db>> { + fn alias_ty_kind_from_def_id(self, def_id: Self::DefId) -> AliasTyKind<'db> { match def_id { SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container { ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => { - AliasTyKind::Free { def_id } + AliasTyKind::Free { def_id: type_alias.into() } + } + ItemContainerId::ImplId(_) => AliasTyKind::Inherent { def_id: type_alias.into() }, + ItemContainerId::TraitId(_) => { + AliasTyKind::Projection { def_id: type_alias.into() } } - ItemContainerId::ImplId(_) => AliasTyKind::Inherent { def_id }, - ItemContainerId::TraitId(_) => AliasTyKind::Projection { def_id }, }, - SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque { def_id }, + SolverDefId::InternedOpaqueTyId(def_id) => { + AliasTyKind::Opaque { def_id: def_id.into() } + } _ => unreachable!(), } } @@ -2339,7 +2240,7 @@ impl<'db> DbInterner<'db> { output: Ty<'db>, c_variadic: bool, safety: Safety, - abi: FnAbi, + abi: ExternAbi, ) -> FnSig<'db> where I: IntoIterator<Item = Ty<'db>>, @@ -2349,9 +2250,7 @@ impl<'db> DbInterner<'db> { self, inputs.into_iter().chain(std::iter::once(output)), ), - c_variadic, - safety, - abi, + fn_sig_kind: FnSigKind::new(abi, safety, c_variadic), } } @@ -2360,23 +2259,22 @@ impl<'db> DbInterner<'db> { where I: IntoIterator<Item = Ty<'db>>, { - FnSig { - inputs_and_output: Tys::new_from_iter( - self, - inputs.into_iter().chain(std::iter::once(output)), - ), - c_variadic: false, - safety: Safety::Safe, - abi: FnAbi::Rust, - } + self.mk_fn_sig(inputs, output, false, Safety::Safe, ExternAbi::Rust) } } fn predicates_of(db: &dyn HirDatabase, def_id: SolverDefId) -> &GenericPredicates { - if let SolverDefId::BuiltinDeriveImplId(impl_) = def_id { - crate::builtin_derive::predicates(db, impl_) - } else { - GenericPredicates::query(db, def_id.try_into().unwrap()) + match def_id { + SolverDefId::BuiltinDeriveImplId(impl_) => crate::builtin_derive::predicates(db, impl_), + SolverDefId::AnonConstId(anon_const) => { + let loc = anon_const.loc(db); + if loc.allow_using_generic_params { + GenericPredicates::query(db, loc.owner.generic_def(db)) + } else { + GenericPredicates::empty() + } + } + _ => GenericPredicates::query(db, def_id.try_into().unwrap()), } } @@ -2428,10 +2326,22 @@ TrivialTypeTraversalImpls! { CoroutineIdWrapper, CoroutineClosureIdWrapper, AdtIdWrapper, + TraitAssocTyId, + TraitAssocConstId, + TraitAssocTermId, + ImplOrTraitAssocTyId, + ImplOrTraitAssocConstId, + ImplOrTraitAssocTermId, + FreeTyAliasId, + FreeConstAliasId, + FreeTermAliasId, + InherentAssocTyId, + InherentAssocConstId, + InherentAssocTermId, + OpaqueTyIdWrapper, AnyImplId, GeneralConstIdWrapper, Safety, - FnAbi, Span, ParamConst, ParamTy, diff --git a/crates/hir-ty/src/next_solver/ir_print.rs b/crates/hir-ty/src/next_solver/ir_print.rs index 5dd372a367..925640e1f6 100644 --- a/crates/hir-ty/src/next_solver/ir_print.rs +++ b/crates/hir-ty/src/next_solver/ir_print.rs @@ -3,6 +3,8 @@ use hir_def::signatures::{TraitSignature, TypeAliasSignature}; use rustc_type_ir::{self as ty, ir_print::IrPrint}; +use crate::next_solver::TermId; + use super::SolverDefId; use super::interner::DbInterner; @@ -32,7 +34,7 @@ impl<'db> IrPrint<ty::AliasTerm<Self>> for DbInterner<'db> { } fn print_debug(t: &ty::AliasTerm<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - crate::with_attached_db(|db| match t.def_id { + crate::with_attached_db(|db| match t.def_id() { SolverDefId::TypeAliasId(id) => fmt.write_str(&format!( "AliasTerm({:?}[{:?}])", TypeAliasSignature::of(db, id).name.as_str(), @@ -141,8 +143,8 @@ impl<'db> IrPrint<ty::ExistentialProjection<Self>> for DbInterner<'db> { fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { crate::with_attached_db(|db| { - let id = match t.def_id { - SolverDefId::TypeAliasId(id) => id, + let id = match t.def_id.0 { + TermId::TypeAliasId(id) => id, _ => panic!("Expected trait."), }; fmt.write_str(&format!( @@ -167,7 +169,7 @@ impl<'db> IrPrint<ty::ProjectionPredicate<Self>> for DbInterner<'db> { fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { crate::with_attached_db(|db| { - let id = match t.projection_term.def_id { + let id = match t.projection_term.def_id() { SolverDefId::TypeAliasId(id) => id, _ => panic!("Expected trait."), }; diff --git a/crates/hir-ty/src/next_solver/normalize.rs b/crates/hir-ty/src/next_solver/normalize.rs index 5d8f3fe519..152b58baeb 100644 --- a/crates/hir-ty/src/next_solver/normalize.rs +++ b/crates/hir-ty/src/next_solver/normalize.rs @@ -99,10 +99,10 @@ impl<'db> NormalizationFolder<'_, 'db> { self.depth += 1; - let infer_term = infcx.next_term_var_of_kind(alias_term); + let infer_term = infcx.next_term_var_of_kind(alias_term, self.at.cause.span()); let obligation = Obligation::new( interner, - self.at.cause.clone(), + *self.at.cause, self.at.param_env, PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate), ); @@ -229,7 +229,6 @@ impl<'db> FallibleTypeFolder<DbInterner<'db>> for NormalizationFolder<'_, 'db> { } // Deeply normalize a value and return it -#[expect(dead_code, reason = "rustc has this")] pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable<DbInterner<'db>>>( infcx: &InferCtxt<'db>, param_env: ParamEnv<'db>, diff --git a/crates/hir-ty/src/next_solver/predicate.rs b/crates/hir-ty/src/next_solver/predicate.rs index 8658d03a9e..30738dea5c 100644 --- a/crates/hir-ty/src/next_solver/predicate.rs +++ b/crates/hir-ty/src/next_solver/predicate.rs @@ -31,6 +31,7 @@ pub type ExistentialPredicate<'db> = ty::ExistentialPredicate<DbInterner<'db>>; pub type ExistentialTraitRef<'db> = ty::ExistentialTraitRef<DbInterner<'db>>; pub type ExistentialProjection<'db> = ty::ExistentialProjection<DbInterner<'db>>; pub type TraitPredicate<'db> = ty::TraitPredicate<DbInterner<'db>>; +pub type HostEffectPredicate<'db> = ty::HostEffectPredicate<DbInterner<'db>>; pub type ClauseKind<'db> = ty::ClauseKind<DbInterner<'db>>; pub type PredicateKind<'db> = ty::PredicateKind<DbInterner<'db>>; pub type NormalizesTo<'db> = ty::NormalizesTo<DbInterner<'db>>; @@ -705,34 +706,6 @@ impl<'db> rustc_type_ir::inherent::Predicate<DbInterner<'db>> for Predicate<'db> _ => None, } } - - /// Whether this projection can be soundly normalized. - /// - /// Wf predicates must not be normalized, as normalization - /// can remove required bounds which would cause us to - /// unsoundly accept some programs. See #91068. - fn allow_normalization(self) -> bool { - // TODO: this should probably live in rustc_type_ir - match self.inner().as_ref().skip_binder() { - PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => { - false - } - PredicateKind::Clause(ClauseKind::Trait(_)) - | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) - | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) - | PredicateKind::Clause(ClauseKind::Projection(_)) - | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) - | PredicateKind::Clause(ClauseKind::HostEffect(..)) - | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) - | PredicateKind::DynCompatible(_) - | PredicateKind::Subtype(_) - | PredicateKind::Coerce(_) - | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) - | PredicateKind::ConstEquate(_, _) - | PredicateKind::NormalizesTo(..) - | PredicateKind::Ambiguous => true, - } - } } impl<'db> Predicate<'db> { @@ -912,7 +885,9 @@ impl<'db> rustc_type_ir::inherent::Clause<DbInterner<'db>> for Clause<'db> { let shifted_pred = cx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder()); // 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1> - let new = EarlyBinder::bind(shifted_pred).instantiate(cx, trait_ref.skip_binder().args); + let new = EarlyBinder::bind(shifted_pred) + .instantiate(cx, trait_ref.skip_binder().args) + .skip_norm_wip(); // 3) ['x] + ['b] -> ['x, 'b] let bound_vars = BoundVarKinds::new_from_iter(cx, trait_bound_vars.iter().chain(pred_bound_vars.iter())); diff --git a/crates/hir-ty/src/next_solver/region.rs b/crates/hir-ty/src/next_solver/region.rs index 3f0aebac2d..72a25f4df6 100644 --- a/crates/hir-ty/src/next_solver/region.rs +++ b/crates/hir-ty/src/next_solver/region.rs @@ -18,6 +18,7 @@ use crate::next_solver::{ use super::{SolverDefId, interner::DbInterner}; pub type RegionKind<'db> = rustc_type_ir::RegionKind<DbInterner<'db>>; +pub type RegionConstraint<'db> = rustc_type_ir::RegionConstraint<DbInterner<'db>>; #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct Region<'db> { @@ -136,7 +137,7 @@ impl<'db> Region<'db> { } RegionKind::ReError(..) => { flags |= TypeFlags::HAS_FREE_REGIONS; - flags |= TypeFlags::HAS_ERROR; + flags |= TypeFlags::HAS_RE_ERROR; } } diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs index d45ac6c959..b1b3a0e0dc 100644 --- a/crates/hir-ty/src/next_solver/solver.rs +++ b/crates/hir-ty/src/next_solver/solver.rs @@ -1,30 +1,32 @@ //! Defining `SolverContext` for next-trait-solver. use hir_def::{ - AssocItemId, GeneralConstId, + AssocItemId, signatures::{ConstSignature, TypeAliasSignature}, }; use rustc_next_trait_solver::delegate::SolverDelegate; use rustc_type_ir::{ - AliasTyKind, GenericArgKind, InferCtxtLike, Interner, PredicatePolarity, TypeFlags, + AliasTyKind, GenericArgKind, InferCtxtLike, InferTy, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, inherent::{IntoKind, Term as _, Ty as _}, lang_items::SolverTraitLangItem, - solve::{Certainty, NoSolution}, + solve::{Certainty, FetchEligibleAssocItemResponse, NoSolution, VisibleForLeakCheck}, }; use tracing::debug; use crate::{ - ParamEnvAndCrate, + ParamEnvAndCrate, Span, + db::GeneralConstId, next_solver::{ - AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, - ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, UnevaluatedConst, - fold::fold_tys, util::sizedness_fast_path, + AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, ErrorGuaranteed, + GenericArgs, ImplOrTraitAssocTermId, OpaqueTyIdWrapper, ParamEnv, Predicate, PredicateKind, + RegionConstraint, SubtypePredicate, TermId, TraitAssocTermId, Ty, TyKind, TypingMode, + UnevaluatedConst, fold::fold_tys, util::sizedness_fast_path, }, }; use super::{ - Const, DbInterner, ErrorGuaranteed, GenericArg, SolverDefId, Span, + Const, DbInterner, GenericArg, infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt}, }; @@ -63,15 +65,15 @@ impl<'db> SolverDelegate for SolverContext<'db> { where V: rustc_type_ir::TypeFoldable<Self::Interner>, { - let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(canonical); + let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(Span::Dummy, canonical); (SolverContext(infcx), value, vars) } - fn fresh_var_for_kind_with_span(&self, arg: GenericArg<'db>, _span: Span) -> GenericArg<'db> { + fn fresh_var_for_kind_with_span(&self, arg: GenericArg<'db>, span: Span) -> GenericArg<'db> { match arg.kind() { - GenericArgKind::Lifetime(_) => self.next_region_var().into(), - GenericArgKind::Type(_) => self.next_ty_var().into(), - GenericArgKind::Const(_) => self.next_const_var().into(), + GenericArgKind::Lifetime(_) => self.next_region_var(span).into(), + GenericArgKind::Type(_) => self.next_ty_var(span).into(), + GenericArgKind::Const(_) => self.next_const_var(span).into(), } } @@ -98,14 +100,9 @@ impl<'db> SolverDelegate for SolverContext<'db> { None } - fn make_deduplicated_outlives_constraints( + fn make_deduplicated_region_constraints( &self, - ) -> Vec< - rustc_type_ir::OutlivesPredicate< - Self::Interner, - <Self::Interner as rustc_type_ir::Interner>::GenericArg, - >, - > { + ) -> Vec<(RegionConstraint<'db>, VisibleForLeakCheck)> { // FIXME: add if we care about regions vec![] } @@ -124,23 +121,22 @@ impl<'db> SolverDelegate for SolverContext<'db> { fn instantiate_canonical_var( &self, kind: CanonicalVarKind<'db>, - _span: <Self::Interner as Interner>::Span, + span: Span, var_values: &[GenericArg<'db>], universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex, ) -> GenericArg<'db> { - self.0.instantiate_canonical_var(kind, var_values, universe_map) + self.0.instantiate_canonical_var(span, kind, var_values, universe_map) } fn add_item_bounds_for_hidden_type( &self, - def_id: SolverDefId, + opaque_id: OpaqueTyIdWrapper, args: GenericArgs<'db>, param_env: ParamEnv<'db>, hidden_ty: Ty<'db>, goals: &mut Vec<Goal<'db, Predicate<'db>>>, ) { let interner = self.interner; - let opaque_id = def_id.expect_opaque_ty(); // Require that the hidden type is well-formed. We have to // make sure we wf-check the hidden type to fix #114728. // @@ -162,14 +158,14 @@ impl<'db> SolverDelegate for SolverContext<'db> { kind: AliasTyKind::Opaque { def_id: def_id2 }, args: args2, .. - }) if def_id == def_id2 && args == args2 => hidden_ty, + }) if opaque_id == def_id2 && args == args2 => hidden_ty, _ => ty, }) }; - let item_bounds = opaque_id.predicates(interner.db); + let item_bounds = opaque_id.0.predicates(interner.db); for predicate in item_bounds.iter_instantiated_copied(interner, args.as_slice()) { - let predicate = replace_opaques_in(predicate); + let predicate = replace_opaques_in(predicate.skip_norm_wip()); // Require that the predicate holds for the concrete type. debug!(?predicate); @@ -180,16 +176,16 @@ impl<'db> SolverDelegate for SolverContext<'db> { fn fetch_eligible_assoc_item( &self, _goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>, - trait_assoc_def_id: SolverDefId, + trait_assoc_def_id: TraitAssocTermId, impl_id: AnyImplId, - ) -> Result<Option<SolverDefId>, ErrorGuaranteed> { + ) -> FetchEligibleAssocItemResponse<Self::Interner> { let AnyImplId::ImplId(impl_id) = impl_id else { // Builtin derive traits don't have type/consts assoc items. - return Ok(None); + return FetchEligibleAssocItemResponse::Err(ErrorGuaranteed); }; let impl_items = impl_id.impl_items(self.0.interner.db()); - let id = match trait_assoc_def_id { - SolverDefId::TypeAliasId(trait_assoc_id) => { + let id = match trait_assoc_def_id.0 { + TermId::TypeAliasId(trait_assoc_id) => { let trait_assoc_data = TypeAliasSignature::of(self.0.interner.db, trait_assoc_id); impl_items .items @@ -206,9 +202,9 @@ impl<'db> SolverDelegate for SolverContext<'db> { .or_else(|| { if trait_assoc_data.ty.is_some() { Some(trait_assoc_id) } else { None } }) - .map(SolverDefId::TypeAliasId) + .map(|def| ImplOrTraitAssocTermId(TermId::TypeAliasId(def))) } - SolverDefId::ConstId(trait_assoc_id) => { + TermId::ConstId(trait_assoc_id) => { let trait_assoc_data = ConstSignature::of(self.0.interner.db, trait_assoc_id); let trait_assoc_name = trait_assoc_data .name @@ -231,11 +227,20 @@ impl<'db> SolverDelegate for SolverContext<'db> { if trait_assoc_data.has_body() { Some(trait_assoc_id) } else { None } }, ) - .map(SolverDefId::ConstId) + .map(|def| ImplOrTraitAssocTermId(TermId::ConstId(def))) } - _ => panic!("Unexpected SolverDefId"), }; - Ok(id) + match id { + Some(id) => FetchEligibleAssocItemResponse::Found(id), + None => match self.typing_mode_raw() { + TypingMode::ErasedNotCoherence(_) => { + FetchEligibleAssocItemResponse::NotFoundBecauseErased + } + typing_mode => { + FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased()) + } + }, + } } fn is_transmutable( @@ -260,11 +265,10 @@ impl<'db> SolverDelegate for SolverContext<'db> { self.cx().db.const_eval(c, subst, None).ok()? } GeneralConstId::StaticId(c) => self.cx().db.const_eval_static(c).ok()?, - // TODO: Wire up const_eval_anon query in Phase 5. - // For now, return an error const so normalization resolves the - // unevaluated const to Error (matching the old behavior where - // complex expressions produced ConstKind::Error directly). - GeneralConstId::AnonConstId(_) => return Some(Const::error(self.cx())), + GeneralConstId::AnonConstId(c) => { + let subst = uv.args; + self.cx().db.anon_const_eval(c, subst, None).ok()? + } }; Some(Const::new_from_allocation( self.interner, @@ -293,10 +297,10 @@ impl<'db> SolverDelegate for SolverContext<'db> { } if trait_pred.polarity() == PredicatePolarity::Positive { - match self.0.cx().as_trait_lang_item(trait_pred.def_id()) { + match self.0.interner.as_trait_lang_item(trait_pred.def_id()) { Some(SolverTraitLangItem::Sized) | Some(SolverTraitLangItem::MetaSized) => { let predicate = self.resolve_vars_if_possible(goal.predicate); - if sizedness_fast_path(self.cx(), predicate, goal.param_env) { + if sizedness_fast_path(self.interner, predicate, goal.param_env) { return Some(Certainty::Yes); } } @@ -322,17 +326,31 @@ impl<'db> SolverDelegate for SolverContext<'db> { let pred = goal.predicate.kind(); match pred.no_bound_vars()? { - PredicateKind::Clause(ClauseKind::RegionOutlives(_outlives)) => Some(Certainty::Yes), - PredicateKind::Clause(ClauseKind::TypeOutlives(_outlives)) => Some(Certainty::Yes), + PredicateKind::DynCompatible(def_id) + if self.0.interner.trait_is_dyn_compatible(def_id) => + { + Some(Certainty::Yes) + } + PredicateKind::Clause(ClauseKind::RegionOutlives(outlives)) => { + self.0.sub_regions(outlives.1, outlives.0); + Some(Certainty::Yes) + } + PredicateKind::Clause(ClauseKind::TypeOutlives(outlives)) => { + self.0.register_type_outlives_constraint(outlives.0, outlives.1); + + Some(Certainty::Yes) + } PredicateKind::Subtype(SubtypePredicate { a, b, .. }) | PredicateKind::Coerce(CoercePredicate { a, b }) => { - if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() { - // FIXME: We also need to register a subtype relation between these vars - // when those are added, and if they aren't in the same sub root then - // we should mark this goal as `has_changed`. - Some(Certainty::AMBIGUOUS) - } else { - None + match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) { + ( + TyKind::Infer(InferTy::TyVar(a_vid)), + TyKind::Infer(InferTy::TyVar(b_vid)), + ) => { + self.sub_unify_ty_vids_raw(a_vid, b_vid); + Some(Certainty::AMBIGUOUS) + } + _ => None, } } PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, _)) => { @@ -343,6 +361,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { } } PredicateKind::Clause(ClauseKind::WellFormed(arg)) => { + let arg = self.shallow_resolve_term(arg); if arg.is_trivially_wf(self.interner) { Some(Certainty::Yes) } else if arg.is_infer() { diff --git a/crates/hir-ty/src/next_solver/structural_normalize.rs b/crates/hir-ty/src/next_solver/structural_normalize.rs index 00c3708358..0dbd97a636 100644 --- a/crates/hir-ty/src/next_solver/structural_normalize.rs +++ b/crates/hir-ty/src/next_solver/structural_normalize.rs @@ -30,18 +30,18 @@ impl<'db> At<'_, 'db> { ) -> Result<Term<'db>, Vec<NextSolverError<'db>>> { assert!(!term.is_infer(), "should have resolved vars before calling"); - if term.to_alias_term().is_none() { + if term.to_alias_term(self.infcx.interner).is_none() { return Ok(term); } - let new_infer = self.infcx.next_term_var_of_kind(term); + let new_infer = self.infcx.next_term_var_of_kind(term, self.cause.span()); // We simply emit an `alias-eq` goal here, since that will take care of // normalizing the LHS of the projection until it is a rigid projection // (or a not-yet-defined opaque in scope). let obligation = Obligation::new( self.infcx.interner, - self.cause.clone(), + *self.cause, self.param_env, PredicateKind::AliasRelate(term, new_infer, AliasRelationDirection::Equate), ); diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs index 39abdaf079..c43e04b9d0 100644 --- a/crates/hir-ty/src/next_solver/ty.rs +++ b/crates/hir-ty/src/next_solver/ty.rs @@ -9,15 +9,15 @@ use hir_def::{ use hir_def::{TraitId, type_ref::Rawness}; use intern::{Interned, InternedRef, impl_internable}; use macros::GenericTypeVisitable; -use rustc_abi::{Float, Integer, Size}; +use rustc_abi::{ExternAbi, Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; use rustc_type_ir::{ - AliasTyKind, BoundVar, BoundVarIndexKind, ClosureKind, CoroutineArgs, CoroutineArgsParts, - DebruijnIndex, FlagComputation, Flags, FloatTy, FloatVid, GenericTypeVisitable, InferTy, IntTy, - IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, UintTy, Upcast, WithCachedTypeInfo, + BoundVar, BoundVarIndexKind, ClosureKind, DebruijnIndex, FlagComputation, Flags, FloatTy, + FloatVid, GenericTypeVisitable, InferTy, IntTy, IntVid, Interner, TyVid, TypeFoldable, + TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, + Upcast, WithCachedTypeInfo, inherent::{ - AdtDef as _, BoundExistentialPredicates, Const as _, GenericArgs as _, IntoKind, ParamLike, + AdtDef as _, BoundExistentialPredicates, GenericArgs as _, IntoKind, ParamLike, Safety as _, SliceLike, Ty as _, }, relate::Relate, @@ -26,13 +26,12 @@ use rustc_type_ir::{ }; use crate::{ - FnAbi, - db::{HirDatabase, InternedClosure}, + db::{HirDatabase, InternedOpaqueTyId}, lower::GenericPredicates, next_solver::{ AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const, - CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Region, - TraitRef, TypeAliasIdWrapper, + CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Predicate, + Region, TraitRef, TypeAliasIdWrapper, Unnormalized, abi::Safety, impl_foldable_for_interned_slice, impl_stored_interned, interned_slice, util::{CoroutineArgsExt, IntegerTypeExt}, @@ -47,6 +46,9 @@ use super::{ pub type SimplifiedType = rustc_type_ir::fast_reject::SimplifiedType<SolverDefId>; pub type TyKind<'db> = rustc_type_ir::TyKind<DbInterner<'db>>; pub type FnHeader<'db> = rustc_type_ir::FnHeader<DbInterner<'db>>; +pub type AliasTyKind<'db> = rustc_type_ir::AliasTyKind<DbInterner<'db>>; +pub type AliasTermKind<'db> = rustc_type_ir::AliasTermKind<DbInterner<'db>>; +pub type FnSigKind<'db> = rustc_type_ir::FnSigKind<DbInterner<'db>>; #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct Ty<'db> { @@ -174,15 +176,44 @@ impl<'db> Ty<'db> { pub fn new_opaque( interner: DbInterner<'db>, - def_id: SolverDefId, + def_id: InternedOpaqueTyId, args: GenericArgs<'db>, ) -> Self { Ty::new_alias( interner, - AliasTy::new_from_args(interner, AliasTyKind::Opaque { def_id }, args), + AliasTy::new_from_args(interner, AliasTyKind::Opaque { def_id: def_id.into() }, args), ) } + /// Note: this needs an interner with crate. + pub fn new_array(interner: DbInterner<'db>, ty: Ty<'db>, n: u128) -> Ty<'db> { + Ty::new( + interner, + TyKind::Array( + ty, + crate::consteval::usize_const(interner.db, Some(n), interner.expect_crate()), + ), + ) + } + + fn new_generic_adt(interner: DbInterner<'db>, adt_id: AdtId, ty_param: Ty<'db>) -> Ty<'db> { + let args = GenericArgs::fill_with_defaults( + interner, + adt_id.into(), + [ty_param.into()], + |_, _, _| panic!("all params except the first should have defaults"), + ); + Ty::new_adt(interner, adt_id, args) + } + + /// Note: Unlike most other constructors, this require the interner to have a crate, because this needs lang items. + pub fn new_box(interner: DbInterner<'db>, ty: Ty<'db>) -> Ty<'db> { + let Some(def_id) = interner.lang_items().OwnedBox else { + return Ty::new_error(interner, ErrorGuaranteed); + }; + Ty::new_generic_adt(interner, def_id.into(), ty) + } + /// Returns the `Size` for primitive types (bool, uint, int, char, float). pub fn primitive_size(self, interner: DbInterner<'db>) -> Size { match self.kind() { @@ -252,9 +283,9 @@ impl<'db> Ty<'db> { tys.last().is_none_or(|ty| ty.has_trivial_sizedness(tcx, sizedness)) } - TyKind::Adt(def, args) => def - .sizedness_constraint(tcx, sizedness) - .is_none_or(|ty| ty.instantiate(tcx, args).has_trivial_sizedness(tcx, sizedness)), + TyKind::Adt(def, args) => def.sizedness_constraint(tcx, sizedness).is_none_or(|ty| { + ty.instantiate(tcx, args).skip_norm_wip().has_trivial_sizedness(tcx, sizedness) + }), TyKind::Alias(..) | TyKind::Param(_) | TyKind::Placeholder(..) | TyKind::Bound(..) => { false @@ -472,10 +503,14 @@ impl<'db> Ty<'db> { } } + pub fn is_box(self) -> bool { + matches!(self.kind(), TyKind::Adt(adt_def, _) if adt_def.is_box()) + } + #[inline] pub fn as_adt(self) -> Option<(AdtId, GenericArgs<'db>)> { match self.kind() { - TyKind::Adt(adt_def, args) => Some((adt_def.def_id().0, args)), + TyKind::Adt(adt_def, args) => Some((adt_def.def_id(), args)), _ => None, } } @@ -501,7 +536,7 @@ impl<'db> Ty<'db> { /// unsafe. pub fn safe_to_unsafe_fn_ty(interner: DbInterner<'db>, sig: PolyFnSig<'db>) -> Ty<'db> { assert!(sig.safety().is_safe()); - Ty::new_fn_ptr(interner, sig.map_bound(|sig| FnSig { safety: Safety::Unsafe, ..sig })) + Ty::new_fn_ptr(interner, sig.map_bound(|sig| sig.set_safety(Safety::Unsafe))) } /// Returns the type of `*ty`. @@ -538,7 +573,7 @@ impl<'db> Ty<'db> { pub fn callable_sig(self, interner: DbInterner<'db>) -> Option<Binder<'db, FnSig<'db>>> { match self.kind() { TyKind::FnDef(callable, args) => { - Some(interner.fn_sig(callable).instantiate(interner, args)) + Some(interner.fn_sig(callable).instantiate(interner, args).skip_norm_wip()) } TyKind::FnPtr(sig, hdr) => Some(sig.with(hdr)), TyKind::Closure(_, closure_args) => { @@ -546,23 +581,13 @@ impl<'db> Ty<'db> { } TyKind::CoroutineClosure(coroutine_id, args) => { Some(args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { - let unit_ty = Ty::new_unit(interner); - let return_ty = Ty::new_coroutine( + let closure_args = args.as_coroutine_closure(); + let return_ty = sig.to_coroutine( interner, + closure_args.parent_args(), + closure_args.kind_ty(), interner.coroutine_for_closure(coroutine_id), - CoroutineArgs::new( - interner, - CoroutineArgsParts { - parent_args: args.as_coroutine_closure().parent_args(), - kind_ty: unit_ty, - resume_ty: unit_ty, - yield_ty: unit_ty, - return_ty: sig.return_ty, - // FIXME: Deduce this from the coroutine closure's upvars. - tupled_upvars_ty: unit_ty, - }, - ) - .args, + closure_args.tupled_upvars_ty(), ); FnSig { inputs_and_output: Tys::new_from_iter( @@ -572,9 +597,7 @@ impl<'db> Ty<'db> { .iter() .chain(std::iter::once(return_ty)), ), - c_variadic: sig.c_variadic, - safety: sig.safety, - abi: sig.abi, + fn_sig_kind: sig.fn_sig_kind, } })) } @@ -597,6 +620,10 @@ impl<'db> Ty<'db> { } } + pub fn is_tuple(self) -> bool { + matches!(self.kind(), TyKind::Tuple(_)) + } + pub fn as_tuple(self) -> Option<Tys<'db>> { match self.kind() { TyKind::Tuple(tys) => Some(tys), @@ -704,9 +731,10 @@ impl<'db> Ty<'db> { match self.kind() { TyKind::Alias(AliasTy { kind: AliasTyKind::Opaque { def_id }, args, .. }) => Some( def_id - .expect_opaque_ty() + .0 .predicates(db) .iter_instantiated_copied(interner, args.as_slice()) + .map(Unnormalized::skip_norm_wip) .collect(), ), TyKind::Param(param) => { @@ -718,6 +746,7 @@ impl<'db> Ty<'db> { TypeParamProvenance::ArgumentImplTrait => { let predicates = GenericPredicates::query_all(db, param.id.parent()) .iter_identity() + .map(Unnormalized::skip_norm_wip) .filter(|wc| match wc.kind().skip_binder() { ClauseKind::Trait(tr) => tr.self_ty() == self, ClauseKind::Projection(pred) => pred.self_ty() == self, @@ -734,7 +763,7 @@ impl<'db> Ty<'db> { } } TyKind::Coroutine(coroutine_id, _args) => { - let InternedClosure(owner, _) = coroutine_id.0.loc(db); + let owner = coroutine_id.0.loc(db).owner; let krate = owner.krate(db); if let Some(future_trait) = hir_def::lang_item::lang_items(db, krate).Future { // This is only used by type walking. @@ -784,25 +813,11 @@ impl<'db> Ty<'db> { } pub fn references_non_lt_error<'db, T: TypeVisitableExt<DbInterner<'db>>>(t: &T) -> bool { - t.references_error() && t.visit_with(&mut ReferencesNonLifetimeError).is_break() -} - -struct ReferencesNonLifetimeError; - -impl<'db> TypeVisitor<DbInterner<'db>> for ReferencesNonLifetimeError { - type Result = ControlFlow<()>; - - fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { - if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) } - } - - fn visit_const(&mut self, c: Const<'db>) -> Self::Result { - if c.is_ct_error() { ControlFlow::Break(()) } else { c.super_visit_with(self) } - } + t.has_non_region_error() } pub fn references_only_ty_error<'db, T: TypeVisitableExt<DbInterner<'db>>>(t: &T) -> bool { - t.references_error() && t.visit_with(&mut ReferencesOnlyTyError).is_break() + references_non_lt_error(t) && t.visit_with(&mut ReferencesOnlyTyError).is_break() } struct ReferencesOnlyTyError; @@ -811,7 +826,29 @@ impl<'db> TypeVisitor<DbInterner<'db>> for ReferencesOnlyTyError { type Result = ControlFlow<()>; fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { - if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) } + if !ty.references_non_lt_error() { + ControlFlow::Continue(()) + } else if ty.is_ty_error() { + ControlFlow::Break(()) + } else { + ty.super_visit_with(self) + } + } + + fn visit_const(&mut self, c: Const<'db>) -> Self::Result { + if !references_non_lt_error(&c) { + ControlFlow::Continue(()) + } else { + c.super_visit_with(self) + } + } + + fn visit_predicate(&mut self, p: Predicate<'db>) -> Self::Result { + if !references_non_lt_error(&p) { + ControlFlow::Continue(()) + } else { + p.super_visit_with(self) + } } } @@ -847,6 +884,15 @@ impl<'db> TypeVisitable<DbInterner<'db>> for Ty<'db> { } } +impl<'db> TypeVisitable<DbInterner<'db>> for StoredTy { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.as_ref().visit_with(visitor) + } +} + impl<'db> TypeSuperVisitable<DbInterner<'db>> for Ty<'db> { fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( &self, @@ -913,6 +959,18 @@ impl<'db> TypeFoldable<DbInterner<'db>> for Ty<'db> { } } +impl<'db> TypeFoldable<DbInterner<'db>> for StoredTy { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + self.as_ref().fold_with(folder).store() + } +} + impl<'db> TypeSuperFoldable<DbInterner<'db>> for Ty<'db> { fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( self, @@ -1294,9 +1352,11 @@ impl<'db> rustc_type_ir::inherent::Ty<DbInterner<'db>> for Ty<'db> { false } - fn discriminant_ty(self, interner: DbInterner<'db>) -> <DbInterner<'db> as Interner>::Ty { + fn discriminant_ty(self, interner: DbInterner<'db>) -> Ty<'db> { match self.kind() { - TyKind::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type().to_ty(interner), + TyKind::Adt(adt, _) if adt.is_enum() => { + adt.repr(interner.db).discr_type().to_ty(interner) + } TyKind::Coroutine(_, args) => args.as_coroutine().discr_ty(interner), TyKind::Param(_) | TyKind::Alias(..) | TyKind::Infer(InferTy::TyVar(_)) => { @@ -1444,7 +1504,7 @@ impl<'db> DbInterner<'db> { TyKind::Tuple(params) => params, _ => panic!(), }; - self.mk_fn_sig(params, s.output(), s.c_variadic, safety, FnAbi::Rust) + self.mk_fn_sig(params, s.output(), s.c_variadic(), safety, ExternAbi::Rust) }) } } diff --git a/crates/hir-ty/src/next_solver/util.rs b/crates/hir-ty/src/next_solver/util.rs index 858233cb2c..fb3bd18bf4 100644 --- a/crates/hir-ty/src/next_solver/util.rs +++ b/crates/hir-ty/src/next_solver/util.rs @@ -423,14 +423,14 @@ pub fn sizedness_constraint_for_ty<'db>( .and_then(|ty| sizedness_constraint_for_ty(interner, sizedness, ty)), Adt(adt, args) => { - if crate::representability::representability(interner.db, adt.def_id().0) + if crate::representability::representability(interner.db, adt.def_id()) == Representability::Infinite { return None; } adt.struct_tail_ty(interner).and_then(|tail_ty| { - let tail_ty = tail_ty.instantiate(interner, args); + let tail_ty = tail_ty.instantiate(interner, args).skip_norm_wip(); sizedness_constraint_for_ty(interner, sizedness, tail_ty) }) } @@ -717,7 +717,7 @@ pub(crate) fn clauses_as_obligations<'db>( param_env: ParamEnv<'db>, ) -> impl Iterator<Item = PredicateObligation<'db>> { clauses.into_iter().map(move |clause| Obligation { - cause: cause.clone(), + cause, param_env, predicate: clause.as_predicate(), recursion_depth: 0, diff --git a/crates/hir-ty/src/opaques.rs b/crates/hir-ty/src/opaques.rs index 2e85beea91..4244b1bac4 100644 --- a/crates/hir-ty/src/opaques.rs +++ b/crates/hir-ty/src/opaques.rs @@ -10,7 +10,7 @@ use rustc_type_ir::inherent::Ty as _; use syntax::ast; use crate::{ - ImplTraitId, InferenceResult, + ImplTraitId, InferBodyId, InferenceResult, db::{HirDatabase, InternedOpaqueTyId}, lower::{ImplTraitIdx, ImplTraits}, next_solver::{ @@ -22,10 +22,10 @@ use crate::{ pub(crate) fn opaque_types_defined_by( db: &dyn HirDatabase, - def_id: DefWithBodyId, + def_id: InferBodyId, result: &mut Vec<SolverDefId>, ) { - if let DefWithBodyId::FunctionId(func) = def_id { + if let Some(func) = def_id.as_function() { // A function may define its own RPITs. extend_with_opaques( db, @@ -66,9 +66,15 @@ pub(crate) fn opaque_types_defined_by( _ => {} }; match def_id { - DefWithBodyId::ConstId(id) => extend_with_atpit_from_container(id.loc(db).container), - DefWithBodyId::FunctionId(id) => extend_with_atpit_from_container(id.loc(db).container), - DefWithBodyId::StaticId(_) | DefWithBodyId::VariantId(_) => {} + InferBodyId::DefWithBodyId(DefWithBodyId::ConstId(id)) => { + extend_with_atpit_from_container(id.loc(db).container) + } + InferBodyId::DefWithBodyId(DefWithBodyId::FunctionId(id)) => { + extend_with_atpit_from_container(id.loc(db).container) + } + InferBodyId::DefWithBodyId(DefWithBodyId::StaticId(_)) + | InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(_)) + | InferBodyId::AnonConstId(_) => {} } // FIXME: Collect opaques from `#[define_opaque]`. @@ -91,8 +97,8 @@ pub(crate) fn opaque_types_defined_by( // These are firewall queries to prevent drawing dependencies between infers: #[salsa::tracked(returns(ref))] -pub(crate) fn rpit_hidden_types<'db>( - db: &'db dyn HirDatabase, +pub(crate) fn rpit_hidden_types( + db: &dyn HirDatabase, function: FunctionId, ) -> ArenaMap<ImplTraitIdx, StoredEarlyBinder<StoredTy>> { let infer = InferenceResult::of(db, DefWithBodyId::from(function)); @@ -105,8 +111,8 @@ pub(crate) fn rpit_hidden_types<'db>( } #[salsa::tracked(returns(ref))] -pub(crate) fn tait_hidden_types<'db>( - db: &'db dyn HirDatabase, +pub(crate) fn tait_hidden_types( + db: &dyn HirDatabase, type_alias: TypeAliasId, ) -> ArenaMap<ImplTraitIdx, StoredEarlyBinder<StoredTy>> { // Call this first, to not perform redundant work if there are no TAITs. @@ -149,7 +155,7 @@ pub(crate) fn tait_hidden_types<'db>( _ = ocx.eq( &cause, param_env, - entry.get().get().instantiate_identity(), + entry.get().get().instantiate_identity().skip_norm_wip(), hidden_type, ); } diff --git a/crates/hir-ty/src/representability.rs b/crates/hir-ty/src/representability.rs index bae204c4ef..0b7dc4d309 100644 --- a/crates/hir-ty/src/representability.rs +++ b/crates/hir-ty/src/representability.rs @@ -1,7 +1,7 @@ //! Detecting whether a type is infinitely-sized. use hir_def::{AdtId, VariantId, hir::generics::GenericParams}; -use rustc_type_ir::inherent::{AdtDef, IntoKind}; +use rustc_type_ir::inherent::IntoKind; use crate::{ db::HirDatabase, @@ -47,14 +47,14 @@ pub(crate) fn representability_cycle( fn variant_representability(db: &dyn HirDatabase, id: VariantId) -> Representability { for ty in db.field_types(id).values() { - rtry!(representability_ty(db, ty.get().instantiate_identity())); + rtry!(representability_ty(db, ty.get().instantiate_identity().skip_norm_wip())); } Representability::Representable } fn representability_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>) -> Representability { match ty.kind() { - TyKind::Adt(adt_id, args) => representability_adt_ty(db, adt_id.def_id().0, args), + TyKind::Adt(adt_id, args) => representability_adt_ty(db, adt_id.def_id(), args), // FIXME(#11924) allow zero-length arrays? TyKind::Array(ty, _) => representability_ty(db, ty), TyKind::Tuple(tys) => { @@ -94,7 +94,11 @@ fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { .collect::<Box<[bool]>>(); let mut handle_variant = |variant| { for field in db.field_types(variant).values() { - params_in_repr_ty(db, field.get().instantiate_identity(), &mut params_in_repr); + params_in_repr_ty( + db, + field.get().instantiate_identity().skip_norm_wip(), + &mut params_in_repr, + ); } }; match def_id { @@ -112,7 +116,7 @@ fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { fn params_in_repr_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>, params_in_repr: &mut [bool]) { match ty.kind() { TyKind::Adt(adt, args) => { - let inner_params_in_repr = self::params_in_repr(db, adt.def_id().0); + let inner_params_in_repr = self::params_in_repr(db, adt.def_id()); for (i, arg) in args.iter().enumerate() { if let GenericArgKind::Type(ty) = arg.kind() && inner_params_in_repr[i] diff --git a/crates/hir-ty/src/solver_errors.rs b/crates/hir-ty/src/solver_errors.rs new file mode 100644 index 0000000000..e4e76fa67b --- /dev/null +++ b/crates/hir-ty/src/solver_errors.rs @@ -0,0 +1,90 @@ +//! Handling of trait solver errors and converting them to errors `hir` can pass to `ide-diagnostics`. +//! +//! Note that we also have [`crate::next_solver::infer::errors`], which takes the raw [`NextSolverError`], +//! and converts it into [`FulfillmentError`] that contains more details. +//! +//! [`NextSolverError`]: crate::next_solver::fulfill::NextSolverError + +use macros::{TypeFoldable, TypeVisitable}; +use rustc_type_ir::{PredicatePolarity, inherent::IntoKind}; + +use crate::{ + Span, + next_solver::{ + ClauseKind, DbInterner, PredicateKind, StoredTraitRef, TraitPredicate, + infer::{ + errors::{FulfillmentError, FulfillmentErrorCode}, + select::SelectionError, + }, + }, +}; + +#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] +pub struct SolverDiagnostic { + pub span: Span, + pub kind: SolverDiagnosticKind, +} + +#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] +pub enum SolverDiagnosticKind { + TraitUnimplemented { + trait_predicate: StoredTraitPredicate, + root_trait_predicate: Option<StoredTraitPredicate>, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)] +pub struct StoredTraitPredicate { + pub trait_ref: StoredTraitRef, + pub polarity: PredicatePolarity, +} + +impl StoredTraitPredicate { + #[inline] + pub fn get<'db>(&'db self, interner: DbInterner<'db>) -> TraitPredicate<'db> { + TraitPredicate { polarity: self.polarity, trait_ref: self.trait_ref.get(interner) } + } +} + +impl SolverDiagnostic { + pub fn from_fulfillment_error(error: &FulfillmentError<'_>) -> Option<Self> { + let span = error.obligation.cause.span(); + if span.is_dummy() { + return None; + } + + // FIXME: Handle more error kinds. + let kind = match &error.code { + FulfillmentErrorCode::Select(SelectionError::Unimplemented) => { + match error.obligation.predicate.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => { + handle_trait_unimplemented(error, trait_pred)? + } + _ => return None, + } + } + _ => return None, + }; + Some(SolverDiagnostic { span, kind }) + } +} + +fn handle_trait_unimplemented<'db>( + error: &FulfillmentError<'db>, + trait_pred: TraitPredicate<'db>, +) -> Option<SolverDiagnosticKind> { + let trait_predicate = StoredTraitPredicate { + trait_ref: StoredTraitRef::new(trait_pred.trait_ref), + polarity: trait_pred.polarity, + }; + + let root_trait_predicate = match error.root_obligation.predicate.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => Some(StoredTraitPredicate { + trait_ref: StoredTraitRef::new(trait_pred.trait_ref), + polarity: trait_pred.polarity, + }), + _ => None, + }; + + Some(SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate }) +} diff --git a/crates/hir-ty/src/specialization.rs b/crates/hir-ty/src/specialization.rs index 8bc6c51fae..467b598447 100644 --- a/crates/hir-ty/src/specialization.rs +++ b/crates/hir-ty/src/specialization.rs @@ -1,17 +1,17 @@ //! Impl specialization related things use hir_def::{ - ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, nameres::crate_def_map, - signatures::ImplSignature, + ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, signatures::ImplSignature, + unstable_features::UnstableFeatures, }; -use intern::sym; use tracing::debug; use crate::{ + Span, db::HirDatabase, lower::GenericPredicates, next_solver::{ - DbInterner, TypingMode, + DbInterner, TypingMode, Unnormalized, infer::{DbInternerInferExt, traits::ObligationCause}, obligation_ctxt::ObligationCtxt, util::clauses_as_obligations, @@ -81,7 +81,7 @@ fn specializes_query( let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let specializing_impl_trait_ref = - db.impl_trait(specializing_impl_def_id).unwrap().instantiate_identity(); + db.impl_trait(specializing_impl_def_id).unwrap().instantiate_identity().skip_norm_wip(); let cause = &ObligationCause::dummy(); debug!( "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", @@ -92,11 +92,12 @@ fn specializes_query( let mut ocx = ObligationCtxt::new(&infcx); - let parent_args = infcx.fresh_args_for_item(parent_impl_def_id.into()); + let parent_args = infcx.fresh_args_for_item(Span::Dummy, parent_impl_def_id.into()); let parent_impl_trait_ref = db .impl_trait(parent_impl_def_id) .expect("expected source impl to be a trait impl") - .instantiate(interner, parent_args); + .instantiate(interner, parent_args) + .skip_norm_wip(); // do the impls unify? If not, no specialization. let Ok(()) = ocx.eq(cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref) @@ -109,8 +110,9 @@ fn specializes_query( // only be referenced via projection predicates. ocx.register_obligations(clauses_as_obligations( GenericPredicates::query_all(db, parent_impl_def_id.into()) - .iter_instantiated(interner, parent_args.as_slice()), - cause.clone(), + .iter_instantiated(interner, parent_args.as_slice()) + .map(Unnormalized::skip_norm_wip), + *cause, param_env, )); @@ -153,10 +155,8 @@ pub(crate) fn specializes( // `#[allow_internal_unstable(specialization)]`, but `#[allow_internal_unstable]` // is an internal feature, std is not using it for specialization nor is likely to // ever use it, and we don't have the span information necessary to replicate that. - let def_map = crate_def_map(db, module.krate(db)); - if !def_map.is_unstable_feature_enabled(&sym::specialization) - && !def_map.is_unstable_feature_enabled(&sym::min_specialization) - { + let features = UnstableFeatures::query(db, module.krate(db)); + if !features.specialization && !features.min_specialization { return false; } diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 430a570444..d259ce7963 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -16,8 +16,8 @@ mod traits; use base_db::{Crate, SourceDatabase}; use expect_test::Expect; use hir_def::{ - AssocItemId, DefWithBodyId, GenericDefId, HasModule, Lookup, ModuleDefId, ModuleId, - SyntheticSyntax, + AdtId, AssocItemId, DefWithBodyId, GenericDefId, HasModule, Lookup, ModuleDefId, ModuleId, + SyntheticSyntax, VariantId, expr_store::{Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap}, hir::{ExprId, Pat, PatId}, item_scope::ItemScope, @@ -36,9 +36,10 @@ use syntax::{ use test_fixture::WithFixture; use crate::{ - InferenceResult, + InferenceDiagnostic, InferenceResult, + db::{AnonConstId, HirDatabase}, display::{DisplayTarget, HirDisplay}, - infer::{Adjustment, TypeMismatch}, + infer::Adjustment, next_solver::Ty, setup_tracing, test_db::TestDB, @@ -88,15 +89,12 @@ fn check_impl( let file_range = FileRange { file_id, range }; if only_types { types.insert(file_range, expected); - } else if expected.starts_with("type: ") { - types.insert(file_range, expected.trim_start_matches("type: ").to_owned()); + } else if let Some(ty) = expected.strip_prefix("type: ") { + types.insert(file_range, ty.to_owned()); } else if expected.starts_with("expected") { mismatches.insert(file_range, expected); - } else if expected.starts_with("adjustments:") { - adjustments.insert( - file_range, - expected.trim_start_matches("adjustments:").trim().to_owned(), - ); + } else if let Some(adjs) = expected.strip_prefix("adjustments:") { + adjustments.insert(file_range, adjs.trim().to_owned()); } else { panic!("unexpected annotation: {expected} @ {range:?}"); } @@ -198,7 +196,14 @@ fn check_impl( } } - for (expr_or_pat, mismatch) in inference_result.type_mismatches() { + let type_mismatches = + inference_result.diagnostics().iter().filter_map(|diag| match diag { + InferenceDiagnostic::TypeMismatch { node, expected, found } => { + Some((*node, expected.as_ref(), found.as_ref())) + } + _ => None, + }); + for (expr_or_pat, expected, actual) in type_mismatches { let Some(node) = (match expr_or_pat { hir_def::hir::ExprOrPatId::ExprId(expr) => { expr_node(body_source_map, expr, &db) @@ -210,8 +215,8 @@ fn check_impl( let range = node.as_ref().original_file_range_rooted(&db); let actual = format!( "expected {}, got {}", - mismatch.expected.as_ref().display_test(&db, display_target), - mismatch.actual.as_ref().display_test(&db, display_target) + expected.display_test(&db, display_target), + actual.display_test(&db, display_target) ); match mismatches.remove(&range) { Some(annotation) => assert_eq!(actual, annotation), @@ -329,7 +334,17 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { krate: Crate| { let display_target = DisplayTarget::from_crate(&db, krate); let mut types: Vec<(InFile<SyntaxNode>, Ty<'_>)> = Vec::new(); - let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new(); + let type_mismatch_for_node = inference_result + .diagnostics() + .iter() + .filter_map(|diag| match diag { + InferenceDiagnostic::TypeMismatch { node, expected, found } => { + Some((*node, (expected.as_ref(), found.as_ref()))) + } + _ => None, + }) + .collect::<FxHashMap<_, _>>(); + let mut mismatches: Vec<(InFile<SyntaxNode>, (Ty<'_>, Ty<'_>))> = Vec::new(); if let Some((binding_id, syntax_ptr)) = self_param { let ty = &inference_result.type_of_binding[binding_id]; @@ -352,8 +367,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { Err(SyntheticSyntax) => continue, }; types.push((node.clone(), ty.as_ref())); - if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) { - mismatches.push((node, mismatch)); + if let Some(mismatch) = type_mismatch_for_node.get(&pat.into()) { + mismatches.push((node, *mismatch)); } } @@ -366,8 +381,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { Err(SyntheticSyntax) => continue, }; types.push((node.clone(), ty.as_ref())); - if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { - mismatches.push((node, mismatch)); + if let Some(mismatch) = type_mismatch_for_node.get(&expr.into()) { + mismatches.push((node, *mismatch)); } } @@ -398,7 +413,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let range = node.value.text_range(); (range.start(), range.end()) }); - for (src_ptr, mismatch) in &mismatches { + for (src_ptr, (expected, actual)) in &mismatches { let range = src_ptr.value.text_range(); let macro_prefix = if src_ptr.file_id != file_id { "!" } else { "" }; format_to!( @@ -406,8 +421,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { "{}{:?}: expected {}, got {}\n", macro_prefix, range, - mismatch.expected.as_ref().display_test(&db, display_target), - mismatch.actual.as_ref().display_test(&db, display_target), + expected.display_test(&db, display_target), + actual.display_test(&db, display_target), ); } } @@ -418,6 +433,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new(); let mut generic_defs: Vec<(GenericDefId, Crate)> = Vec::new(); + let mut variants: Vec<(VariantId, Crate)> = Vec::new(); visit_module(&db, def_map, module, &mut |it| { let krate = module.krate(&db); match it { @@ -438,6 +454,16 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { } ModuleDefId::AdtId(it) => { generic_defs.push((it.into(), krate)); + match it { + AdtId::StructId(id) => variants.push((id.into(), krate)), + AdtId::UnionId(id) => variants.push((id.into(), krate)), + AdtId::EnumId(id) => variants.extend( + id.enum_variants(&db) + .variants + .iter() + .map(|&(variant, ..)| (variant.into(), krate)), + ), + } } ModuleDefId::TraitId(it) => { generic_defs.push((it.into(), krate)); @@ -488,7 +514,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { for (def, krate) in defs { let (body, source_map) = Body::with_source_map(&db, def); let infer = InferenceResult::of(&db, def); - let self_param = body.self_param.map(|id| (id, source_map.self_param_syntax())); + let self_param = body.self_param().map(|id| (id, source_map.self_param_syntax())); infer_def(infer, body, source_map, self_param, krate); } @@ -497,11 +523,26 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { for (def, krate) in generic_defs { let (store, source_map) = ExpressionStore::with_source_map(&db, def.into()); // Skip if there are no const expressions in the signature - if store.const_expr_origins().is_empty() { + if store.expr_roots().next().is_none() { continue; } - let infer = InferenceResult::of(&db, def); - infer_def(infer, store, source_map, None, krate); + for &anon_const in AnonConstId::all_from_signature(&db, def).into_iter().flatten() { + let infer = InferenceResult::of(&db, anon_const); + infer_def(infer, store, source_map, None, krate); + } + } + variants.dedup(); + for (def, krate) in variants { + let (store, source_map) = ExpressionStore::with_source_map(&db, def.into()); + // Skip if there are no const expressions in the signature + if store.expr_roots().next().is_none() { + continue; + } + let anon_consts = db.field_types_with_diagnostics(def).defined_anon_consts(); + for &anon_const in anon_consts { + let infer = InferenceResult::of(&db, anon_const); + infer_def(infer, store, source_map, None, krate); + } } buf.truncate(buf.trim_end().len()); @@ -558,7 +599,7 @@ pub(crate) fn visit_module( let body = Body::of(db, it.into()); visit_body(db, body, cb); } - ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => { + ModuleDefId::AdtId(AdtId::EnumId(it)) => { it.enum_variants(db).variants.iter().for_each(|&(it, _, _)| { let body = Body::of(db, it.into()); cb(it.into()); diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs index 5324d8c605..3942a7ae27 100644 --- a/crates/hir-ty/src/tests/closure_captures.rs +++ b/crates/hir-ty/src/tests/closure_captures.rs @@ -6,7 +6,7 @@ use hir_def::{ }; use hir_expand::{HirFileId, files::InFileWrapper}; use itertools::Itertools; -use rustc_type_ir::inherent::{AdtDef as _, IntoKind}; +use rustc_type_ir::inherent::IntoKind; use span::{Edition, TextRange}; use stdx::{format_to, never}; use syntax::{AstNode, AstPtr}; @@ -42,7 +42,7 @@ fn display_place(db: &TestDB, store: &ExpressionStore, place: &Place, local: Bin match ty.kind() { TyKind::Tuple(_) => format_to!(result, ".{field_idx}"), TyKind::Adt(adt_def, _) => { - let variant = match adt_def.def_id().0 { + let variant = match adt_def.def_id() { AdtId::StructId(id) => VariantId::from(id), AdtId::UnionId(id) => id.into(), AdtId::EnumId(id) => { @@ -102,19 +102,19 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec // FIXME: Deduplicate this with hir::Local::sources(). let captured_local = capture.captured_local(); - let local_text_range = match body.self_param.zip(source_map.self_param_syntax()) + let local_text_range = if body.self_params.contains(&captured_local) + && let Some(source) = source_map.self_param_syntax() { - Some((param, source)) if param == captured_local => { - format!("{:?}", text_range(db, source)) - } - _ => source_map + format!("{:?}", text_range(db, source)) + } else { + source_map .patterns_for_binding(captured_local) .iter() .map(|&definition| { text_range(db, source_map.pat_syntax(definition).unwrap()) }) .map(|it| format!("{it:?}")) - .join(", "), + .join(", ") }; let place = display_place(db, body, &capture.place, captured_local); let capture_ty = capture @@ -600,3 +600,20 @@ fn f() { expect!["77..110;46..47;96..97 ByRef(Immutable) b &'<erased> i32"], ); } + +#[test] +fn fail_to_normalize_place() { + check_closure_captures( + r#" +//- minicore: index, slice +const FAIL_CONST: usize = loop {}; +struct Foo { + arr: [i32; FAIL_CONST], +} +fn foo(foo: &Foo) { + || { return foo.arr[0] }; +} + "#, + expect!["102..126;85..88;114..117 ByRef(Immutable) *foo &'<erased> Foo"], + ); +} diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index a80ce5002d..db06d55278 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs @@ -392,6 +392,34 @@ fn test() { } #[test] +fn gen_yield_coerce() { + check_no_mismatches( + r#" +fn test() { + let g = gen { + yield &1u32; + yield &&1u32; + }; +} + "#, + ); +} + +#[test] +fn async_gen_yield_coerce() { + check_no_mismatches( + r#" +fn test() { + let g = async gen { + yield &1u32; + yield &&1u32; + }; +} + "#, + ); +} + +#[test] fn assign_coerce() { check_no_mismatches( r" @@ -878,11 +906,11 @@ struct V<T> { t: T } fn main() { let a: V<&dyn Tr>; (a,) = V { t: &S }; - //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,) + //^^^^expected V<&'? S>, got ({unknown},) let mut a: V<&dyn Tr> = V { t: &S }; (a,) = V { t: &S }; - //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,) + //^^^^expected V<&'? S>, got ({unknown},) } "#, ); @@ -1008,3 +1036,36 @@ fn f() { "#, ); } + +#[test] +fn regression_22270() { + check_no_mismatches( + r#" +fn a() {} +fn b() {} + +fn foo<T, const N: usize>(x: [T; N]) -> Vec<T> { + loop {} +} + +fn bar() { + foo([a, b]); +} + "#, + ); +} + +#[test] +fn async_fn_ret() { + check_no_mismatches( + r#" +//- minicore: coerce_unsized, unsize, future, index, slice, range +async fn foo(a: &[i32]) -> &[i32] { + if true { + return &[]; + } + &a[..0] +} + "#, + ); +} diff --git a/crates/hir-ty/src/tests/diagnostics.rs b/crates/hir-ty/src/tests/diagnostics.rs index f257aa1b6e..94ca88088d 100644 --- a/crates/hir-ty/src/tests/diagnostics.rs +++ b/crates/hir-ty/src/tests/diagnostics.rs @@ -1,6 +1,4 @@ -use crate::tests::check_no_mismatches; - -use super::check; +use super::{check, check_no_mismatches}; #[test] fn function_return_type_mismatch_1() { diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs index 960155a8e4..4c26b19120 100644 --- a/crates/hir-ty/src/tests/incremental.rs +++ b/crates/hir-ty/src/tests/incremental.rs @@ -36,7 +36,7 @@ fn foo() -> i32 { "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "InferenceResult::for_body_", "FunctionSignature::of_", "FunctionSignature::with_source_map_", @@ -80,7 +80,7 @@ fn foo() -> i32 { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "AttrFlags::query_", "FunctionSignature::with_source_map_", "FunctionSignature::of_", @@ -125,7 +125,7 @@ fn baz() -> i32 { "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "InferenceResult::for_body_", "FunctionSignature::of_", "FunctionSignature::with_source_map_", @@ -196,7 +196,7 @@ fn baz() -> i32 { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "AttrFlags::query_", "FunctionSignature::with_source_map_", "FunctionSignature::of_", @@ -249,7 +249,7 @@ $0", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", @@ -286,7 +286,7 @@ pub struct NewStruct { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", @@ -324,7 +324,7 @@ $0", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", @@ -362,7 +362,7 @@ pub enum SomeEnum { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", @@ -400,7 +400,7 @@ $0", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", @@ -435,7 +435,7 @@ fn bar() -> f32 { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", @@ -477,7 +477,7 @@ $0", "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", @@ -520,7 +520,7 @@ impl SomeStruct { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", @@ -578,7 +578,7 @@ fn main() { "file_item_tree_query", "ast_id_map", "parse", - "real_span_map_shim", + "real_span_map", "TraitItems::query_with_diagnostics_", "Body::of_", "Body::with_source_map_", @@ -611,15 +611,14 @@ fn main() { "StructSignature::with_source_map_", "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", - "value_ty_query", "InherentImpls::for_crate_", - "callable_item_signature_query", + "callable_item_signature_with_diagnostics", "TraitImpls::for_crate_and_deps_", "TraitImpls::for_crate_", - "impl_trait_with_diagnostics_query", + "impl_trait_with_diagnostics", "ImplSignature::of_", "ImplSignature::with_source_map_", - "impl_self_ty_with_diagnostics_query", + "impl_self_ty_with_diagnostics", "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "body_upvars_mentioned", @@ -674,7 +673,7 @@ fn main() { "parse", "ast_id_map", "file_item_tree_query", - "real_span_map_shim", + "real_span_map", "crate_local_def_map", "TraitItems::query_with_diagnostics_", "Body::with_source_map_", @@ -703,12 +702,12 @@ fn main() { "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "InherentImpls::for_crate_", - "callable_item_signature_query", + "callable_item_signature_with_diagnostics", "TraitImpls::for_crate_", "ImplSignature::with_source_map_", "ImplSignature::of_", - "impl_trait_with_diagnostics_query", - "impl_self_ty_with_diagnostics_query", + "impl_trait_with_diagnostics", + "impl_self_ty_with_diagnostics", "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "body_upvars_mentioned", diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index fc8c1f8164..4291c9ba18 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs @@ -1193,7 +1193,7 @@ fn test() { 123..167 '{ ...o(); }': () 133..134 's': &'? S 137..151 'unsafe { f() }': &'? S - 146..147 'f': fn f() -> &'static S + 146..147 'f': extern "C" fn f() -> &'static S 146..149 'f()': &'static S 157..158 's': &'? S 157..164 's.foo()': bool diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs index 993293bb56..91273cd177 100644 --- a/crates/hir-ty/src/tests/never_type.rs +++ b/crates/hir-ty/src/tests/never_type.rs @@ -354,10 +354,10 @@ fn diverging_expression_3_break() { 11..85 '{ ...} }; }': () 54..55 'x': u32 63..82 '{ loop...k; } }': u32 - 65..80 'loop { break; }': () + 65..80 'loop { break; }': u32 70..80 '{ break; }': () 72..77 'break': ! - 65..80: expected u32, got () + 72..77: expected u32, got () 97..343 '{ ...; }; }': () 140..141 'x': u32 149..175 '{ for ...; }; }': u32 diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index d6bc03f57d..e719f43e74 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -133,8 +133,8 @@ fn infer_literal_pattern() { 55..72 'let "f... any()': bool 59..64 '"foo"': &'static str 59..64 '"foo"': &'static str - 67..70 'any': fn any<&'static str>() -> &'static str - 67..72 'any()': &'static str + 67..70 'any': fn any<&'? str>() -> &'? str + 67..72 'any()': &'? str 73..75 '{}': () 80..99 'if let...y() {}': () 83..96 'let 1 = any()': bool @@ -193,31 +193,27 @@ fn infer_literal_pattern() { fn infer_range_pattern() { check_infer_with_mismatches( r#" -//- minicore: range -fn test(x..y: &core::ops::Range<u32>) { +fn test() { if let 1..76 = 2u32 {} if let 1..=76 = 2u32 {} } "#, expect![[r#" - 8..9 'x': Range<u32> - 8..12 'x..y': Range<u32> - 11..12 'y': Range<u32> - 38..96 '{ ...2 {} }': () - 44..66 'if let...u32 {}': () - 47..63 'let 1....= 2u32': bool - 51..52 '1': u32 - 51..56 '1..76': u32 + 10..68 '{ ...2 {} }': () + 16..38 'if let...u32 {}': () + 19..35 'let 1....= 2u32': bool + 23..24 '1': u32 + 23..28 '1..76': u32 + 26..28 '76': u32 + 31..35 '2u32': u32 + 36..38 '{}': () + 43..66 'if let...u32 {}': () + 46..63 'let 1....= 2u32': bool + 50..51 '1': u32 + 50..56 '1..=76': u32 54..56 '76': u32 59..63 '2u32': u32 64..66 '{}': () - 71..94 'if let...u32 {}': () - 74..91 'let 1....= 2u32': bool - 78..79 '1': u32 - 78..84 '1..=76': u32 - 82..84 '76': u32 - 87..91 '2u32': u32 - 92..94 '{}': () "#]], ); check_no_mismatches( @@ -265,7 +261,6 @@ fn infer_pattern_match_ergonomics() { #[test] fn infer_pattern_match_ergonomics_ref() { - cov_mark::check!(match_ergonomics_ref); check_infer( r#" fn test() { @@ -409,7 +404,7 @@ fn infer_pattern_match_byte_string_literal() { 209..233 'if let...[S] {}': () 212..230 'let b"... &v[S]': bool 216..222 'b"foo"': &'static [u8] - 216..222 'b"foo"': &'static [u8] + 216..222 'b"foo"': &'static [u8; 3] 225..230 '&v[S]': &'? [u8] 226..227 'v': [u8; 3] 226..230 'v[S]': [u8] @@ -422,8 +417,6 @@ fn infer_pattern_match_byte_string_literal() { 254..256 '&v': &'? [u8; 3] 255..256 'v': [u8; 3] 257..259 '{}': () - 199..200 '3': usize - 62..63 'N': usize "#]], ); } @@ -825,6 +818,8 @@ fn box_pattern() { ); check_infer( r#" + #![feature(lang_items)] + #[lang = "owned_box"] pub struct Box<T>(T); @@ -835,12 +830,13 @@ fn box_pattern() { } "#, expect![[r#" - 52..58 'params': Box<i32> - 70..124 '{ ... } }': () - 76..122 'match ... }': () - 82..88 'params': Box<i32> - 99..110 'box integer': Box<i32> - 114..116 '{}': () + 77..83 'params': Box<i32> + 95..149 '{ ... } }': () + 101..147 'match ... }': () + 107..113 'params': Box<i32> + 124..135 'box integer': Box<i32> + 128..135 'integer': i32 + 139..141 '{}': () "#]], ); } @@ -1307,3 +1303,59 @@ fn bar() { "#, ); } + +#[test] +fn deref_pattern() { + check_infer( + r#" +//- minicore: deref_pat +use core::ops::{Deref, DerefPure}; + +#[lang = "owned_box"] +pub struct Box<T>(T); +impl<T> Deref for Box<T> { + type Target = T; + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl<T> DerefPure for Box<T> {} + +pub struct Foo<T>(T); +impl<T> Deref for Foo<T> { + type Target = [T]; + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl<T> DerefPure for Foo<T> {} + +fn foo(v: &Box<Foo<i32>>) { + match v { + deref!(deref!(inner)) => {} + _ => {} + } +} + "#, + expect![[r#" + 142..146 'self': &'? Box<T> + 165..188 '{ ... }': &'? T + 175..182 'loop {}': ! + 180..182 '{}': () + 310..314 'self': &'? Foo<T> + 333..356 '{ ... }': &'? [T] + 343..350 'loop {}': ! + 348..350 '{}': () + !0..20 'builti...inner)': Foo<i32> + !0..28 'builti...nner))': Box<Foo<i32>> + !14..19 'inner': &'? [i32] + 399..400 'v': &'? Box<Foo<i32>> + 418..493 '{ ... } }': () + 424..491 'match ... }': () + 430..431 'v': &'? Box<Foo<i32>> + 467..469 '{}': () + 478..479 '_': &'? Box<Foo<i32>> + 483..485 '{}': () + "#]], + ); +} diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index e30fa779da..5a90e700ac 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -2,6 +2,8 @@ mod new_solver; use expect_test::expect; +use crate::tests::check; + use super::{check_infer, check_no_mismatches, check_types}; #[test] @@ -1956,7 +1958,7 @@ fn main() { Alias::Braced; //^^^^^^^^^^^^^ {unknown} let Alias::Braced = loop {}; - //^^^^^^^^^^^^^ ! + //^^^^^^^^^^^^^ {unknown} let Alias::Braced(..) = loop {}; //^^^^^^^^^^^^^^^^^ Enum @@ -2013,18 +2015,20 @@ where #[test] fn tait_async_stack_overflow_17199() { - check_types( + // The error here is because we don't support TAITs. + check( r#" //- minicore: fmt, future type Foo = impl core::fmt::Debug; async fn foo() -> Foo { () + // ^^ expected impl Debug, got () } async fn test() { let t = foo().await; - // ^ impl Debug + // ^ type: impl Debug } "#, ); @@ -2234,7 +2238,7 @@ type Bar = impl Foo; async fn f<A, B, C>() -> Bar {} "#, expect![[r#" - 64..66 '{}': () + 64..66 '{}': impl Foo + ?Sized "#]], ); } @@ -2363,7 +2367,6 @@ fn test() { } "#, expect![[r#" - 46..49 'Foo': Foo<N> 93..97 'self': Foo<N> 108..125 '{ ... }': usize 118..119 'N': usize @@ -2755,7 +2758,6 @@ where 664..680 'filter...ter_fn': dyn Fn(&'? T) -> bool + 'static 691..698 'loop {}': ! 696..698 '{}': () - 512..513 'N': usize "#]], ); } diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs index 565360dc25..33a12fcd1e 100644 --- a/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/crates/hir-ty/src/tests/regression/new_solver.rs @@ -34,7 +34,6 @@ impl Space for [u8; 1] { 223..227 'iter': IntoIter<u8> 230..231 'a': Vec<u8> 230..243 'a.into_iter()': IntoIter<u8> - 322..323 '1': usize "#]], ); } @@ -473,8 +472,6 @@ fn foo() { 249..257 'to_bytes': fn to_bytes() -> [u8; _] 249..259 'to_bytes()': [u8; _] 249..268 'to_byt..._vec()': Vec<<[u8; _] as Foo>::Item> - 205..206 '_': usize - 156..157 'N': usize "#]], ); } @@ -516,15 +513,15 @@ fn test_at_most() { "#, expect![[r#" 48..49 '0': usize - 182..186 'self': Between<M, _, T> + 182..186 'self': Between<M, 0, T> 188..192 '_sep': &'? str - 200..206 '_other': Between<M, _, T> - 222..242 '{ ... }': Between<M, _, T> - 232..236 'self': Between<M, _, T> + 200..206 '_other': Between<M, 0, T> + 222..242 '{ ... }': Between<M, 0, T> + 232..236 'self': Between<M, 0, T> 300..304 'self': Self - 343..372 '{ ... }': Between<M, _, Self> - 353..360 'Between': fn Between<M, _, Self>(Self) -> Between<M, _, Self> - 353..366 'Between(self)': Between<M, _, Self> + 343..372 '{ ... }': Between<M, 0, Self> + 353..360 'Between': fn Between<M, 0, Self>(Self) -> Between<M, 0, Self> + 353..366 'Between(self)': Between<M, 0, Self> 361..365 'self': Self 404..408 'self': Self 433..462 '{ ... }': Between<0, N, Self> @@ -532,21 +529,22 @@ fn test_at_most() { 443..456 'Between(self)': Between<0, N, Self> 451..455 'self': Self 510..587 '{ ...um); }': () - 520..523 'num': Between<1, _, char> + 520..523 'num': Between<1, 0, char> 526..529 ''9'': char - 526..545 ''9'.at...:<1>()': Between<1, _, char> - 555..559 '_ver': Between<1, _, char> - 562..565 'num': Between<1, _, char> - 562..584 'num.se..., num)': Between<1, _, char> + 526..545 ''9'.at...:<1>()': Between<1, 0, char> + 541..542 '1': usize + 555..559 '_ver': Between<1, 0, char> + 562..565 'num': Between<1, 0, char> + 562..584 'num.se..., num)': Between<1, 0, char> 575..578 '"."': &'static str - 580..583 'num': Between<1, _, char> + 580..583 'num': Between<1, 0, char> 607..644 '{ ...>(); }': () 617..620 'num': Between<0, 1, char> 623..626 ''9'': char 623..641 ''9'.at...:<1>()': Between<0, 1, char> + 637..638 '1': usize 320..335 '{ Consts::MAX }': usize 322..333 'Consts::MAX': usize - 421..422 '0': i32 144..159 '{ Consts::MAX }': usize 146..157 'Consts::MAX': usize "#]], diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index 3ea21f8265..c0b8d93b47 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -1240,6 +1240,9 @@ fn infer_array() { 274..275 'x': [u8; 0] 287..289 '[]': [u8; 0] 299..300 'y': [u8; 4] + 307..308 '2': usize + 307..310 '2+2': usize + 309..310 '2': usize 314..323 '[1,2,3,4]': [u8; 4] 315..316 '1': u8 317..318 '2': u8 @@ -1811,8 +1814,6 @@ impl Foo for u8 { } #[test] -// FIXME -#[should_panic] fn const_eval_in_function_signature() { check_types( r#" @@ -2066,6 +2067,138 @@ fn test() { } #[test] +fn gen_block_types_inferred() { + check_infer( + r#" +//- minicore: iterator, deref +use core::iter::Iterator; + +fn test() { + let mut generator = gen { + yield 1i8; + }; + let result = generator.next(); +} + "#, + expect![[r#" + 37..131 '{ ...t(); }': () + 47..60 'mut generator': impl Iterator<Item = i8> + 63..93 'gen { ... }': impl Iterator<Item = i8> + 77..86 'yield 1i8': () + 83..86 '1i8': i8 + 103..109 'result': Option<i8> + 112..121 'generator': impl Iterator<Item = i8> + 112..128 'genera...next()': Option<i8> + "#]], + ); +} + +#[test] +fn async_gen_block_types_inferred() { + check_infer( + r#" +//- minicore: async_iterator, option, future, deref, pin +use core::async_iter::AsyncIterator; +use core::pin::Pin; +use core::task::Context; + +fn test(mut cx: Context<'_>) { + let mut generator = async gen { + yield 1i8; + }; + let result = Pin::new(&mut generator).poll_next(&mut cx); +} + "#, + expect![[r#" + 91..97 'mut cx': Context<'?> + 112..239 '{ ...cx); }': () + 122..135 'mut generator': impl AsyncIterator<Item = {unknown}> + 138..174 'async ... }': impl AsyncIterator<Item = {unknown}> + 158..167 'yield 1i8': () + 164..167 '1i8': i8 + 184..190 'result': Poll<Option<{unknown}>> + 193..201 'Pin::new': fn new<&'? mut impl AsyncIterator<Item = {unknown}>>(&'? mut impl AsyncIterator<Item = {unknown}>) -> Pin<&'? mut impl AsyncIterator<Item = {unknown}>> + 193..217 'Pin::n...rator)': Pin<&'? mut impl AsyncIterator<Item = {unknown}>> + 193..236 'Pin::n...ut cx)': Poll<Option<{unknown}>> + 202..216 '&mut generator': &'? mut impl AsyncIterator<Item = {unknown}> + 207..216 'generator': impl AsyncIterator<Item = {unknown}> + 228..235 '&mut cx': &'? mut Context<'?> + 233..235 'cx': Context<'?> + "#]], + ); +} + +#[test] +fn gen_fn_types_inferred() { + check_infer( + r#" +//- minicore: iterator, deref +use core::iter::Iterator; + +gen fn html() { + yield (); +} + +fn test() { + let mut generator = html(); + let result = generator.next(); +} + "#, + expect![[r#" + 41..58 '{ ... (); }': () + 47..55 'yield ()': () + 53..55 '()': () + 70..140 '{ ...t(); }': () + 80..93 'mut generator': impl Iterator<Item = ()> + 96..100 'html': fn html() -> impl Iterator<Item = ()> + 96..102 'html()': impl Iterator<Item = ()> + 112..118 'result': Option<()> + 121..130 'generator': impl Iterator<Item = ()> + 121..137 'genera...next()': Option<()> + "#]], + ); +} + +#[test] +fn async_gen_fn_types_inferred() { + check_infer( + r#" +//- minicore: async_iterator, option, future, deref, pin +use core::async_iter::AsyncIterator; +use core::pin::Pin; +use core::task::Context; + +async gen fn html() { + yield (); +} + +fn test(mut cx: Context<'_>) { + let mut generator = html(); + let result = Pin::new(&mut generator).poll_next(&mut cx); +} + "#, + expect![[r#" + 103..120 '{ ... (); }': () + 109..117 'yield ()': () + 115..117 '()': () + 130..136 'mut cx': Context<'?> + 151..248 '{ ...cx); }': () + 161..174 'mut generator': impl AsyncIterator<Item = ()> + 177..181 'html': fn html() -> impl AsyncIterator<Item = ()> + 177..183 'html()': impl AsyncIterator<Item = ()> + 193..199 'result': Poll<Option<()>> + 202..210 'Pin::new': fn new<&'? mut impl AsyncIterator<Item = ()>>(&'? mut impl AsyncIterator<Item = ()>) -> Pin<&'? mut impl AsyncIterator<Item = ()>> + 202..226 'Pin::n...rator)': Pin<&'? mut impl AsyncIterator<Item = ()>> + 202..245 'Pin::n...ut cx)': Poll<Option<()>> + 211..225 '&mut generator': &'? mut impl AsyncIterator<Item = ()> + 216..225 'generator': impl AsyncIterator<Item = ()> + 237..244 '&mut cx': &'? mut Context<'?> + 242..244 'cx': Context<'?> + "#]], + ); +} + +#[test] fn tuple_pattern_nested_match_ergonomics() { check_no_mismatches( r#" @@ -2271,6 +2404,7 @@ fn infer_generic_from_later_assignment() { 89..127 'loop {... }': ! 94..127 '{ ... }': () 104..107 'end': Option<bool> + 104..107 'end': Option<bool> 104..120 'end = ...(true)': () 110..114 'Some': fn Some<bool>(bool) -> Option<bool> 110..120 'Some(true)': Option<bool> @@ -2563,7 +2697,6 @@ fn generic_default_in_struct_literal() { #[test] fn generic_default_depending_on_other_type_arg() { - // FIXME: the {unknown} is a bug check_infer( r#" struct Thing<T = u128, F = fn() -> T> { t: T } @@ -2580,7 +2713,7 @@ fn generic_default_depending_on_other_type_arg() { 83..130 '{ ...2 }; }': () 89..91 't1': Thing<u32, fn() -> u32> 97..99 't2': Thing<u128, fn() -> u128> - 105..127 'Thing:...1u32 }': Thing<u32, fn() -> {unknown}> + 105..127 'Thing:...1u32 }': Thing<u32, fn() -> u32> 121..125 '1u32': u32 "#]], ); @@ -3459,15 +3592,13 @@ struct TS(usize); fn main() { let x; [x,] = &[1,]; - //^^^^expected &'? [i32; 1], got [{unknown}] let x; [(x,),] = &[(1,),]; - //^^^^^^^expected &'? [(i32,); 1], got [{unknown}] let x; ((x,),) = &((1,),); - //^^^^^^^expected &'? ((i32,),), got (({unknown},),) + //^^^^^^^expected &'? ((i32,),), got ({unknown},) let x; (x,) = &(1,); @@ -3475,7 +3606,7 @@ fn main() { let x; (S { a: x },) = &(S { a: 42 },); - //^^^^^^^^^^^^^expected &'? (S,), got (S,) + //^^^^^^^^^^^^^expected &'? (S,), got ({unknown},) let x; S { a: x } = &S { a: 42 }; @@ -3868,8 +3999,6 @@ fn main() { 208..209 'c': u8 213..214 'a': A 213..221 'a.into()': [u8; 2] - 33..34 '2': usize - 111..112 '3': usize "#]], ); } @@ -3901,6 +4030,7 @@ fn main() { 100..147 'async_... })': () 114..146 'async ... }': impl AsyncFnOnce(i32) 121..124 'arg': i32 + 121..124 'arg': i32 126..146 '{ ... }': () 136..139 'arg': i32 153..160 'closure': fn closure<impl FnOnce(i32)>(impl FnOnce(i32)) @@ -4055,14 +4185,14 @@ fn foo() { 130..153 '{ ... }': &'? T 140..147 'loop {}': ! 145..147 '{}': () - 207..220 'LazyLock::new': fn new<[u32; _]>() -> LazyLock<[u32; _]> - 207..222 'LazyLock::new()': LazyLock<[u32; _]> + 207..220 'LazyLock::new': fn new<[u32; 0]>() -> LazyLock<[u32; 0]> + 207..222 'LazyLock::new()': LazyLock<[u32; 0]> 234..285 '{ ...CK); }': () - 244..245 '_': &'? [u32; _] - 248..263 'LazyLock::force': fn force<[u32; _]>(&'? LazyLock<[u32; _]>) -> &'? [u32; _] - 248..282 'LazyLo..._LOCK)': &'? [u32; _] - 264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; _]> - 265..281 'VALUES...Y_LOCK': LazyLock<[u32; _]> + 244..245 '_': &'? [u32; 0] + 248..263 'LazyLock::force': fn force<[u32; 0]>(&'? LazyLock<[u32; 0]>) -> &'? [u32; 0] + 248..282 'LazyLo..._LOCK)': &'? [u32; 0] + 264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; 0]> + 265..281 'VALUES...Y_LOCK': LazyLock<[u32; 0]> 197..202 '{ 0 }': usize 199..200 '0': usize "#]], @@ -4140,11 +4270,38 @@ union U { "#, expect![[r#" 242..243 '0': isize - 46..47 '2': i32 - 65..68 '0.0': f32 - 90..91 '2': i32 - 200..201 '0': i32 - 212..213 '0': i32 + 111..125 '{ C as usize }': usize + 113..114 'C': f32 + 113..123 'C as usize': usize + "#]], + ); +} + +#[test] +fn async_closure_with_params() { + check_no_mismatches( + r#" +fn foo() { + let capture = false; + async move |param: i32| { + capture; + }; +} + "#, + ); +} + +#[test] +fn enum_variant_anon_const() { + check_infer( + r#" +enum Enum { + Variant([(); { 2 }]), +} + "#, + expect![[r#" + 29..34 '{ 2 }': usize + 31..32 '2': usize "#]], ); } diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 278666ef35..ea978cde58 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -124,6 +124,25 @@ async fn test() { } #[test] +fn infer_async_gen_closure() { + check( + r#" +//- minicore: async_iterator, fn, add, builtin_impls +//- /main.rs edition:2024 +fn test() { + let f = async gen move |x: i32| { + yield x + 42; + //^^^^^^ expected Poll<Option<{unknown}>>, got i32 + }; + let a = f(4); + a; +// ^ type: impl AsyncIterator<Item = {unknown}> +} +"#, + ); +} + +#[test] fn auto_sized_async_block() { check_no_mismatches( r#" @@ -1271,7 +1290,6 @@ fn bar() { 241..245 'R::B': fn B<(), i32>(i32) -> R<(), i32> 241..248 'R::B(7)': R<(), i32> 246..247 '7': i32 - 46..47 '2': usize "#]], ); } @@ -3783,8 +3801,6 @@ fn main() { 371..373 'v4': usize 376..378 'v3': [u8; 4] 376..389 'v3.do_thing()': usize - 86..87 '4': usize - 192..193 '2': usize "#]], ) } @@ -3824,9 +3840,6 @@ fn main() { 240..242 'v2': [u8; 2] 245..246 'v': [u8; 2] 245..257 'v.do_thing()': [u8; 2] - 130..131 'L': usize - 102..103 'L': usize - 130..131 'L': usize "#]], ) } @@ -4911,6 +4924,7 @@ async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) { "#, expect![[r#" 37..38 'a': T + 37..38 'a': T 43..83 '{ ...ait; }': () 53..57 'fut1': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?> 60..61 'a': T @@ -4919,6 +4933,7 @@ async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) { 70..74 'fut1': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?> 70..80 'fut1.await': i32 124..129 'mut b': T + 124..129 'mut b': T 134..174 '{ ...ait; }': () 144..148 'fut2': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?> 151..152 'b': T @@ -4927,6 +4942,7 @@ async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) { 161..165 'fut2': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?> 161..171 'fut2.await': i32 216..217 'c': T + 216..217 'c': T 222..262 '{ ...ait; }': () 232..236 'fut3': <T as AsyncFnOnce<(u32,)>>::CallOnceFuture 239..240 'c': T diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 878696c721..ad1c3fb709 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -15,18 +15,16 @@ use hir_def::{ }; use hir_expand::name::Name; use intern::sym; -use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt}; use rustc_type_ir::{ TypingMode, - inherent::{AdtDef, BoundExistentialPredicates, IntoKind, Span as _}, - solve::Certainty, + inherent::{BoundExistentialPredicates, IntoKind}, }; use crate::{ + Span, db::HirDatabase, next_solver::{ - Canonical, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, SolverContext, Span, - StoredClauses, Ty, TyKind, + DbInterner, GenericArgs, ParamEnv, StoredClauses, Ty, TyKind, infer::{ DbInternerInferExt, InferCtxt, traits::{Obligation, ObligationCause}, @@ -79,91 +77,6 @@ pub fn structurally_normalize_ty<'db>( ty.replace_infer_with_error(infcx.interner) } -#[derive(Clone, Debug, PartialEq)] -pub enum NextTraitSolveResult { - Certain, - Uncertain, - NoSolution, -} - -impl NextTraitSolveResult { - pub fn no_solution(&self) -> bool { - matches!(self, NextTraitSolveResult::NoSolution) - } - - pub fn certain(&self) -> bool { - matches!(self, NextTraitSolveResult::Certain) - } - - pub fn uncertain(&self) -> bool { - matches!(self, NextTraitSolveResult::Uncertain) - } -} - -pub fn next_trait_solve_canonical_in_ctxt<'db>( - infer_ctxt: &InferCtxt<'db>, - goal: Canonical<'db, Goal<'db, Predicate<'db>>>, -) -> NextTraitSolveResult { - infer_ctxt.probe(|_| { - let context = <&SolverContext<'db>>::from(infer_ctxt); - - tracing::info!(?goal); - - let (goal, var_values) = context.instantiate_canonical(&goal); - tracing::info!(?var_values); - - let res = context.evaluate_root_goal(goal, Span::dummy(), None); - - let obligation = Obligation { - cause: ObligationCause::dummy(), - param_env: goal.param_env, - recursion_depth: 0, - predicate: goal.predicate, - }; - infer_ctxt.inspect_evaluated_obligation(&obligation, &res, || { - Some(context.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1) - }); - - let res = res.map(|r| (r.has_changed, r.certainty)); - - tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); - - match res { - Err(_) => NextTraitSolveResult::NoSolution, - Ok((_, Certainty::Yes)) => NextTraitSolveResult::Certain, - Ok((_, Certainty::Maybe { .. })) => NextTraitSolveResult::Uncertain, - } - }) -} - -/// Solve a trait goal using next trait solver. -pub fn next_trait_solve_in_ctxt<'db, 'a>( - infer_ctxt: &'a InferCtxt<'db>, - goal: Goal<'db, Predicate<'db>>, -) -> Result<(HasChanged, Certainty), rustc_type_ir::solve::NoSolution> { - tracing::info!(?goal); - - let context = <&SolverContext<'db>>::from(infer_ctxt); - - let res = context.evaluate_root_goal(goal, Span::dummy(), None); - - let obligation = Obligation { - cause: ObligationCause::dummy(), - param_env: goal.param_env, - recursion_depth: 0, - predicate: goal.predicate, - }; - infer_ctxt.inspect_evaluated_obligation(&obligation, &res, || { - Some(context.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1) - }); - - let res = res.map(|r| (r.has_changed, r.certainty)); - - tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); - - res -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Update)] pub enum FnTrait { // Warning: Order is important. If something implements `x` it should also implement @@ -209,7 +122,7 @@ pub fn implements_trait_unique<'db>( trait_: TraitId, ) -> bool { implements_trait_unique_impl(db, env, trait_, &mut |infcx| { - infcx.fill_rest_fresh_args(trait_.into(), [ty.into()]) + infcx.fill_rest_fresh_args(Span::Dummy, trait_.into(), [ty.into()]) }) } @@ -235,14 +148,13 @@ fn implements_trait_unique_impl<'db>( let args = create_args(&infcx); let trait_ref = rustc_type_ir::TraitRef::new_from_args(interner, trait_.into(), args); - let goal = Goal::new(interner, env.param_env, trait_ref); - let result = crate::traits::next_trait_solve_in_ctxt(&infcx, goal); - matches!(result, Ok((_, Certainty::Yes))) + let obligation = Obligation::new(interner, ObligationCause::dummy(), env.param_env, trait_ref); + infcx.predicate_must_hold_modulo_regions(&obligation) } pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id: ImplId) -> bool { - let self_ty = db.impl_self_ty(impl_id).instantiate_identity(); + let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); let self_ty = self_ty.kind(); let impl_allowed = match self_ty { TyKind::Tuple(_) @@ -259,7 +171,7 @@ pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id | TyKind::Uint(_) | TyKind::Float(_) => def_map.is_rustc_coherence_is_core(), - TyKind::Adt(adt_def, _) => adt_def.def_id().0.module(db).krate(db) == def_map.krate(), + TyKind::Adt(adt_def, _) => adt_def.def_id().module(db).krate(db) == def_map.krate(), TyKind::Dynamic(it, _) => it .principal_def_id() .is_some_and(|trait_id| trait_id.0.module(db).krate(db) == def_map.krate()), @@ -282,7 +194,7 @@ pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id | TyKind::Uint(_) | TyKind::Float(_) => true, - TyKind::Adt(adt_def, _) => match adt_def.def_id().0 { + TyKind::Adt(adt_def, _) => match adt_def.def_id() { hir_def::AdtId::StructId(id) => StructSignature::of(db, id) .flags .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS), @@ -334,7 +246,7 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool let local_crate = impl_.lookup(db).container.krate(db); let is_local = |tgt_crate| tgt_crate == local_crate; - let trait_ref = impl_trait.instantiate_identity(); + let trait_ref = impl_trait.instantiate_identity().skip_norm_wip(); let trait_id = trait_ref.def_id.0; if is_local(trait_id.module(db).krate(db)) { // trait to be implemented is local @@ -347,7 +259,7 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool match ty.kind() { TyKind::Ref(_, referenced, _) => ty = referenced, TyKind::Adt(adt_def, subs) => { - let AdtId::StructId(s) = adt_def.def_id().0 else { + let AdtId::StructId(s) = adt_def.def_id() else { break ty; }; let struct_signature = StructSignature::of(db, s); @@ -370,7 +282,7 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool // FIXME: param coverage // - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`) let is_not_orphan = trait_ref.args.types().any(|ty| match unwrap_fundamental(ty).kind() { - TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().0.module(db).krate(db)), + TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().module(db).krate(db)), TyKind::Error(_) => true, TyKind::Dynamic(it, _) => { it.principal_def_id().is_some_and(|trait_id| is_local(trait_id.0.module(db).krate(db))) diff --git a/crates/hir-ty/src/upvars.rs b/crates/hir-ty/src/upvars.rs index 48f3c803d8..6dcd8b59a5 100644 --- a/crates/hir-ty/src/upvars.rs +++ b/crates/hir-ty/src/upvars.rs @@ -3,7 +3,7 @@ use hir_def::{ DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId, expr_store::{ExpressionStore, path::Path}, - hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, + hir::{BindingId, Expr, ExprId, ExprOrPatId}, resolver::{HasResolver, Resolver, ValueNs}, }; use hir_expand::mod_path::PathKind; @@ -110,10 +110,7 @@ pub fn upvars_mentioned_impl( owner: ExpressionStoreOwnerId, ) -> Option<Box<FxHashMap<ExprId, Upvars>>> { let store = ExpressionStore::of(db, owner); - if store.const_expr_origins().is_empty() { - // Save constructing a Resolver. - return None; - } + store.expr_roots().next()?; let mut resolver = owner.resolver(db); let mut result = FxHashMap::default(); for root_expr in store.expr_roots() { @@ -181,22 +178,6 @@ pub fn upvars_mentioned_impl( path, ); } - &Expr::Assignment { target, .. } => { - body.walk_pats(target, &mut |pat| { - let Pat::Path(path) = &body[pat] else { return }; - resolve_maybe_upvar( - db, - resolver, - owner, - body, - current_closure, - expr, - pat.into(), - upvars, - path, - ); - }); - } &Expr::Closure { body: body_expr, .. } => { let mut closure_upvars = FxHashSet::default(); handle_expr_inside_closure( diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index ae9b2c4618..764d02ccf1 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -5,10 +5,9 @@ use std::iter::Enumerate; use base_db::target::{self, TargetData}; use hir_def::{ - EnumId, EnumVariantId, FunctionId, Lookup, TraitId, attrs::AttrFlags, lang_item::LangItems, + EnumId, EnumVariantId, FunctionId, Lookup, TraitId, lang_item::LangItems, signatures::FunctionSignature, }; -use intern::sym; use rustc_abi::TargetDataLayout; use span::Edition; @@ -105,21 +104,10 @@ pub fn is_fn_unsafe_to_call( let loc = func.lookup(db); match loc.container { - hir_def::ItemContainerId::ExternBlockId(block) => { - let is_intrinsic_block = block.abi(db) == Some(sym::rust_dash_intrinsic); - if is_intrinsic_block { - // legacy intrinsics - // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute - if AttrFlags::query(db, func.into()).contains(AttrFlags::RUSTC_SAFE_INTRINSIC) { - Unsafety::Safe - } else { - Unsafety::Unsafe - } - } else { - // Function in an `extern` block are always unsafe to call, except when - // it is marked as `safe`. - if data.is_safe() { Unsafety::Safe } else { Unsafety::Unsafe } - } + hir_def::ItemContainerId::ExternBlockId(_) => { + // Function in an `extern` block are always unsafe to call, except when + // it is marked as `safe`. + if data.is_safe() { Unsafety::Safe } else { Unsafety::Unsafe } } _ => Unsafety::Safe, } diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs index a88457e3c7..7eee78b8c4 100644 --- a/crates/hir-ty/src/variance.rs +++ b/crates/hir-ty/src/variance.rs @@ -18,10 +18,7 @@ use hir_def::{ signatures::{StructFlags, StructSignature}, }; use rustc_ast_ir::Mutability; -use rustc_type_ir::{ - Variance, - inherent::{AdtDef, IntoKind}, -}; +use rustc_type_ir::{Variance, inherent::IntoKind}; use stdx::never; use crate::{ @@ -129,7 +126,7 @@ impl<'db> Context<'db> { let mut add_constraints_from_variant = |variant| { for (_, field) in db.field_types(variant).iter() { self.add_constraints_from_ty( - field.get().instantiate_identity(), + field.get().instantiate_identity().skip_norm_wip(), Variance::Covariant, ); } @@ -214,7 +211,7 @@ impl<'db> Context<'db> { } } TyKind::Adt(def, args) => { - self.add_constraints_from_args(def.def_id().0.into(), args, variance); + self.add_constraints_from_args(def.def_id().into(), args, variance); } TyKind::Alias(alias) => { // FIXME: Probably not correct wrt. opaques. @@ -479,7 +476,6 @@ struct Other<'a> { #[test] fn rustc_test_variance_associated_consts() { - // FIXME: Should be invariant check( r#" trait Trait { @@ -491,7 +487,7 @@ struct Foo<T: Trait> { //~ ERROR [T: o] } "#, expect![[r#" - Foo[T: bivariant] + Foo[T: invariant] "#]], ); } diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 223103b6e5..f9cf05e73a 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -18,9 +18,7 @@ use hir_expand::{ }; use hir_ty::{ db::HirDatabase, - method_resolution::{ - self, CandidateId, MethodError, MethodResolutionContext, MethodResolutionUnstableFeatures, - }, + method_resolution::{self, CandidateId, MethodError, MethodResolutionContext}, next_solver::{DbInterner, TypingMode, infer::DbInternerInferExt}, }; use intern::Symbol; @@ -498,15 +496,16 @@ fn resolve_impl_trait_item<'db>( // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) let interner = DbInterner::new_with(db, environment.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let unstable_features = - MethodResolutionUnstableFeatures::from_def_map(resolver.top_level_def_map()); + let features = resolver.top_level_def_map().features(); let ctx = MethodResolutionContext { infcx: &infcx, resolver: &resolver, param_env: environment.param_env, traits_in_scope: &traits_in_scope, edition: krate.edition(db), - unstable_features: &unstable_features, + features, + call_span: hir_ty::Span::Dummy, + receiver_span: hir_ty::Span::Dummy, }; let resolution = ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty); let resolution = match resolution { diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 6cfb79d5a1..a044f24587 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -6,7 +6,7 @@ use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_def::{ - DefWithBodyId, GenericParamId, SyntheticSyntax, + DefWithBodyId, GenericParamId, HasModule, SyntheticSyntax, expr_store::{ ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast, hir_generic_arg_to_ast, hir_segment_to_ast_segment, @@ -15,11 +15,15 @@ use hir_def::{ }; use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name}; use hir_ty::{ - CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource, - PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, ParamEnvAndCrate, + PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, + display::{DisplayTarget, HirDisplay}, + next_solver::DbInterner, + solver_errors::SolverDiagnosticKind, }; +use stdx::{impl_from, never}; use syntax::{ AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange, ast::{self, HasGenericArgs}, @@ -27,7 +31,7 @@ use syntax::{ }; use triomphe::Arc; -use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type}; +use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, Variant}; pub use hir_def::VariantId; pub use hir_ty::{ @@ -35,6 +39,49 @@ pub use hir_ty::{ diagnostics::{CaseType, IncorrectCase}, }; +#[derive(Debug, Clone)] +pub enum SpanAst { + Expr(ast::Expr), + Pat(ast::Pat), + Type(ast::Type), +} +const _: () = { + use syntax::ast::*; + impl_from!(Expr, Pat, Type for SpanAst); +}; + +impl From<Either<ast::Expr, ast::Pat>> for SpanAst { + fn from(value: Either<ast::Expr, ast::Pat>) -> Self { + match value { + Either::Left(it) => it.into(), + Either::Right(it) => it.into(), + } + } +} + +impl ast::AstNode for SpanAst { + fn can_cast(kind: syntax::SyntaxKind) -> bool { + ast::Expr::can_cast(kind) || ast::Pat::can_cast(kind) || ast::Type::can_cast(kind) + } + + fn cast(syntax: syntax::SyntaxNode) -> Option<Self> { + ast::Expr::cast(syntax.clone()) + .map(SpanAst::Expr) + .or_else(|| ast::Pat::cast(syntax.clone()).map(SpanAst::Pat)) + .or_else(|| ast::Type::cast(syntax).map(SpanAst::Type)) + } + + fn syntax(&self) -> &syntax::SyntaxNode { + match self { + SpanAst::Expr(it) => it.syntax(), + SpanAst::Pat(it) => it.syntax(), + SpanAst::Type(it) => it.syntax(), + } + } +} + +pub type SpanSyntax = InFile<AstPtr<SpanAst>>; + macro_rules! diagnostics { ($AnyDiagnostic:ident <$db:lifetime> -> $($diag:ident $(<$lt:lifetime>)?,)*) => { #[derive(Debug)] @@ -56,12 +103,18 @@ diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, BreakOutsideOfLoop, CastToUnsized<'db>, + ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, + FunctionalRecordUpdateOnNonStruct, + GenericDefaultRefersToSelf, InactiveCode, IncoherentImpl, IncorrectCase, + IncorrectGenericsLen, + IncorrectGenericsOrder, InvalidCast<'db>, InvalidDeriveTarget, + InvalidLhsOfAssignment, MacroDefError, MacroError, MacroExpansionParseError, @@ -74,11 +127,16 @@ diagnostics![AnyDiagnostic<'db> -> MovedOutOfRef<'db>, NeedMut, NonExhaustiveLet, + NonExhaustiveRecordExpr, NoSuchField, + MismatchedArrayPatLen, + DuplicateField, + PatternArgInExternFn, PrivateAssocItem, PrivateField, RemoveTrailingReturn, RemoveUnnecessaryElse, + UnusedMustUse<'db>, ReplaceFilterMapNextWithFindMap, TraitImplIncorrectSafety, TraitImplMissingAssocItems, @@ -102,10 +160,11 @@ diagnostics![AnyDiagnostic<'db> -> GenericArgsProhibited, ParenthesizedGenericArgsWithoutFnTrait, BadRtn, - IncorrectGenericsLen, - IncorrectGenericsOrder, MissingLifetime, ElidedLifetimesInPath, + TypeMustBeKnown<'db>, + UnionExprMustHaveExactlyOneField, + UnimplementedTrait<'db>, ]; #[derive(Debug)] @@ -212,6 +271,12 @@ pub struct NoSuchField { } #[derive(Debug)] +pub struct DuplicateField { + pub field: InFile<AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>>, + pub variant: Variant, +} + +#[derive(Debug)] pub struct PrivateAssocItem { pub expr_or_pat: InFile<ExprOrPatPtr>, pub item: AssocItem, @@ -225,12 +290,31 @@ pub struct MismatchedTupleStructPatArgCount { } #[derive(Debug)] +pub struct MismatchedArrayPatLen { + pub pat: InFile<ExprOrPatPtr>, + pub expected: u128, + pub found: u128, + pub has_rest: bool, +} + +#[derive(Debug)] +pub struct ExpectedArrayOrSlicePat<'db> { + pub pat: InFile<ExprOrPatPtr>, + pub found: Type<'db>, +} + +#[derive(Debug)] pub struct ExpectedFunction<'db> { pub call: InFile<ExprOrPatPtr>, pub found: Type<'db>, } #[derive(Debug)] +pub struct FunctionalRecordUpdateOnNonStruct { + pub base_expr: InFile<ExprOrPatPtr>, +} + +#[derive(Debug)] pub struct UnresolvedField<'db> { pub expr: InFile<ExprOrPatPtr>, pub receiver: Type<'db>, @@ -312,6 +396,11 @@ pub struct NonExhaustiveLet { } #[derive(Debug)] +pub struct NonExhaustiveRecordExpr { + pub expr: InFile<ExprOrPatPtr>, +} + +#[derive(Debug)] pub struct TypeMismatch<'db> { pub expr_or_pat: InFile<ExprOrPatPtr>, pub expected: Type<'db>, @@ -386,6 +475,12 @@ pub struct RemoveUnnecessaryElse { } #[derive(Debug)] +pub struct UnusedMustUse<'db> { + pub expr: InFile<ExprOrPatPtr>, + pub message: Option<&'db str>, +} + +#[derive(Debug)] pub struct CastToUnsized<'db> { pub expr: InFile<ExprOrPatPtr>, pub cast_ty: Type<'db>, @@ -442,6 +537,12 @@ pub struct ElidedLifetimesInPath { pub hard_error: bool, } +#[derive(Debug)] +pub struct TypeMustBeKnown<'db> { + pub at_point: SpanSyntax, + pub top_term: Option<Either<Type<'db>, String>>, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GenericArgKind { Lifetime, @@ -465,10 +566,38 @@ pub struct IncorrectGenericsOrder { pub expected_kind: GenericArgKind, } +#[derive(Debug)] +pub struct GenericDefaultRefersToSelf { + /// The `Self` segment. + pub segment: InFile<AstPtr<ast::PathSegment>>, +} + +#[derive(Debug)] +pub struct UnionExprMustHaveExactlyOneField { + pub expr: InFile<ExprOrPatPtr>, +} + +#[derive(Debug)] +pub struct InvalidLhsOfAssignment { + pub lhs: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>, +} + +#[derive(Debug)] +pub struct PatternArgInExternFn { + pub node: InFile<AstPtr<ast::Pat>>, +} + +#[derive(Debug)] +pub struct UnimplementedTrait<'db> { + pub span: SpanSyntax, + pub trait_predicate: crate::TraitPredicate<'db>, + pub root_trait_predicate: Option<crate::TraitPredicate<'db>>, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, - diagnostic: BodyValidationDiagnostic, + diagnostic: BodyValidationDiagnostic<'db>, source_map: &hir_def::expr_store::BodySourceMap, ) -> Option<AnyDiagnostic<'db>> { match diagnostic { @@ -536,59 +665,47 @@ impl<'db> AnyDiagnostic<'db> { } } BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => { - match source_map.expr_syntax(match_expr) { - Ok(source_ptr) => { - let root = source_ptr.file_syntax(db); - if let Either::Left(ast::Expr::MatchExpr(match_expr)) = - &source_ptr.value.to_node(&root) - { - match match_expr.expr() { - Some(scrut_expr) if match_expr.match_arm_list().is_some() => { - return Some( - MissingMatchArms { - scrutinee_expr: InFile::new( - source_ptr.file_id, - AstPtr::new(&scrut_expr), - ), - uncovered_patterns, - } - .into(), - ); - } - _ => {} - } + if let Ok(source_ptr) = source_map.expr_syntax(match_expr) + && let root = source_ptr.file_syntax(db) + && let Either::Left(ast::Expr::MatchExpr(match_expr)) = + source_ptr.value.to_node(&root) + && let Some(scrut_expr) = match_expr.expr() + && match_expr.match_arm_list().is_some() + { + return Some( + MissingMatchArms { + scrutinee_expr: InFile::new( + source_ptr.file_id, + AstPtr::new(&scrut_expr), + ), + uncovered_patterns, } - } - Err(SyntheticSyntax) => (), + .into(), + ); } } BodyValidationDiagnostic::NonExhaustiveLet { pat, uncovered_patterns } => { - match source_map.pat_syntax(pat) { - Ok(source_ptr) => { - if let Some(ast_pat) = source_ptr.value.cast::<ast::Pat>() { - return Some( - NonExhaustiveLet { - pat: InFile::new(source_ptr.file_id, ast_pat), - uncovered_patterns, - } - .into(), - ); + if let Ok(source_ptr) = source_map.pat_syntax(pat) + && let Some(ast_pat) = source_ptr.value.cast::<ast::Pat>() + { + return Some( + NonExhaustiveLet { + pat: InFile::new(source_ptr.file_id, ast_pat), + uncovered_patterns, } - } - Err(SyntheticSyntax) => {} + .into(), + ); } } BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => { - if let Ok(source_ptr) = source_map.expr_syntax(return_expr) { + if let Ok(source_ptr) = source_map.expr_syntax(return_expr) // Filters out desugared return expressions (e.g. desugared try operators). - if let Some(ptr) = source_ptr.value.cast::<ast::ReturnExpr>() { - return Some( - RemoveTrailingReturn { - return_expr: InFile::new(source_ptr.file_id, ptr), - } + && let Some(ptr) = source_ptr.value.cast::<ast::ReturnExpr>() + { + return Some( + RemoveTrailingReturn { return_expr: InFile::new(source_ptr.file_id, ptr) } .into(), - ); - } + ); } } BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => { @@ -601,6 +718,11 @@ impl<'db> AnyDiagnostic<'db> { ); } } + BodyValidationDiagnostic::UnusedMustUse { expr, message } => { + if let Ok(source_ptr) = source_map.expr_syntax(expr) { + return Some(UnusedMustUse { expr: source_ptr, message }.into()); + } + } } None } @@ -608,9 +730,10 @@ impl<'db> AnyDiagnostic<'db> { pub(crate) fn inference_diagnostic( db: &'db dyn HirDatabase, def: DefWithBodyId, - d: &InferenceDiagnostic, + d: &'db InferenceDiagnostic, source_map: &hir_def::expr_store::BodySourceMap, sig_map: &hir_def::expr_store::ExpressionStoreSourceMap, + env: ParamEnvAndCrate<'db>, ) -> Option<AnyDiagnostic<'db>> { let expr_syntax = |expr| { source_map @@ -624,10 +747,28 @@ impl<'db> AnyDiagnostic<'db> { .inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern")) .ok() }; + let type_syntax = |pat| { + source_map + .type_syntax(pat) + .inspect_err(|_| stdx::never!("inference diagnostic in desugared type")) + .ok() + }; let expr_or_pat_syntax = |id| match id { ExprOrPatId::ExprId(expr) => expr_syntax(expr), ExprOrPatId::PatId(pat) => pat_syntax(pat), }; + let span_syntax = |span| match span { + hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()), + hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()), + hir_ty::Span::TypeRefId(idx) => type_syntax(idx).map(|it| it.upcast()), + hir_ty::Span::BindingId(idx) => { + pat_syntax(source_map.patterns_for_binding(idx)[0]).map(|it| it.upcast()) + } + hir_ty::Span::Dummy => { + never!("should never create a diagnostic for dummy spans"); + None + } + }; Some(match d { &InferenceDiagnostic::NoSuchField { field: expr, private, variant } => { let expr_or_pat = match expr { @@ -639,6 +780,23 @@ impl<'db> AnyDiagnostic<'db> { let private = private.map(|id| Field { id, parent: variant.into() }); NoSuchField { field: expr_or_pat, private, variant }.into() } + &InferenceDiagnostic::MismatchedArrayPatLen { pat, expected, found, has_rest } => { + let pat = pat_syntax(pat)?.map(Into::into); + MismatchedArrayPatLen { pat, expected, found, has_rest }.into() + } + InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => { + let pat = pat_syntax(*pat)?.map(Into::into); + ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into() + } + &InferenceDiagnostic::DuplicateField { field: expr, variant } => { + let expr_or_pat = match expr { + ExprOrPatId::ExprId(expr) => { + source_map.field_syntax(expr).map(AstPtr::wrap_left) + } + ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat), + }; + DuplicateField { field: expr_or_pat, variant: variant.into() }.into() + } &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into() } @@ -711,21 +869,21 @@ impl<'db> AnyDiagnostic<'db> { let expr = expr_syntax(expr)?; BreakOutsideOfLoop { expr, is_break, bad_value_break }.into() } + &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => { + NonExhaustiveRecordExpr { expr: expr_syntax(expr)? }.into() + } + &InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr } => { + FunctionalRecordUpdateOnNonStruct { base_expr: expr_syntax(base_expr)? }.into() + } InferenceDiagnostic::TypedHole { expr, expected } => { let expr = expr_syntax(*expr)?; TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into() } &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => { - let expr_or_pat = match pat { - ExprOrPatId::ExprId(expr) => expr_syntax(expr)?, - ExprOrPatId::PatId(pat) => { - let InFile { file_id, value } = pat_syntax(pat)?; - - // cast from Either<Pat, SelfParam> -> Either<_, Pat> - let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?; - InFile { file_id, value: ptr } - } - }; + let InFile { file_id, value } = pat_syntax(pat)?; + // cast from Either<Pat, SelfParam> -> Either<_, Pat> + let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?; + let expr_or_pat = InFile { file_id, value: ptr }; MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into() } InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { @@ -801,6 +959,64 @@ impl<'db> AnyDiagnostic<'db> { let expected_kind = GenericArgKind::from_id(param_id); IncorrectGenericsOrder { provided_arg, expected_kind }.into() } + &InferenceDiagnostic::InvalidLhsOfAssignment { lhs } => { + let lhs = expr_syntax(lhs)?; + InvalidLhsOfAssignment { lhs }.into() + } + &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => { + let at_point = span_syntax(at_point)?; + let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() { + rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type { + ty, + env: crate::body_param_env_from_has_crate(db, def), + }), + // FIXME: Printing the const to string is definitely not the correct thing to do here. + rustc_type_ir::GenericArgKind::Const(konst) => Either::Right( + konst.display(db, DisplayTarget::from_crate(db, def.krate(db))).to_string(), + ), + rustc_type_ir::GenericArgKind::Lifetime(_) => { + unreachable!("we currently don't emit TypeMustBeKnown for lifetimes") + } + }); + TypeMustBeKnown { at_point, top_term }.into() + } + &InferenceDiagnostic::UnionExprMustHaveExactlyOneField { expr } => { + let expr = expr_syntax(expr)?; + UnionExprMustHaveExactlyOneField { expr }.into() + } + InferenceDiagnostic::TypeMismatch { node, expected, found } => { + let expr_or_pat = expr_or_pat_syntax(*node)?; + TypeMismatch { + expr_or_pat, + expected: Type { env, ty: expected.as_ref() }, + actual: Type { env, ty: found.as_ref() }, + } + .into() + } + InferenceDiagnostic::SolverDiagnostic(d) => { + let span = span_syntax(d.span)?; + Self::solver_diagnostic(db, &d.kind, span, env)? + } + }) + } + + fn solver_diagnostic( + db: &'db dyn HirDatabase, + d: &'db SolverDiagnosticKind, + span: SpanSyntax, + env: ParamEnvAndCrate<'db>, + ) -> Option<AnyDiagnostic<'db>> { + let interner = DbInterner::new_no_crate(db); + Some(match d { + SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate } => { + let trait_predicate = + crate::TraitPredicate { inner: trait_predicate.get(interner), env }; + let root_trait_predicate = + root_trait_predicate.as_ref().map(|root_trait_predicate| { + crate::TraitPredicate { inner: root_trait_predicate.get(interner), env } + }); + UnimplementedTrait { span, trait_predicate, root_trait_predicate }.into() + } }) } @@ -894,6 +1110,11 @@ impl<'db> AnyDiagnostic<'db> { } .into() } + PathLoweringDiagnostic::GenericDefaultRefersToSelf { segment } => { + let segment = hir_segment_to_ast_segment(&path.value, segment)?; + let segment = path.with_value(AstPtr::new(&segment)); + GenericDefaultRefersToSelf { segment }.into() + } }) } diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 53f24713cd..a71851ea8c 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -7,6 +7,7 @@ use hir_def::{ expr_store::{Body, ExpressionStore}, hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, item_tree::FieldsShape, + layout::ExternAbi, signatures::{ ConstSignature, FunctionSignature, ImplSignature, StaticFlags, StaticSignature, TraitFlags, TraitSignature, TypeAliasSignature, @@ -22,16 +23,16 @@ use hir_ty::{ hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_params_bounds, write_visibility, }, - next_solver::ClauseKind, + next_solver::{ClauseKind, Unnormalized}, }; use itertools::Itertools; -use rustc_type_ir::inherent::IntoKind; +use rustc_type_ir::inherent::IntoKind as _; use crate::{ Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, EnumVariant, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, - LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, - TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, + LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitPredicate, + TraitRef, TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, }; fn write_builtin_derive_impl_method<'db>( @@ -163,13 +164,16 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re if data.is_async() { f.write_str("async ")?; } + if data.is_gen() { + f.write_str("gen ")?; + } // FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe // (they are conditionally unsafe to call). We probably should show something else. if func.is_unsafe_to_call(db, None, f.edition()) { f.write_str("unsafe ")?; } - if let Some(abi) = &data.abi { - write!(f, "extern \"{}\" ", abi.as_str())?; + if data.abi != ExternAbi::Rust { + write!(f, "extern \"{}\" ", data.abi.as_str())?; } write!(f, "fn {}", data.name.display(f.db, f.edition()))?; @@ -200,7 +204,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re first = false; } - let pat_id = body.params[param.idx - body.self_param.is_some() as usize]; + let pat_id = body.params[param.idx - body.self_param().is_some() as usize]; let pat_str = body.pretty_print_pat(db, func_id.into(), pat_id, true, f.edition()); f.write_str(&pat_str)?; @@ -223,7 +227,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re // `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns. // Use ugly pattern match to strip the Future trait. // Better way? - let ret_type = if !data.is_async() { + let ret_type = if !data.is_async() && !data.is_gen() { data.ret_type } else if let Some(ret_type) = data.ret_type { match &data.store[ret_type] { @@ -579,6 +583,7 @@ impl<'db> HirDisplay<'db> for TypeParam { let predicates = GenericPredicates::query_all(f.db, self.id.parent()); let predicates = predicates .iter_identity() + .map(Unnormalized::skip_norm_wip) .filter(|wc| match wc.kind().skip_binder() { ClauseKind::Trait(tr) => tr.self_ty() == ty, ClauseKind::Projection(proj) => proj.self_ty() == ty, @@ -850,6 +855,12 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> { } } +impl<'db> HirDisplay<'db> for TraitPredicate<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { + self.inner.hir_fmt(f) + } +} + impl<'db> HirDisplay<'db> for Trait { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { // FIXME(trait-alias) needs special handling to print the equal sign diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index 0a48be5473..219eb9c3b9 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs @@ -254,14 +254,9 @@ impl TryFrom<AssocItem> for GenericDefId { } } -impl From<(ExpressionStoreOwnerId, BindingId)> for Local { - fn from((parent, binding_id): (ExpressionStoreOwnerId, BindingId)) -> Self { - Local { parent, binding_id } - } -} impl From<(DefWithBodyId, BindingId)> for Local { fn from((parent, binding_id): (DefWithBodyId, BindingId)) -> Self { - Local { parent: parent.into(), binding_id } + Local { parent: parent.into(), parent_infer: parent.into(), binding_id } } } diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index 45c9811cc0..9bff8bda3a 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs @@ -293,8 +293,9 @@ impl HasSource for Param<'_> { .map(|value| InFile { file_id, value }) } Callee::Closure(closure, _) => { - let InternedClosure(owner, expr_id) = closure.loc(db); - let (_, source_map) = ExpressionStore::with_source_map(db, owner); + let InternedClosure { owner, expr: expr_id, .. } = closure.loc(db); + let (_, source_map) = + ExpressionStore::with_source_map(db, owner.expression_store_owner(db)); let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?; let root = db.parse_or_expand(file_id); match value.to_node(&root) { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d24e2c0cb5..63b834a8d1 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -75,6 +75,7 @@ use hir_def::{ TypeAliasSignature, UnionSignature, VariantFields, }, src::HasSource as _, + unstable_features::UnstableFeatures, visibility::visibility_from_ast, }; use hir_expand::{ @@ -82,16 +83,14 @@ use hir_expand::{ proc_macro::ProcMacroKind, }; use hir_ty::{ - GenericPredicates, InferenceResult, ParamEnvAndCrate, TyDefId, TyLoweringDiagnostic, - ValueTyDefId, all_super_traits, autoderef, check_orphan_rules, + GenericPredicates, InferBodyId, InferenceResult, ParamEnvAndCrate, TyDefId, + TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef, check_orphan_rules, consteval::try_const_usize, - db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId}, + db::{AnonConstId, InternedClosure, InternedClosureId, InternedCoroutineClosureId}, diagnostics::BodyValidationDiagnostic, direct_super_traits, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, - method_resolution::{ - self, InherentImpls, MethodResolutionContext, MethodResolutionUnstableFeatures, - }, + method_resolution::{self, InherentImpls, MethodResolutionContext}, mir::interpret_mir, next_solver::{ AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion, @@ -106,7 +105,7 @@ use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, fast_reject, - inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, + inherent::{GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, }; use span::{AstIdNode, Edition, FileId}; use stdx::{format_to, impl_from, never}; @@ -115,7 +114,7 @@ use syntax::{ ast::{self, HasName as _, HasVisibility as _}, format_smolstr, }; -use triomphe::{Arc, ThinArc}; +use triomphe::Arc; use crate::db::{DefDatabase, HirDatabase}; @@ -174,7 +173,7 @@ pub use { // FIXME: Properly encapsulate mir hir_ty::mir, hir_ty::{ - CastError, FnAbi, PointerCast, attach_db, attach_db_allow_change, + CastError, PointerCast, attach_db, attach_db_allow_change, consteval::ConstEvalError, diagnostics::UnsafetyReason, display::{ClosureStyle, DisplayTarget, HirDisplay, HirDisplayError, HirWrite}, @@ -197,7 +196,7 @@ use { hir_def::expr_store::path::Path, hir_expand::{ name::AsName, - span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef}, + span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, }, }; @@ -343,7 +342,7 @@ impl Crate { } pub fn is_unstable_feature_enabled(self, db: &dyn HirDatabase, feature: &Symbol) -> bool { - crate_def_map(db, self.id).is_unstable_feature_enabled(feature) + UnstableFeatures::query(db, self.id).is_enabled(feature) } } @@ -760,7 +759,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.field_types_with_diagnostics(s.id.into()).1.clone(), + db.field_types_with_diagnostics(s.id.into()).diagnostics(), source_map, ); } @@ -772,7 +771,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.field_types_with_diagnostics(u.id.into()).1.clone(), + db.field_types_with_diagnostics(u.id.into()).diagnostics(), source_map, ); } @@ -802,7 +801,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.field_types_with_diagnostics(v.into()).1.clone(), + db.field_types_with_diagnostics(v.into()).diagnostics(), source_map, ); expr_store_diagnostics(db, acc, source_map); @@ -818,7 +817,7 @@ impl Module { push_ty_diagnostics( db, acc, - db.type_for_type_alias_with_diagnostics(type_alias.id).1, + db.type_for_type_alias_with_diagnostics(type_alias.id).diagnostics(), source_map, ); acc.extend(def.diagnostics(db, style_lints)); @@ -881,7 +880,6 @@ impl Module { } let drop_maybe_dangle = (|| { - // FIXME: This can be simplified a lot by exposing hir-ty's utils.rs::Generics helper let trait_ = trait_?; let drop_trait = interner.lang_items().Drop?; if drop_trait != trait_.into() { @@ -948,7 +946,7 @@ impl Module { .collect(); if !missing.is_empty() { - let self_ty = db.impl_self_ty(impl_id).instantiate_identity(); + let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); let self_ty = structurally_normalize_ty( &infcx, self_ty, @@ -984,10 +982,8 @@ impl Module { // relationship here would be significantly more expensive. if !missing.is_empty() { let krate = self.krate(db).id; - let def_map = crate_def_map(db, krate); - if def_map.is_unstable_feature_enabled(&sym::specialization) - || def_map.is_unstable_feature_enabled(&sym::min_specialization) - { + let features = UnstableFeatures::query(db, krate); + if features.specialization || features.min_specialization { missing.retain(|(assoc_name, assoc_item)| { let AssocItem::Function(_) = assoc_item else { return true; @@ -1031,11 +1027,19 @@ impl Module { impl_assoc_items_scratch.clear(); } - push_ty_diagnostics(db, acc, db.impl_self_ty_with_diagnostics(impl_id).1, source_map); push_ty_diagnostics( db, acc, - db.impl_trait_with_diagnostics(impl_id).and_then(|it| it.1), + db.impl_self_ty_with_diagnostics(impl_id).diagnostics(), + source_map, + ); + push_ty_diagnostics( + db, + acc, + db.impl_trait_with_diagnostics(impl_id) + .as_ref() + .map(|it| it.diagnostics()) + .unwrap_or_default(), source_map, ); @@ -1124,9 +1128,9 @@ fn macro_call_diagnostics<'db>( let Some(e) = db.parse_macro_expansion_error(macro_call_id) else { return; }; - let ValueResult { value: parse_errors, err } = &*e; + let ValueResult { value: parse_errors, err } = e; if let Some(err) = err { - let loc = db.lookup_intern_macro_call(macro_call_id); + let loc = macro_call_id.loc(db); let file_id = loc.kind.file_id(); let mut range = precise_macro_call_location(&loc.kind, db, loc.krate); let RenderedExpandError { message, error, kind } = err.render_to_string(db); @@ -1138,7 +1142,7 @@ fn macro_call_diagnostics<'db>( } if !parse_errors.is_empty() { - let loc = db.lookup_intern_macro_call(macro_call_id); + let loc = macro_call_id.loc(db); let range = precise_macro_call_location(&loc.kind, db, loc.krate); acc.push(MacroExpansionParseError { range, errors: parse_errors.clone() }.into()) } @@ -1341,14 +1345,14 @@ impl<'db> InstantiatedField<'db> { let var_id = self.inner.parent.into(); let field = db.field_types(var_id)[self.inner.id].get(); - let ty = field.instantiate(interner, self.args); + let ty = field.instantiate(interner, self.args).skip_norm_wip(); TypeNs::new(db, var_id, ty) } } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct TupleField { - pub owner: ExpressionStoreOwnerId, + pub owner: InferBodyId, pub tuple: TupleId, pub index: u32, } @@ -1366,7 +1370,7 @@ impl TupleField { .get(self.index as usize) .copied() .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)); - Type { env: body_param_env_from_has_crate(db, self.owner), ty } + Type { env: body_param_env_from_has_crate(db, self.owner.expression_store_owner(db)), ty } } } @@ -1436,11 +1440,11 @@ impl Field { }; let interner = DbInterner::new_no_crate(db); let args = generic_args_from_tys(interner, def_id.into(), generics.map(|ty| ty.ty)); - let ty = db.field_types(var_id)[self.id].get().instantiate(interner, args); + let ty = db.field_types(var_id)[self.id].get().instantiate(interner, args).skip_norm_wip(); Type::new(db, var_id, ty) } - pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> { db.layout_of_ty( self.ty(db).ty.store(), param_env_from_has_crate( @@ -1529,7 +1533,7 @@ impl Struct { } pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> { - let args = infer_ctxt.fresh_args_for_item(self.id.into()); + let args = infer_ctxt.fresh_args_for_item(hir_ty::Span::Dummy, self.id.into()); InstantiatedStruct { inner: self, args } } } @@ -1566,7 +1570,7 @@ impl<'db> InstantiatedStruct<'db> { let interner = DbInterner::new_no_crate(db); let ty = db.ty(self.inner.id.into()); - TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args)) + TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip()) } } @@ -1700,7 +1704,7 @@ impl Enum { self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } - pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + pub fn layout<'db>(self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> { Adt::from(self).layout(db) } @@ -1728,7 +1732,7 @@ impl<'db> InstantiatedEnum<'db> { let interner = DbInterner::new_no_crate(db); let ty = db.ty(self.inner.id.into()); - TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args)) + TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip()) } } @@ -1787,7 +1791,7 @@ impl EnumVariant { db.const_eval_discriminant(self.into()) } - pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> { let parent_enum = self.parent_enum(db); let parent_layout = parent_enum.layout(db)?; Ok(match &parent_layout.0.variants { @@ -1808,8 +1812,10 @@ impl EnumVariant { } pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> { - let args = - infer_ctxt.fresh_args_for_item(self.parent_enum(infer_ctxt.interner.db()).id.into()); + let args = infer_ctxt.fresh_args_for_item( + hir_ty::Span::Dummy, + self.parent_enum(infer_ctxt.interner.db()).id.into(), + ); InstantiatedVariant { inner: self, args } } } @@ -1868,7 +1874,7 @@ impl Adt { has_non_default_type_params(db, self.into()) } - pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + pub fn layout<'db>(self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> { let interner = DbInterner::new_no_crate(db); let adt_id = AdtId::from(self); let args = GenericArgs::for_item_with_defaults(interner, adt_id.into(), |_, id, _| { @@ -1988,6 +1994,47 @@ impl Variant { Variant::EnumVariant(e) => (*e).name(db), } } + + pub fn adt(&self, db: &dyn HirDatabase) -> Adt { + match *self { + Variant::Struct(it) => it.into(), + Variant::Union(it) => it.into(), + Variant::EnumVariant(it) => it.parent_enum(db).into(), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct AnonConst { + id: AnonConstId, +} + +impl AnonConst { + pub fn owner(self, db: &dyn HirDatabase) -> ExpressionStoreOwner { + self.id.loc(db).owner.into() + } + + pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { + let loc = self.id.loc(db); + let env = body_param_env_from_has_crate(db, loc.owner); + Type { env, ty: loc.ty.get().instantiate_identity().skip_norm_wip() } + } + + pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst<'_>, ConstEvalError> { + let interner = DbInterner::new_no_crate(db); + let ty = self.id.loc(db).ty.get().instantiate_identity().skip_norm_wip(); + db.anon_const_eval(self.id, GenericArgs::empty(interner), None).map(|it| EvaluatedConst { + allocation: it, + def: self.id.into(), + ty, + }) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum InferBody { + Body(DefWithBody), + AnonConst(AnonConst), } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -2084,6 +2131,12 @@ impl DefWithBody { }) } + #[deprecated = "you should really not use this, this is exported for analysis-stats only"] + pub fn run_mir_body(self, db: &dyn HirDatabase) -> Result<(), MirLowerError> { + let Some(id) = self.id() else { return Ok(()) }; + db.mir_body(id.into()).map(drop) + } + /// A textual representation of the HIR of this def's body for debugging purposes. pub fn debug_hir(self, db: &dyn HirDatabase) -> String { let Some(id) = self.id() else { @@ -2098,7 +2151,7 @@ impl DefWithBody { let Some(id) = self.id() else { return String::new(); }; - let body = db.mir_body(id); + let body = db.mir_body(id.into()); match body { Ok(body) => body.pretty_print(db, self.module(db).krate(db).to_display_target(db)), Err(e) => format!("error:\n{e:?}"), @@ -2115,6 +2168,7 @@ impl DefWithBody { return; }; let krate = self.module(db).id.krate(db); + let env = body_param_env_from_has_crate(db, id); let (body, source_map) = Body::with_source_map(db, id); let sig_source_map = match self { @@ -2138,34 +2192,14 @@ impl DefWithBody { let infer = InferenceResult::of(db, id); for d in infer.diagnostics() { - acc.extend(AnyDiagnostic::inference_diagnostic(db, id, d, source_map, sig_source_map)); - } - - for (pat_or_expr, mismatch) in infer.type_mismatches() { - let expr_or_pat = match pat_or_expr { - ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left), - ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right), - }; - let expr_or_pat = match expr_or_pat { - Ok(Either::Left(expr)) => expr, - Ok(Either::Right(InFile { file_id, value: pat })) => { - // cast from Either<Pat, SelfParam> -> Either<_, Pat> - let Some(ptr) = AstPtr::try_from_raw(pat.syntax_node_ptr()) else { - continue; - }; - InFile { file_id, value: ptr } - } - Err(SyntheticSyntax) => continue, - }; - - acc.push( - TypeMismatch { - expr_or_pat, - expected: Type::new(db, id, mismatch.expected.as_ref()), - actual: Type::new(db, id, mismatch.actual.as_ref()), - } - .into(), - ); + acc.extend(AnyDiagnostic::inference_diagnostic( + db, + id, + d, + source_map, + sig_source_map, + env, + )); } let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, id); @@ -2203,9 +2237,9 @@ impl DefWithBody { } } - if let Ok(borrowck_results) = db.borrowck(id) { + if let Ok(borrowck_results) = db.borrowck(id.into()) { for borrowck_result in borrowck_results.iter() { - let mir_body = &borrowck_result.mir_body; + let mir_body = borrowck_result.mir_body(db); for moof in &borrowck_result.moved_out_of_ref { let span: InFile<SyntaxNodePtr> = match moof.span { mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) { @@ -2260,7 +2294,8 @@ impl DefWithBody { { need_mut = &mir::MutabilityReason::Not; } - let local = Local { parent: id.into(), binding_id }; + let local = + Local { parent: id.into(), parent_infer: mir_body.owner, binding_id }; let is_mut = body[binding_id].mode == BindingAnnotation::Mutable; match (need_mut, is_mut) { @@ -2382,6 +2417,9 @@ fn expr_store_diagnostics<'db>( ExpressionStoreDiagnostics::UndeclaredLabel { node, name } => { UndeclaredLabel { node: *node, name: name.clone() }.into() } + ExpressionStoreDiagnostics::PatternArgInExternFn { node } => { + PatternArgInExternFn { node: *node }.into() + } }); } @@ -2448,7 +2486,8 @@ impl Function { let resolver = id.resolver(db); let interner = DbInterner::new_no_crate(db); // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. - let callable_sig = db.callable_item_signature(id.into()).instantiate_identity(); + let callable_sig = + db.callable_item_signature(id.into()).instantiate_identity().skip_norm_wip(); let ty = Ty::new_fn_ptr(interner, callable_sig); Type::new_with_resolver_inner(db, &resolver, ty) } @@ -2525,10 +2564,13 @@ impl Function { // `impl_generics_len - impl_trait_ref.args.len()`. let trait_method_fn_ptr = Ty::new_fn_ptr( interner, - db.callable_item_signature(trait_method.into()).instantiate_identity(), + db.callable_item_signature(trait_method.into()) + .instantiate_identity() + .skip_norm_wip(), ); - let impl_trait_ref = - hir_ty::builtin_derive::impl_trait(interner, impl_).instantiate_identity(); + let impl_trait_ref = hir_ty::builtin_derive::impl_trait(interner, impl_) + .instantiate_identity() + .skip_norm_wip(); let trait_method_args = GenericArgs::identity_for_item(interner, trait_method.into()); let trait_method_own_args = GenericArgs::new_from_iter( @@ -2543,8 +2585,9 @@ impl Function { interner, impl_trait_ref.args.iter().chain(shifted_trait_method_own_args), ); - let impl_method_fn_ptr = - EarlyBinder::bind(trait_method_fn_ptr).instantiate(interner, impl_method_args); + let impl_method_fn_ptr = EarlyBinder::bind(trait_method_fn_ptr) + .instantiate(interner, impl_method_args) + .skip_norm_wip(); Type { env, ty: impl_method_fn_ptr } } } @@ -2575,7 +2618,7 @@ impl Function { let ret_type = self.ret_type(db); let interner = DbInterner::new_no_crate(db); let args = self.adapt_generic_args(interner, generics); - ret_type.derived(EarlyBinder::bind(ret_type.ty).instantiate(interner, args)) + ret_type.derived(EarlyBinder::bind(ret_type.ty).instantiate(interner, args).skip_norm_wip()) } fn adapt_generic_args<'db>( @@ -2690,7 +2733,7 @@ impl Function { idx: param.idx, ty: Type { env: param.ty.env, - ty: EarlyBinder::bind(param.ty.ty).instantiate(interner, args), + ty: EarlyBinder::bind(param.ty.ty).instantiate(interner, args).skip_norm_wip(), }, }) .collect() @@ -2957,25 +3000,38 @@ impl<'db> Param<'db> { Callee::Def(CallableDefId::FunctionId(it)) => { let parent = DefWithBodyId::FunctionId(it); let body = Body::of(db, parent); - if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { - Some(Local { parent: parent.into(), binding_id: self_param }) + if let Some(self_param) = body.self_param().filter(|_| self.idx == 0) { + Some(Local { + parent: parent.into(), + parent_infer: parent.into(), + binding_id: self_param, + }) } else if let Pat::Bind { id, .. } = - &body[body.params[self.idx - body.self_param.is_some() as usize]] + &body[body.params[self.idx - body.self_param().is_some() as usize]] { - Some(Local { parent: parent.into(), binding_id: *id }) + Some(Local { + parent: parent.into(), + parent_infer: parent.into(), + binding_id: *id, + }) } else { None } } Callee::Closure(closure, _) => { let c = closure.loc(db); - let body_owner = c.0; - let store = ExpressionStore::of(db, c.0); + let body_infer_owner = c.owner; + let body_owner = c.owner.expression_store_owner(db); + let store = ExpressionStore::of(db, body_owner); - if let Expr::Closure { args, .. } = &store[c.1] + if let Expr::Closure { args, .. } = &store[c.expr] && let Pat::Bind { id, .. } = &store[args[self.idx]] { - return Some(Local { parent: body_owner, binding_id: *id }); + return Some(Local { + parent: body_owner, + parent_infer: body_infer_owner, + binding_id: *id, + }); } None } @@ -3042,7 +3098,7 @@ impl SelfParam { let interner = DbInterner::new_no_crate(db); let args = self.func.adapt_generic_args(interner, generics); let Type { env, ty } = self.ty(db); - Type { env, ty: EarlyBinder::bind(ty).instantiate(interner, args) } + Type { env, ty: EarlyBinder::bind(ty).instantiate(interner, args).skip_norm_wip() } } } @@ -3140,7 +3196,7 @@ impl Const { /// Evaluate the constant. pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst<'_>, ConstEvalError> { let interner = DbInterner::new_no_crate(db); - let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity(); + let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity().skip_norm_wip(); db.const_eval(self.id, GenericArgs::empty(interner), None).map(|it| EvaluatedConst { allocation: it, def: self.id.into(), @@ -3156,7 +3212,7 @@ impl HasVisibility for Const { } pub struct EvaluatedConst<'db> { - def: DefWithBodyId, + def: InferBodyId, allocation: hir_ty::next_solver::Allocation<'db>, ty: Ty<'db>, } @@ -3220,7 +3276,7 @@ impl Static { /// Evaluate the static initializer. pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst<'_>, ConstEvalError> { - let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity(); + let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity().skip_norm_wip(); db.const_eval_static(self.id).map(|it| EvaluatedConst { allocation: it, def: self.id.into(), @@ -4018,7 +4074,7 @@ impl AssocItem { push_ty_diagnostics( db, acc, - db.type_for_type_alias_with_diagnostics(type_alias.id).1, + db.type_for_type_alias_with_diagnostics(type_alias.id).diagnostics(), &TypeAliasSignature::with_source_map(db, type_alias.id).1, ); for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) { @@ -4182,26 +4238,24 @@ impl GenericDef { }; expr_store_diagnostics(db, acc, source_map); - push_ty_diagnostics(db, acc, db.generic_defaults_with_diagnostics(def).1, source_map); push_ty_diagnostics( db, acc, - GenericPredicates::query_with_diagnostics(db, def).1.clone(), + db.generic_defaults_with_diagnostics(def).diagnostics(), + source_map, + ); + push_ty_diagnostics( + db, + acc, + GenericPredicates::query_with_diagnostics(db, def).diagnostics(), + source_map, + ); + push_ty_diagnostics( + db, + acc, + db.const_param_types_with_diagnostics(def).diagnostics(), source_map, ); - for (param_id, param) in generics.iter_type_or_consts() { - if let TypeOrConstParamData::ConstParamData(_) = param { - push_ty_diagnostics( - db, - acc, - db.const_param_ty_with_diagnostics(ConstParamId::from_unchecked( - TypeOrConstParamId { parent: def, local_id: param_id }, - )) - .1, - source_map, - ); - } - } } /// Returns a string describing the kind of this type. @@ -4296,6 +4350,7 @@ impl<'db> GenericSubstitution<'db> { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Local { pub(crate) parent: ExpressionStoreOwnerId, + pub(crate) parent_infer: InferBodyId, pub(crate) binding_id: BindingId, } @@ -4397,7 +4452,7 @@ impl Local { pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { let def = self.parent; - let infer = InferenceResult::of(db, def); + let infer = InferenceResult::of(db, self.parent_infer); let ty = infer.binding_ty(self.binding_id); Type::new(db, def, ty) } @@ -4411,8 +4466,8 @@ impl Local { } ExpressionStoreOwnerId::Body(def_with_body_id) => { b = Body::with_source_map(db, def_with_body_id); - if let Some((param, source)) = b.0.self_param.zip(b.1.self_param_syntax()) - && param == self.binding_id + if b.0.self_params.contains(&self.binding_id) + && let Some(source) = b.1.self_param_syntax() { let root = source.file_syntax(db); return vec![LocalSource { @@ -4452,8 +4507,8 @@ impl Local { } ExpressionStoreOwnerId::Body(def_with_body_id) => { b = Body::with_source_map(db, def_with_body_id); - if let Some((param, source)) = b.0.self_param.zip(b.1.self_param_syntax()) - && param == self.binding_id + if b.0.self_params.contains(&self.binding_id) + && let Some(source) = b.1.self_param_syntax() { let root = source.file_syntax(db); return LocalSource { @@ -4624,13 +4679,12 @@ impl GenericParam { GenericParam::ConstParam(_) => return None, GenericParam::LifetimeParam(it) => it.id.parent, }; - let generics = hir_ty::generics::generics(db, parent); let index = match self { - GenericParam::TypeParam(it) => generics.type_or_const_param_idx(it.id.into())?, + GenericParam::TypeParam(it) => hir_ty::type_or_const_param_idx(db, it.id.into()), GenericParam::ConstParam(_) => return None, - GenericParam::LifetimeParam(it) => generics.lifetime_idx(it.id)?, + GenericParam::LifetimeParam(it) => hir_ty::lifetime_param_idx(db, it.id), }; - db.variances_of(parent).get(index).map(Into::into) + db.variances_of(parent).get(index as usize).map(Into::into) } } @@ -4702,8 +4756,8 @@ impl TypeParam { pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { let resolver = self.id.parent().resolver(db); let interner = DbInterner::new_no_crate(db); - let index = hir_ty::param_idx(db, self.id.into()).unwrap(); - let ty = Ty::new_param(interner, self.id, index as u32); + let index = hir_ty::type_or_const_param_idx(db, self.id.into()); + let ty = Ty::new_param(interner, self.id, index); Type::new_with_resolver_inner(db, &resolver, ty) } @@ -4789,25 +4843,30 @@ impl ConstParam { } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - Type::new(db, self.id.parent(), db.const_param_ty_ns(self.id)) + Type::new(db, self.id.parent(), db.const_param_ty(self.id)) + } + + pub fn default(self, db: &dyn HirDatabase, display_target: DisplayTarget) -> Option<String> { + let arg = generic_arg_from_param(db, self.id.into())?; + Some(arg.display(db, display_target).to_string()) } - pub fn default( + pub fn default_source_code( self, db: &dyn HirDatabase, - display_target: DisplayTarget, + target_module: Module, ) -> Option<ast::ConstArg> { let arg = generic_arg_from_param(db, self.id.into())?; - known_const_to_ast(arg.konst()?, db, display_target) + known_const_to_ast(arg.konst()?, db, target_module.id) } } fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<GenericArg<'_>> { - let local_idx = hir_ty::param_idx(db, id)?; + let local_idx = hir_ty::type_or_const_param_idx(db, id); let defaults = db.generic_defaults(id.parent); - let ty = defaults.get(local_idx)?; + let ty = defaults.get(local_idx as usize)?; // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. - Some(ty.instantiate_identity()) + Some(ty.instantiate_identity().skip_norm_wip()) } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -4982,7 +5041,7 @@ impl Impl { pub fn trait_ref(self, db: &dyn HirDatabase) -> Option<TraitRef<'_>> { match self.id { AnyImplId::ImplId(id) => { - let trait_ref = db.impl_trait(id)?.instantiate_identity(); + let trait_ref = db.impl_trait(id)?.instantiate_identity().skip_norm_wip(); let resolver = id.resolver(db); Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) } @@ -4994,8 +5053,9 @@ impl Impl { param_env: hir_ty::builtin_derive::param_env(interner, id), krate, }; - let trait_ref = - hir_ty::builtin_derive::impl_trait(interner, id).instantiate_identity(); + let trait_ref = hir_ty::builtin_derive::impl_trait(interner, id) + .instantiate_identity() + .skip_norm_wip(); Some(TraitRef { env, trait_ref }) } } @@ -5006,7 +5066,7 @@ impl Impl { AnyImplId::ImplId(id) => { let resolver = id.resolver(db); // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. - let ty = db.impl_self_ty(id).instantiate_identity(); + let ty = db.impl_self_ty(id).instantiate_identity().skip_norm_wip(); Type::new_with_resolver_inner(db, &resolver, ty) } AnyImplId::BuiltinDeriveImplId(id) => { @@ -5019,6 +5079,7 @@ impl Impl { }; let ty = hir_ty::builtin_derive::impl_trait(interner, id) .instantiate_identity() + .skip_norm_wip() .self_ty(); Type { env, ty } } @@ -5159,14 +5220,15 @@ impl<'db> Closure<'db> { AnyClosureId::ClosureId(it) => it.loc(db), AnyClosureId::CoroutineClosureId(it) => it.loc(db), }; - let InternedClosure(owner, closure) = closure; - let infer = InferenceResult::of(db, owner); + let InternedClosure { owner: infer_owner, expr: closure, .. } = closure; + let infer = InferenceResult::of(db, infer_owner); + let owner = infer_owner.expression_store_owner(db); let param_env = body_param_env_from_has_crate(db, owner); infer.closures_data[&closure] .min_captures .values() .flatten() - .map(|capture| ClosureCapture { owner, closure, capture, param_env }) + .map(|capture| ClosureCapture { owner, infer_owner, closure, capture, param_env }) .collect() } @@ -5254,6 +5316,7 @@ impl FnTrait { #[derive(Clone, Debug, PartialEq, Eq)] pub struct ClosureCapture<'db> { owner: ExpressionStoreOwnerId, + infer_owner: InferBodyId, closure: ExprId, capture: &'db hir_ty::closure_analysis::CapturedPlace, param_env: ParamEnvAndCrate<'db>, @@ -5261,7 +5324,11 @@ pub struct ClosureCapture<'db> { impl<'db> ClosureCapture<'db> { pub fn local(&self) -> Local { - Local { parent: self.owner, binding_id: self.capture.captured_local() } + Local { + parent: self.owner, + parent_infer: self.infer_owner, + binding_id: self.capture.captured_local(), + } } /// Returns whether this place has any field (aka. non-deref) projections. @@ -5304,7 +5371,7 @@ impl<'db> ClosureCapture<'db> { match ty.kind() { TyKind::Tuple(_) => format_to!(result, "_{field_idx}"), TyKind::Adt(adt_def, _) => { - let variant = match adt_def.def_id().0 { + let variant = match adt_def.def_id() { AdtId::StructId(id) => VariantId::from(id), AdtId::UnionId(id) => id.into(), AdtId::EnumId(id) => { @@ -5338,7 +5405,7 @@ impl<'db> ClosureCapture<'db> { match ty.kind() { TyKind::Tuple(_) => format_to!(result, ".{field_idx}"), TyKind::Adt(adt_def, _) => { - let variant = match adt_def.def_id().0 { + let variant = match adt_def.def_id() { AdtId::StructId(id) => VariantId::from(id), AdtId::UnionId(id) => id.into(), AdtId::EnumId(id) => { @@ -5488,13 +5555,13 @@ impl<'db> Type<'db> { } }; let args = GenericArgs::error_for_item(interner, def.into()); - Type::new(db, def, ty.instantiate(interner, args)) + Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) } // FIXME: We shouldn't leak `TyKind::Param`s. fn from_def_params(db: &'db dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Self { let ty = db.ty(def.into()); - Type::new(db, def, ty.instantiate_identity()) + Type::new(db, def, ty.instantiate_identity().skip_norm_wip()) } fn from_value_def( @@ -5518,7 +5585,7 @@ impl<'db> Type<'db> { } }; let args = GenericArgs::error_for_item(interner, def.into()); - Type::new(db, def, ty.instantiate(interner, args)) + Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) } pub fn new_slice(ty: Self) -> Self { @@ -5584,7 +5651,7 @@ impl<'db> Type<'db> { // For non-phantom_data adts we check variants/fields as well as generic parameters TyKind::Adt(adt_def, args) - if !is_phantom_data(self.interner.db(), adt_def.def_id().0) => + if !is_phantom_data(self.interner.db(), adt_def.def_id()) => { let _variant_id_to_fields = |id: VariantId| { let variant_data = &id.fields(self.interner.db()); @@ -5596,7 +5663,10 @@ impl<'db> Type<'db> { .fields() .iter() .map(|(idx, _)| { - field_types[idx].get().instantiate(self.interner, args) + field_types[idx] + .get() + .instantiate(self.interner, args) + .skip_norm_wip() }) .filter(|it| !it.references_non_lt_error()) .collect() @@ -5604,7 +5674,7 @@ impl<'db> Type<'db> { }; let variant_id_to_fields = |_: VariantId| vec![]; - let variants: Vec<Vec<Ty<'db>>> = match adt_def.def_id().0 { + let variants: Vec<Vec<Ty<'db>>> = match adt_def.def_id() { AdtId::StructId(id) => { vec![variant_id_to_fields(id.into())] } @@ -5710,21 +5780,15 @@ impl<'db> Type<'db> { /// This function is used in `.await` syntax completion. pub fn into_future_output(&self, db: &'db dyn HirDatabase) -> Option<Type<'db>> { let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); - let trait_ = lang_items - .IntoFutureIntoFuture - .and_then(|into_future_fn| { - let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?; - let into_future_trait = assoc_item.container_or_implemented_trait(db)?; - Some(into_future_trait.id) - }) - .or(lang_items.Future)?; + let (trait_, output_assoc_type) = lang_items + .IntoFuture + .zip(lang_items.IntoFutureOutput) + .or(lang_items.Future.zip(lang_items.FutureOutput))?; if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { return None; } - let output_assoc_type = - trait_.trait_items(db).associated_type_by_name(&Name::new_symbol_root(sym::Output))?; self.normalize_trait_assoc_type(db, &[], output_assoc_type.into()) } @@ -5738,10 +5802,7 @@ impl<'db> Type<'db> { /// This does **not** resolve `IntoIterator`, only `Iterator`. pub fn iterator_item(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> { let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); - let iterator_trait = lang_items.Iterator?; - let iterator_item = iterator_trait - .trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::Item))?; + let iterator_item = lang_items.IteratorItem?; self.normalize_trait_assoc_type(db, &[], iterator_item.into()) } @@ -5756,19 +5817,13 @@ impl<'db> Type<'db> { /// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type pub fn into_iterator_iter(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> { let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); - let trait_ = lang_items.IntoIterIntoIter.and_then(|into_iter_fn| { - let assoc_item = as_assoc_item(db, AssocItem::Function, into_iter_fn)?; - let into_iter_trait = assoc_item.container_or_implemented_trait(db)?; - Some(into_iter_trait.id) - })?; + let trait_ = lang_items.IntoIterator?; if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { return None; } - let into_iter_assoc_type = trait_ - .trait_items(db) - .associated_type_by_name(&Name::new_symbol_root(sym::IntoIter))?; + let into_iter_assoc_type = lang_items.IntoIterIntoIterType?; self.normalize_trait_assoc_type(db, &[], into_iter_assoc_type.into()) } @@ -5882,7 +5937,7 @@ impl<'db> Type<'db> { pub fn is_packed(&self, db: &'db dyn HirDatabase) -> bool { let adt_id = match self.ty.kind() { - TyKind::Adt(adt_def, ..) => adt_def.def_id().0, + TyKind::Adt(adt_def, ..) => adt_def.def_id(), _ => return false, }; @@ -5921,7 +5976,7 @@ impl<'db> Type<'db> { let interner = DbInterner::new_no_crate(db); let (variant_id, substs) = match self.ty.kind() { TyKind::Adt(adt_def, substs) => { - let id = match adt_def.def_id().0 { + let id = match adt_def.def_id() { AdtId::StructId(id) => id.into(), AdtId::UnionId(id) => id.into(), AdtId::EnumId(_) => return Vec::new(), @@ -5935,7 +5990,7 @@ impl<'db> Type<'db> { .iter() .map(|(local_id, ty)| { let def = Field { parent: variant_id.into(), id: local_id }; - let ty = ty.get().instantiate(interner, substs); + let ty = ty.get().instantiate(interner, substs).skip_norm_wip(); (def, self.derived(ty)) }) .collect() @@ -6178,8 +6233,7 @@ impl<'db> Type<'db> { TypingMode::non_body_analysis() }; let infcx = interner.infer_ctxt().build(typing_mode); - let unstable_features = - MethodResolutionUnstableFeatures::from_def_map(resolver.top_level_def_map()); + let features = resolver.top_level_def_map().features(); let environment = param_env_from_resolver(db, resolver); let ctx = MethodResolutionContext { infcx: &infcx, @@ -6187,7 +6241,9 @@ impl<'db> Type<'db> { param_env: environment.param_env, traits_in_scope, edition: resolver.krate().data(db).edition, - unstable_features: &unstable_features, + features, + call_span: hir_ty::Span::Dummy, + receiver_span: hir_ty::Span::Dummy, }; f(&ctx) } @@ -6214,7 +6270,7 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); - let (self_ty, _) = ctx.infcx.instantiate_canonical(&canonical); + let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { Some(name) => { @@ -6322,7 +6378,7 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); - let (self_ty, _) = ctx.infcx.instantiate_canonical(&canonical); + let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { Some(name) => { @@ -6437,7 +6493,7 @@ impl<'db> Type<'db> { else { return None; }; - match def_id.expect_type_alias().loc(db).container { + match def_id.0.loc(db).container { ItemContainerId::TraitId(id) => Some(Trait { id }), _ => None, } @@ -6521,7 +6577,7 @@ impl<'db> Type<'db> { .collect() } - pub fn layout(&self, db: &'db dyn HirDatabase) -> Result<Layout, LayoutError> { + pub fn layout(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> { db.layout_of_ty(self.ty.store(), self.env.store()) .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) } @@ -6558,21 +6614,13 @@ impl<'db> TypeNs<'db> { ); let trait_ref = hir_ty::next_solver::TraitRef::new_from_args(infcx.interner, trait_.id.into(), args); - - let pred_kind = rustc_type_ir::Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(rustc_type_ir::TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )); - let predicate = hir_ty::next_solver::Predicate::new(infcx.interner, pred_kind); - let goal = hir_ty::next_solver::Goal::new( + let obligation = hir_ty::next_solver::infer::traits::Obligation::new( infcx.interner, - hir_ty::next_solver::ParamEnv::empty(), - predicate, + hir_ty::next_solver::infer::traits::ObligationCause::dummy(), + self.env.param_env, + trait_ref, ); - let res = hir_ty::traits::next_trait_solve_in_ctxt(&infcx, goal); - res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) + infcx.predicate_must_hold_modulo_regions(&obligation) } pub fn is_bool(&self) -> bool { @@ -6701,9 +6749,9 @@ impl<'db> Callable<'db> { } #[derive(Clone, Debug, Eq, PartialEq)] -pub struct Layout(Arc<TyLayout>, Arc<TargetDataLayout>); +pub struct Layout<'db>(Arc<TyLayout>, &'db TargetDataLayout); -impl Layout { +impl<'db> Layout<'db> { pub fn size(&self) -> u64 { self.0.size.bytes() } @@ -6713,7 +6761,7 @@ impl Layout { } pub fn niches(&self) -> Option<u128> { - Some(self.0.largest_niche?.available(&*self.1)) + Some(self.0.largest_niche?.available(self.1)) } pub fn field_offset(&self, field: Field) -> Option<u64> { @@ -6802,7 +6850,7 @@ impl Layout { let tag_size = if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants { match tag_encoding { - TagEncoding::Direct => tag.size(&*self.1).bytes_usize(), + TagEncoding::Direct => tag.size(self.1).bytes_usize(), TagEncoding::Niche { .. } => 0, } } else { @@ -6934,6 +6982,33 @@ pub trait HasVisibility { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum PredicatePolarity { + /// `T: Trait` + Positive, + /// `T: !Trait` + Negative, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TraitPredicate<'db> { + inner: hir_ty::next_solver::TraitPredicate<'db>, + env: ParamEnvAndCrate<'db>, +} + +impl<'db> TraitPredicate<'db> { + pub fn polarity(&self) -> PredicatePolarity { + match self.inner.polarity { + rustc_type_ir::PredicatePolarity::Positive => PredicatePolarity::Positive, + rustc_type_ir::PredicatePolarity::Negative => PredicatePolarity::Negative, + } + } + + pub fn trait_ref(&self) -> TraitRef<'db> { + TraitRef { env: self.env, trait_ref: self.inner.trait_ref } + } +} + /// Trait for obtaining the defining crate of an item. pub trait HasCrate { fn krate(&self, db: &dyn HirDatabase) -> Crate; @@ -7041,6 +7116,12 @@ impl HasCrate for Module { } } +impl HasCrate for AnonConst { + fn krate(&self, db: &dyn HirDatabase) -> Crate { + hir_def::HasModule::krate(&self.id.loc(db).owner, db).into() + } +} + pub trait HasContainer { fn container(&self, db: &dyn HirDatabase) -> ItemContainer; } @@ -7217,17 +7298,14 @@ pub enum DocLinkDef { fn push_ty_diagnostics<'db>( db: &'db dyn HirDatabase, acc: &mut Vec<AnyDiagnostic<'db>>, - diagnostics: Option<ThinArc<(), TyLoweringDiagnostic>>, + diagnostics: &[TyLoweringDiagnostic], source_map: &ExpressionStoreSourceMap, ) { - if let Some(diagnostics) = diagnostics { - acc.extend( - diagnostics - .slice - .iter() - .filter_map(|diagnostic| AnyDiagnostic::ty_diagnostic(diagnostic, source_map, db)), - ); - } + acc.extend( + diagnostics + .iter() + .filter_map(|diagnostic| AnyDiagnostic::ty_diagnostic(diagnostic, source_map, db)), + ); } pub trait MethodCandidateCallback { @@ -7342,10 +7420,8 @@ fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) .filter(|(_, param)| matches!(param, TypeOrConstParamData::TypeParamData(_))) .map(|(local_id, _)| TypeOrConstParamId { parent: generic_def, local_id }) .any(|param| { - let Some(param) = hir_ty::param_idx(db, param) else { - return false; - }; - defaults.get(param).is_none() + let param = hir_ty::type_or_const_param_idx(db, param); + defaults.get(param as usize).is_none() }) } @@ -7379,5 +7455,16 @@ fn empty_param_env<'db>(krate: base_db::Crate) -> ParamEnvAndCrate<'db> { ParamEnvAndCrate { param_env: ParamEnv::empty(), krate } } +// FIXME: We probably don't want to expose this. +pub trait MacroCallIdExt { + fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc; +} +impl MacroCallIdExt for span::MacroCallId { + #[inline] + fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc { + hir_expand::MacroCallId::from(self).loc(db) + } +} + pub use hir_ty::next_solver; pub use hir_ty::setup_tracing; diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index b7cc780ae4..a1bbe47188 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -13,11 +13,11 @@ use std::{ use base_db::{FxIndexSet, all_crates, toolchain_channel}; use either::Either; use hir_def::{ - BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, HasModule, MacroId, StructId, - TraitId, VariantId, + BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, HasModule, MacroId, + StructId, TraitId, VariantId, attrs::parse_extra_crate_attrs, expr_store::{Body, ExprOrPatSource, ExpressionStore, HygieneId, path::Path}, - hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, + hir::{BindingId, Expr, ExprId, ExprOrPatId}, nameres::{ModuleOrigin, crate_def_map}, resolver::{self, HasResolver, Resolver, TypeNs, ValueNs}, type_ref::Mutability, @@ -32,24 +32,24 @@ use hir_expand::{ name::AsName, }; use hir_ty::{ - InferenceResult, + InferBodyId, InferenceResult, + db::AnonConstId, diagnostics::unsafe_operations, infer_query_with_inspect, next_solver::{ - AnyImplId, DbInterner, Span, + AnyImplId, DbInterner, format_proof_tree::{ProofTreeData, dump_proof_tree_structured}, }, }; use intern::{Interned, Symbol, sym}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::Span as _; use smallvec::{SmallVec, smallvec}; use span::{FileId, SyntaxContext}; use stdx::{TupleExt, always}; use syntax::{ - AstNode, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind, SyntaxNode, - SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize, + AstNode, AstPtr, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind, + SyntaxNode, SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize, algo::skip_trivia_token, ast::{self, HasAttrs as _, HasGenericParams}, }; @@ -165,11 +165,17 @@ pub struct Semantics<'db, DB: ?Sized> { imp: SemanticsImpl<'db>, } +type DefWithoutBodyWithAnonConsts = Either<GenericDefId, VariantId>; +type ExprToAnonConst = FxHashMap<ExprId, AnonConstId>; +type DefAnonConstsMap = FxHashMap<DefWithoutBodyWithAnonConsts, ExprToAnonConst>; + pub struct SemanticsImpl<'db> { pub db: &'db dyn HirDatabase, - s2d_cache: RefCell<SourceToDefCache>, + s2d_cache: RefCell<SourceToDefCache<'db>>, /// MacroCall to its expansion's MacroCallId cache macro_call_cache: RefCell<FxHashMap<InFile<ast::MacroCall>, MacroCallId>>, + /// All anon consts defined by a *signature* (not a body). + signature_anon_consts_cache: RefCell<DefAnonConstsMap>, } impl<DB: ?Sized> fmt::Debug for Semantics<'_, DB> { @@ -455,7 +461,12 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> { impl<'db> SemanticsImpl<'db> { fn new(db: &'db dyn HirDatabase) -> Self { - SemanticsImpl { db, s2d_cache: Default::default(), macro_call_cache: Default::default() } + SemanticsImpl { + db, + s2d_cache: Default::default(), + macro_call_cache: Default::default(), + signature_anon_consts_cache: Default::default(), + } } pub fn parse(&self, file_id: EditionedFileId) -> ast::SourceFile { @@ -519,7 +530,7 @@ impl<'db> SemanticsImpl<'db> { } } HirFileId::MacroFile(macro_file) => { - let node = self.db.lookup_intern_macro_call(macro_file).to_node(self.db); + let node = macro_file.loc(self.db).to_node(self.db); let root = find_root(&node.value); self.cache(root, node.file_id); Some(node) @@ -544,8 +555,16 @@ impl<'db> SemanticsImpl<'db> { node } + pub fn to_node_syntax(&self, ptr: InFile<SyntaxNodePtr>) -> SyntaxNode { + ptr.value.to_node(&self.parse_or_expand(ptr.file_id)) + } + + pub fn to_node<N: AstNode>(&self, ptr: InFile<AstPtr<N>>) -> N { + ptr.value.to_node(&self.parse_or_expand(ptr.file_id)) + } + pub fn expand(&self, file_id: MacroCallId) -> ExpandResult<SyntaxNode> { - let res = self.db.parse_macro_expansion(file_id).map(|it| it.0.syntax_node()); + let res = self.db.parse_macro_expansion(file_id).as_ref().map(|it| it.0.syntax_node()); self.cache(res.value.clone(), file_id.into()); res } @@ -563,7 +582,7 @@ impl<'db> SemanticsImpl<'db> { macro_call: &ast::MacroCall, ) -> Option<ExpandResult<SyntaxNode>> { let file_id = self.to_def(macro_call)?; - let macro_call = self.db.lookup_intern_macro_call(file_id); + let macro_call = file_id.loc(self.db); let skip = matches!( macro_call.def.kind, @@ -645,7 +664,7 @@ impl<'db> SemanticsImpl<'db> { let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id); let root_node = value.0.syntax_node(); self.cache(root_node.clone(), file_id.into()); - Some(ExpandResult { value: root_node, err }) + Some(ExpandResult { value: root_node, err: err.clone() }) }) .collect(); Some(res) @@ -781,21 +800,16 @@ impl<'db> SemanticsImpl<'db> { /// Checks if renaming `renamed` to `new_name` may introduce conflicts with other locals, /// and returns the conflicting locals. pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &Name) -> Vec<Local> { - // FIXME: signatures - let Some(def) = to_be_renamed.parent.as_def_with_body() else { - return Vec::new(); - }; - let body = Body::of(self.db, def); + let (store, root_expr) = to_be_renamed.parent_infer.store_and_root_expr(self.db); let resolver = to_be_renamed.parent.resolver(self.db); - let starting_expr = - body.binding_owner(to_be_renamed.binding_id).unwrap_or(body.root_expr()); + let starting_expr = store.binding_owner(to_be_renamed.binding_id).unwrap_or(root_expr); let mut visitor = RenameConflictsVisitor { - body, + body: store, conflicts: FxHashSet::default(), db: self.db, new_name: new_name.symbol().clone(), old_name: to_be_renamed.name(self.db).symbol().clone(), - owner: def, + owner: to_be_renamed.parent, to_be_renamed: to_be_renamed.binding_id, resolver, }; @@ -803,7 +817,11 @@ impl<'db> SemanticsImpl<'db> { visitor .conflicts .into_iter() - .map(|binding_id| Local { parent: to_be_renamed.parent, binding_id }) + .map(|binding_id| Local { + parent: to_be_renamed.parent, + parent_infer: to_be_renamed.parent_infer, + binding_id, + }) .collect() } @@ -1304,7 +1322,7 @@ impl<'db> SemanticsImpl<'db> { }) .map(|(call_id, item)| { let item_range = item.syntax().text_range(); - let loc = db.lookup_intern_macro_call(call_id); + let loc = call_id.loc(db); let text_range = match loc.kind { hir_expand::MacroCallKind::Attr { censored_attr_ids: attr_ids, @@ -1635,6 +1653,44 @@ impl<'db> SemanticsImpl<'db> { .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) } + /// Returns the `return` expressions in this function's body, + /// excluding those inside closures or async blocks. + pub fn fn_return_points(&self, func: Function) -> Vec<InFile<ast::ReturnExpr>> { + let func_id = match func.id { + AnyFunctionId::FunctionId(id) => id, + _ => return vec![], + }; + let (body, source_map) = Body::with_source_map(self.db, func_id.into()); + + fn collect_returns( + sema: &SemanticsImpl<'_>, + body: &Body, + source_map: &hir_def::expr_store::ExpressionStoreSourceMap, + expr_id: ExprId, + acc: &mut Vec<InFile<ast::ReturnExpr>>, + ) { + match &body[expr_id] { + Expr::Closure { .. } | Expr::Const(_) => return, + Expr::Return { .. } => { + if let Ok(source) = source_map.expr_syntax(expr_id) + && let Some(ret_expr) = source.value.cast::<ast::ReturnExpr>() + { + let root = sema.parse_or_expand(source.file_id); + acc.push(InFile::new(source.file_id, ret_expr.to_node(&root))); + } + } + _ => {} + } + body.walk_child_exprs(expr_id, |child| { + collect_returns(sema, body, source_map, child, acc); + }); + } + + let mut returns = vec![]; + collect_returns(self, body, source_map, body.root_expr(), &mut returns); + returns + } + pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> { let text = lifetime.text(); let lifetime_param = lifetime.syntax().ancestors().find_map(|syn| { @@ -1915,15 +1971,16 @@ impl<'db> SemanticsImpl<'db> { pub fn get_unsafe_ops(&self, def: ExpressionStoreOwner) -> FxHashSet<ExprOrPatSource> { let Ok(def) = ExpressionStoreOwnerId::try_from(def) else { return Default::default() }; let (body, source_map) = ExpressionStore::with_source_map(self.db, def); - let infer = InferenceResult::of(self.db, def); let mut res = FxHashSet::default(); - for root in body.expr_roots() { - unsafe_operations(self.db, infer, def, body, root, &mut |node, _| { - if let Ok(node) = source_map.expr_or_pat_syntax(node) { - res.insert(node); - } - }); - } + self.with_all_infers_for_store(def, &mut |infer| { + for root in body.expr_roots() { + unsafe_operations(self.db, infer, def, body, root, &mut |node, _| { + if let Ok(node) = source_map.expr_or_pat_syntax(node) { + res.insert(node); + } + }); + } + }); res } @@ -2071,11 +2128,14 @@ impl<'db> SemanticsImpl<'db> { } pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> { - self.analyze_no_infer(node).map(|SourceAnalyzer { file_id, resolver, .. }| SemanticsScope { - db: self.db, - file_id, - resolver, - }) + self.analyze_no_infer(node).map( + |SourceAnalyzer { file_id, resolver, infer_body, .. }| SemanticsScope { + db: self.db, + file_id, + resolver, + infer_body, + }, + ) } pub fn scope_at_offset( @@ -2084,10 +2144,11 @@ impl<'db> SemanticsImpl<'db> { offset: TextSize, ) -> Option<SemanticsScope<'db>> { self.analyze_with_offset_no_infer(node, offset).map( - |SourceAnalyzer { file_id, resolver, .. }| SemanticsScope { + |SourceAnalyzer { file_id, resolver, infer_body, .. }| SemanticsScope { db: self.db, file_id, resolver, + infer_body, }, ) } @@ -2114,6 +2175,85 @@ impl<'db> SemanticsImpl<'db> { container.as_expression_store_owner().map(|id| id.into()) } + fn populate_anon_const_cache_for<'a>( + &self, + cache: &'a mut DefAnonConstsMap, + def: DefWithoutBodyWithAnonConsts, + ) -> &'a ExprToAnonConst { + cache.entry(def).or_insert_with(|| match def { + Either::Left(def) => { + let all_anon_consts = + AnonConstId::all_from_signature(self.db, def).into_iter().flatten().copied(); + all_anon_consts + .map(|anon_const| (anon_const.loc(self.db).expr, anon_const)) + .collect() + } + Either::Right(def) => { + let all_anon_consts = + self.db.field_types_with_diagnostics(def).defined_anon_consts().iter().copied(); + all_anon_consts + .map(|anon_const| (anon_const.loc(self.db).expr, anon_const)) + .collect() + } + }) + } + + fn find_anon_const_for_root_expr_in_signature( + &self, + def: DefWithoutBodyWithAnonConsts, + root_expr: ExprId, + ) -> Option<AnonConstId> { + let mut cache = self.signature_anon_consts_cache.borrow_mut(); + let anon_consts_map = self.populate_anon_const_cache_for(&mut cache, def); + anon_consts_map.get(&root_expr).copied() + } + + pub(crate) fn infer_body_for_expr_or_pat( + &self, + def: ExpressionStoreOwnerId, + store: &ExpressionStore, + node: ExprOrPatId, + ) -> Option<InferBodyId> { + let handle_def_without_body = |def| { + let root_expr = match node { + ExprOrPatId::ExprId(expr) => store.find_root_for_expr(expr), + ExprOrPatId::PatId(pat) => store.find_root_for_pat(pat), + }; + let anon_const = self.find_anon_const_for_root_expr_in_signature(def, root_expr)?; + Some(anon_const.into()) + }; + match def { + ExpressionStoreOwnerId::Signature(def) => handle_def_without_body(Either::Left(def)), + ExpressionStoreOwnerId::Body(def) => Some(def.into()), + ExpressionStoreOwnerId::VariantFields(def) => { + handle_def_without_body(Either::Right(def)) + } + } + } + + fn with_all_infers_for_store( + &self, + owner: ExpressionStoreOwnerId, + callback: &mut dyn FnMut(&'db InferenceResult), + ) { + let mut handle_def_without_body = |def| { + let mut cache = self.signature_anon_consts_cache.borrow_mut(); + let map = self.populate_anon_const_cache_for(&mut cache, def); + for &anon_const in map.values() { + callback(InferenceResult::of(self.db, anon_const)); + } + }; + match owner { + ExpressionStoreOwnerId::Signature(def) => handle_def_without_body(Either::Left(def)), + ExpressionStoreOwnerId::Body(def) => { + callback(InferenceResult::of(self.db, def)); + } + ExpressionStoreOwnerId::VariantFields(def) => { + handle_def_without_body(Either::Right(def)) + } + } + } + /// Returns none if the file of the node is not part of a crate. fn analyze(&self, node: &SyntaxNode) -> Option<SourceAnalyzer<'db>> { let node = self.find_file(node); @@ -2155,34 +2295,36 @@ impl<'db> SemanticsImpl<'db> { }); } ChildContainer::VariantId(def) => { - return Some(SourceAnalyzer::new_variant_body(self.db, def, node, offset, infer)); + return Some(SourceAnalyzer::new_variant_body( + self.db, self, def, node, offset, infer, + )); } ChildContainer::TraitId(it) => { return Some(if infer { - SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset) + SourceAnalyzer::new_generic_def(self.db, self, it.into(), node, offset) } else { - SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset) + SourceAnalyzer::new_generic_def_no_infer(self.db, self, it.into(), node, offset) }); } ChildContainer::ImplId(it) => { return Some(if infer { - SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset) + SourceAnalyzer::new_generic_def(self.db, self, it.into(), node, offset) } else { - SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset) + SourceAnalyzer::new_generic_def_no_infer(self.db, self, it.into(), node, offset) }); } ChildContainer::EnumId(it) => { return Some(if infer { - SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset) + SourceAnalyzer::new_generic_def(self.db, self, it.into(), node, offset) } else { - SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset) + SourceAnalyzer::new_generic_def_no_infer(self.db, self, it.into(), node, offset) }); } ChildContainer::GenericDefId(it) => { return Some(if infer { - SourceAnalyzer::new_generic_def(self.db, it, node, offset) + SourceAnalyzer::new_generic_def(self.db, self, it, node, offset) } else { - SourceAnalyzer::new_generic_def_no_infer(self.db, it, node, offset) + SourceAnalyzer::new_generic_def_no_infer(self.db, self, it, node, offset) }); } ChildContainer::ModuleId(it) => it.resolver(self.db), @@ -2315,6 +2457,7 @@ impl<'db> SemanticsImpl<'db> { text_range: TextRange, ) -> Option<FxIndexSet<Local>> { let sa = self.analyze(element.either(|e| e.syntax(), |s| s.syntax()))?; + let infer_body = sa.infer_body?; let store = sa.store()?; let mut resolver = sa.resolver.clone(); let def = resolver.expression_store_owner()?; @@ -2371,13 +2514,7 @@ impl<'db> SemanticsImpl<'db> { None } } - ExprOrPatId::PatId(pat_id) => { - if let Pat::Path(path) = &store[pat_id] { - Some(path) - } else { - None - } - } + ExprOrPatId::PatId(_) => None, }; if let Some(path) = path @@ -2387,7 +2524,11 @@ impl<'db> SemanticsImpl<'db> { let hygiene = store.expr_or_pat_path_hygiene(id); resolver.resolve_path_in_value_ns_fully(self.db, path, hygiene).inspect(|value| { if let ValueNs::LocalBinding(id) = value { - locals.insert((def, *id).into()); + locals.insert(Local { + parent: def, + parent_infer: infer_body, + binding_id: *id, + }); } }); } @@ -2427,7 +2568,8 @@ impl<'db> SemanticsImpl<'db> { if result.is_err() && let Some(tree) = proof_tree { - let data = dump_proof_tree_structured(tree, Span::dummy(), infer_ctxt); + let data = + dump_proof_tree_structured(tree, hir_ty::Span::Dummy, infer_ctxt); RESULT.with(|ctx| ctx.borrow_mut().push(data)); } }), @@ -2448,7 +2590,7 @@ fn macro_call_to_macro_id( macro_call_id: MacroCallId, ) -> Option<MacroId> { let db: &dyn ExpandDatabase = ctx.db; - let loc = db.lookup_intern_macro_call(macro_call_id); + let loc = macro_call_id.loc(db); match loc.def.ast_id() { Either::Left(it) => { @@ -2514,7 +2656,6 @@ to_def_impls![ (crate::ConstParam, ast::ConstParam, const_param_to_def), (crate::GenericParam, ast::GenericParam, generic_param_to_def), (crate::Macro, ast::Macro, macro_to_def), - (crate::Local, ast::IdentPat, bind_pat_to_def), (crate::Local, ast::SelfParam, self_param_to_def), (crate::Label, ast::Label, label_to_def), (crate::Adt, ast::Adt, adt_to_def), @@ -2524,6 +2665,14 @@ to_def_impls![ (MacroCallId, ast::MacroCall, macro_call_to_macro_call), ]; +impl ToDef for ast::IdentPat { + type Def = crate::Local; + + fn to_def(sema: &SemanticsImpl<'_>, src: InFile<&Self>) -> Option<Self::Def> { + sema.with_ctx(|ctx| ctx.bind_pat_to_def(src, sema)) + } +} + fn find_root(node: &SyntaxNode) -> SyntaxNode { node.ancestors().last().unwrap() } @@ -2550,6 +2699,7 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode { #[derive(Debug)] pub struct SemanticsScope<'db> { pub db: &'db dyn HirDatabase, + infer_body: Option<InferBodyId>, file_id: HirFileId, resolver: Resolver<'db>, } @@ -2601,9 +2751,11 @@ impl<'db> SemanticsScope<'db> { resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(id.into()), resolver::ScopeDef::Local(binding_id) => { - match self.resolver.expression_store_owner() { - Some(parent) => ScopeDef::Local(Local { parent, binding_id }), - None => continue, + match (self.resolver.expression_store_owner(), self.infer_body) { + (Some(parent), Some(parent_infer)) => { + ScopeDef::Local(Local { parent, parent_infer, binding_id }) + } + _ => continue, } } resolver::ScopeDef::Label(label_id) => { @@ -2657,6 +2809,7 @@ impl<'db> SemanticsScope<'db> { resolve_hir_path( self.db, &self.resolver, + self.infer_body, &Path::BarePath(Interned::new(ModPath::from_segments(kind, segments))), HygieneId::ROOT, None, @@ -2715,9 +2868,9 @@ impl ops::Deref for VisibleTraits { struct RenameConflictsVisitor<'a> { db: &'a dyn HirDatabase, - owner: DefWithBodyId, + owner: ExpressionStoreOwnerId, resolver: Resolver<'a>, - body: &'a Body, + body: &'a ExpressionStore, to_be_renamed: BindingId, new_name: Symbol, old_name: Symbol, @@ -2761,15 +2914,6 @@ impl RenameConflictsVisitor<'_> { self.resolve_path(expr.into(), path); self.resolver.reset_to_guard(guard); } - &Expr::Assignment { target, .. } => { - let guard = self.resolver.update_to_inner_scope(self.db, self.owner, expr); - self.body.walk_pats(target, &mut |pat| { - if let Pat::Path(path) = &self.body[pat] { - self.resolve_path(pat.into(), path); - } - }); - self.resolver.reset_to_guard(guard); - } _ => {} } diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index d932198b43..583b3e4bda 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -114,19 +114,22 @@ use syntax::{ }; use tt::TextRange; -use crate::{InFile, InlineAsmOperand, db::HirDatabase, semantics::child_by_source::ChildBySource}; +use crate::{ + InFile, InlineAsmOperand, SemanticsImpl, db::HirDatabase, + semantics::child_by_source::ChildBySource, +}; #[derive(Default)] -pub(super) struct SourceToDefCache { +pub(super) struct SourceToDefCache<'db> { pub(super) dynmap_cache: FxHashMap<(ChildContainer, HirFileId), DynMap>, - expansion_info_cache: FxHashMap<MacroCallId, ExpansionInfo>, + expansion_info_cache: FxHashMap<MacroCallId, ExpansionInfo<'db>>, pub(super) file_to_def_cache: FxHashMap<FileId, SmallVec<[ModuleId; 1]>>, pub(super) included_file_cache: FxHashMap<EditionedFileId, Option<MacroCallId>>, /// Rootnode to HirFileId cache pub(super) root_to_file_cache: FxHashMap<SyntaxNode, HirFileId>, } -impl SourceToDefCache { +impl<'db> SourceToDefCache<'db> { pub(super) fn cache( root_to_file_cache: &mut FxHashMap<SyntaxNode, HirFileId>, root_node: SyntaxNode, @@ -156,9 +159,9 @@ impl SourceToDefCache { pub(super) fn get_or_insert_expansion( &mut self, - db: &dyn HirDatabase, + db: &'db dyn HirDatabase, macro_file: MacroCallId, - ) -> &ExpansionInfo { + ) -> &ExpansionInfo<'db> { self.expansion_info_cache.entry(macro_file).or_insert_with(|| { let exp_info = macro_file.expansion_info(db); @@ -172,7 +175,7 @@ impl SourceToDefCache { pub(super) struct SourceToDefCtx<'db, 'cache> { pub(super) db: &'db dyn HirDatabase, - pub(super) cache: &'cache mut SourceToDefCache, + pub(super) cache: &'cache mut SourceToDefCache<'db>, } impl SourceToDefCtx<'_, '_> { @@ -345,14 +348,16 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn bind_pat_to_def( &mut self, src: InFile<&ast::IdentPat>, - ) -> Option<(ExpressionStoreOwnerId, BindingId)> { + semantics: &SemanticsImpl<'_>, + ) -> Option<crate::Local> { let container = self.find_container(src.syntax_ref())?.as_expression_store_owner()?; let (store, source_map) = ExpressionStore::with_source_map(self.db, container); let src = src.cloned().map(ast::Pat::from); let pat_id = source_map.node_pat(src.as_ref())?; // the pattern could resolve to a constant, verify that this is not the case if let crate::Pat::Bind { id, .. } = store[pat_id.as_pat()?] { - Some((container, id)) + let parent_infer = semantics.infer_body_for_expr_or_pat(container, store, pat_id)?; + Some(crate::Local { parent: container, parent_infer, binding_id: id }) } else { None } @@ -366,7 +371,7 @@ impl SourceToDefCtx<'_, '_> { .as_expression_store_owner()? .as_def_with_body()?; let body = Body::of(self.db, container); - Some((container, body.self_param?)) + Some((container, body.self_param()?)) } pub(super) fn label_to_def( &mut self, diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 6c43f80ce8..06182620c8 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -5,12 +5,15 @@ //! //! So, this modules should not be used during hir construction, it exists //! purely for "IDE needs". -use std::iter::{self, once}; +use std::{ + cell::OnceCell, + iter::{self, once}, +}; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, DefWithBodyId, ExpressionStoreOwnerId, FieldId, - FunctionId, GenericDefId, LocalFieldId, ModuleDefId, StructId, TraitId, VariantId, + FunctionId, GenericDefId, LocalFieldId, ModuleDefId, StructId, VariantId, expr_store::{ Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, HygieneId, lower::ExprCollector, @@ -21,7 +24,7 @@ use hir_def::{ lang_item::LangItems, nameres::MacroSubNs, resolver::{Resolver, TypeNs, ValueNs, resolver_for_scope}, - type_ref::{Mutability, TypeRef, TypeRefId}, + type_ref::{Mutability, TypeRefId}, }; use hir_expand::{ HirFileId, InFile, @@ -29,7 +32,8 @@ use hir_expand::{ name::{AsName, Name}, }; use hir_ty::{ - Adjustment, InferenceResult, LifetimeElisionKind, ParamEnvAndCrate, TyLoweringContext, + Adjustment, InferBodyId, InferenceResult, LifetimeElisionKind, ParamEnvAndCrate, + TyLoweringContext, TyLoweringInferVarsCtx, diagnostics::{ InsideUnsafeBlock, record_literal_missing_fields, record_pattern_missing_fields, unsafe_operations, @@ -37,8 +41,8 @@ use hir_ty::{ lang_items::lang_items_for_bin_op, method_resolution::{self, CandidateId}, next_solver::{ - AliasTy, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty, TyKind, TypingMode, - infer::DbInternerInferExt, + AliasTy, DbInterner, DefaultAny, ErrorGuaranteed, GenericArgs, ParamEnv, Region, Ty, + TyKind, TypingMode, infer::DbInternerInferExt, }, traits::structurally_normalize_ty, }; @@ -47,7 +51,7 @@ use itertools::Itertools; use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, - inherent::{AdtDef, IntoKind, Ty as _}, + inherent::{IntoKind, Ty as _}, }; use smallvec::SmallVec; use stdx::never; @@ -59,7 +63,7 @@ use syntax::{ use crate::{ Adt, AnyFunctionId, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, EnumVariant, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, - Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias, + SemanticsImpl, Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias, db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; @@ -71,6 +75,7 @@ pub(crate) struct SourceAnalyzer<'db> { pub(crate) file_id: HirFileId, pub(crate) resolver: Resolver<'db>, pub(crate) body_or_sig: Option<BodyOrSig<'db>>, + pub(crate) infer_body: Option<InferBodyId>, } #[derive(Debug)] @@ -137,34 +142,39 @@ impl<'db> SourceAnalyzer<'db> { scope_for_offset(db, scopes, source_map, node.file_id, offset) } }; + let (scope, _expr) = scope.unzip(); let resolver = resolver_for_scope(db, def, scope); SourceAnalyzer { resolver, body_or_sig: Some(BodyOrSig::Body { def, body, source_map, infer }), file_id, + infer_body: Some(def.into()), } } pub(crate) fn new_generic_def( db: &'db dyn HirDatabase, + sema: &SemanticsImpl<'db>, def: GenericDefId, node: InFile<&SyntaxNode>, offset: Option<TextSize>, ) -> SourceAnalyzer<'db> { - Self::new_generic_def_(db, def, node, offset, true) + Self::new_generic_def_(db, sema, def, node, offset, true) } pub(crate) fn new_generic_def_no_infer( db: &'db dyn HirDatabase, + sema: &SemanticsImpl<'db>, def: GenericDefId, node: InFile<&SyntaxNode>, offset: Option<TextSize>, ) -> SourceAnalyzer<'db> { - Self::new_generic_def_(db, def, node, offset, false) + Self::new_generic_def_(db, sema, def, node, offset, false) } pub(crate) fn new_generic_def_( db: &'db dyn HirDatabase, + sema: &SemanticsImpl<'db>, def: GenericDefId, node @ InFile { file_id, .. }: InFile<&SyntaxNode>, offset: Option<TextSize>, @@ -184,17 +194,31 @@ impl<'db> SourceAnalyzer<'db> { scope_for_offset(db, scopes, source_map, node.file_id, offset) } }; + let (scope, expr) = scope.unzip(); let resolver = resolver_for_scope(db, def, scope); - let infer = if infer { Some(InferenceResult::of(db, def)) } else { None }; + let infer_body = expr.and_then(|expr| { + sema.infer_body_for_expr_or_pat( + ExpressionStoreOwnerId::Signature(def), + store, + expr.into(), + ) + }); + let infer = if infer && let Some(infer_body) = infer_body { + Some(InferenceResult::of(db, infer_body)) + } else { + None + }; SourceAnalyzer { resolver, body_or_sig: Some(BodyOrSig::Sig { def, store, source_map, generics, infer }), file_id, + infer_body, } } pub(crate) fn new_variant_body( db: &'db dyn HirDatabase, + sema: &SemanticsImpl<'db>, def: VariantId, node @ InFile { file_id, .. }: InFile<&SyntaxNode>, offset: Option<TextSize>, @@ -214,8 +238,20 @@ impl<'db> SourceAnalyzer<'db> { scope_for_offset(db, scopes, source_map, node.file_id, offset) } }; + let (scope, expr) = scope.unzip(); let resolver = resolver_for_scope(db, def, scope); - let infer = if infer { Some(InferenceResult::of(db, def)) } else { None }; + let infer_body = expr.and_then(|expr| { + sema.infer_body_for_expr_or_pat( + ExpressionStoreOwnerId::VariantFields(def), + &fields.store, + expr.into(), + ) + }); + let infer = if infer && let Some(infer_body) = infer_body { + Some(InferenceResult::of(db, infer_body)) + } else { + None + }; SourceAnalyzer { resolver, body_or_sig: Some(BodyOrSig::VariantFields { @@ -225,6 +261,7 @@ impl<'db> SourceAnalyzer<'db> { infer, }), file_id, + infer_body, } } @@ -232,7 +269,7 @@ impl<'db> SourceAnalyzer<'db> { resolver: Resolver<'db>, node: InFile<&SyntaxNode>, ) -> SourceAnalyzer<'db> { - SourceAnalyzer { resolver, body_or_sig: None, file_id: node.file_id } + SourceAnalyzer { resolver, body_or_sig: None, file_id: node.file_id, infer_body: None } } fn owner(&self) -> Option<ExpressionStoreOwnerId> { @@ -339,39 +376,45 @@ impl<'db> SourceAnalyzer<'db> { let type_ref = self.type_id(ty)?; - let mut ty = TyLoweringContext::new( + let generic_def = self.resolver.generic_def()?; + let generics = OnceCell::new(); + let mut vars_cts = VarsCtx { types: interner.default_types(), infer: self.infer() }; + let ty = TyLoweringContext::new( db, &self.resolver, self.store()?, - self.resolver.generic_def()?, + generic_def.into(), + generic_def, + &generics, // FIXME: Is this correct here? Anyway that should impact mostly diagnostics, which we don't emit here // (this can impact the lifetimes generated, e.g. in `const` they won't be `'static`, but this seems like a // small problem). LifetimeElisionKind::Infer, ) + .with_infer_vars_behavior(Some(&mut vars_cts)) .lower_ty(type_ref); - // Try and substitute unknown types using InferenceResult - if let Some(infer) = self.infer() - && let Some(store) = self.store() - { - let mut inferred_types = vec![]; - TypeRef::walk(type_ref, store, &mut |type_ref_id, type_ref| { - if matches!(type_ref, TypeRef::Placeholder) { - inferred_types.push(infer.type_of_type_placeholder(type_ref_id)); + struct VarsCtx<'a, 'db> { + types: &'db DefaultAny<'db>, + infer: Option<&'a InferenceResult>, + } + + impl<'db> TyLoweringInferVarsCtx<'db> for VarsCtx<'_, 'db> { + fn next_ty_var(&mut self, span: hir_ty::Span) -> Ty<'db> { + if let hir_ty::Span::TypeRefId(type_ref) = span + && let Some(ty) = + self.infer.and_then(|infer| infer.type_of_type_placeholder(type_ref)) + { + ty + } else { + self.types.types.error } - }); - let mut inferred_types = inferred_types.into_iter(); - - let substituted_ty = hir_ty::next_solver::fold::fold_tys(interner, ty, |ty| { - if ty.is_ty_error() { inferred_types.next().flatten().unwrap_or(ty) } else { ty } - }); - - // Only used the result if the placeholder and unknown type counts matched - let success = - inferred_types.next().is_none() && !substituted_ty.references_non_lt_error(); - if success { - ty = substituted_ty; + } + fn next_const_var(&mut self, _span: hir_ty::Span) -> hir_ty::next_solver::Const<'db> { + self.types.consts.error + } + fn next_region_var(&mut self, _span: hir_ty::Span) -> Region<'db> { + self.types.regions.error } } @@ -409,7 +452,7 @@ impl<'db> SourceAnalyzer<'db> { ExprOrPatId::PatId(idx) => infer .pat_adjustment(idx) .and_then(|adjusts| adjusts.last()) - .map(|adjust| adjust.as_ref()), + .map(|adjust| adjust.source.as_ref()), }; let ty = infer.expr_or_pat_ty(expr_or_pat_id); @@ -436,7 +479,7 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option<Type<'db>> { let binding = match self.body_or_sig.as_ref()? { BodyOrSig::Sig { .. } | BodyOrSig::VariantFields { .. } => return None, - BodyOrSig::Body { body, .. } => body.self_param?, + BodyOrSig::Body { body, .. } => body.self_param()?, }; let ty = self.infer()?.binding_ty(binding); Some(Type::new_with_resolver(db, &self.resolver, ty)) @@ -449,12 +492,12 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option<BindingMode> { let id = self.pat_id(&pat.clone().into())?; let infer = self.infer()?; - infer.binding_mode(id.as_pat()?).map(|bm| match bm { - hir_ty::BindingMode::Move => BindingMode::Move, - hir_ty::BindingMode::Ref(hir_ty::next_solver::Mutability::Mut) => { + Some(match infer.binding_mode(id.as_pat()?)? { + hir_ty::BindingMode(hir_ty::ByRef::No, _) => BindingMode::Move, + hir_ty::BindingMode(hir_ty::ByRef::Yes(hir_ty::next_solver::Mutability::Mut), _) => { BindingMode::Ref(Mutability::Mut) } - hir_ty::BindingMode::Ref(hir_ty::next_solver::Mutability::Not) => { + hir_ty::BindingMode(hir_ty::ByRef::Yes(hir_ty::next_solver::Mutability::Not), _) => { BindingMode::Ref(Mutability::Shared) } }) @@ -470,7 +513,7 @@ impl<'db> SourceAnalyzer<'db> { infer .pat_adjustment(pat_id.as_pat()?)? .iter() - .map(|ty| Type::new_with_resolver(db, &self.resolver, ty.as_ref())) + .map(|adjust| Type::new_with_resolver(db, &self.resolver, adjust.source.as_ref())) .collect(), ) } @@ -483,7 +526,7 @@ impl<'db> SourceAnalyzer<'db> { let expr_id = self.expr_id(call.clone().into())?.as_expr()?; let (func, args) = self.infer()?.method_resolution(expr_id)?; let interner = DbInterner::new_no_crate(db); - let ty = db.value_ty(func.into())?.instantiate(interner, args); + let ty = db.value_ty(func.into())?.instantiate(interner, args).skip_norm_wip(); let ty = Type::new_with_resolver(db, &self.resolver, ty); let mut res = ty.as_callable(db)?; res.is_bound_method = true; @@ -538,7 +581,7 @@ impl<'db> SourceAnalyzer<'db> { &self, field: &ast::FieldExpr, ) -> Option<Either<Field, TupleField>> { - let def = self.owner()?; + let def = self.infer_body?; let expr_id = self.expr_id(field.clone().into())?.as_expr()?; self.infer()?.field_resolution(expr_id).map(|it| { it.map_either(Into::into, |f| TupleField { owner: def, tuple: f.tuple, index: f.index }) @@ -565,7 +608,7 @@ impl<'db> SourceAnalyzer<'db> { field: &ast::FieldExpr, ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution<'db>>)> { - let def = self.owner()?; + let def = self.infer_body?; let expr_id = self.expr_id(field.clone().into())?.as_expr()?; let inference_result = self.infer()?; match inference_result.field_resolution(expr_id) { @@ -626,8 +669,7 @@ impl<'db> SourceAnalyzer<'db> { has_start: bool, has_end: bool, ) -> Option<StructId> { - let has_new_range = - self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range); + let has_new_range = self.resolver.top_level_def_map().features().new_range; let lang_items = self.lang_items(db); match (op_kind, has_start, has_end) { (RangeOp::Exclusive, false, false) => lang_items.RangeFull, @@ -707,35 +749,25 @@ impl<'db> SourceAnalyzer<'db> { db: &'db dyn HirDatabase, prefix_expr: &ast::PrefixExpr, ) -> Option<Function> { + let lang_items = self.lang_items(db); let (_op_trait, op_fn) = match prefix_expr.op_kind()? { ast::UnaryOp::Deref => { // This can be either `Deref::deref` or `DerefMut::deref_mut`. // Since deref kind is inferenced and stored in `InferenceResult.method_resolution`, // use that result to find out which one it is. - let (deref_trait, deref) = self.lang_trait_fn( - db, - self.lang_items(db).Deref, - &Name::new_symbol_root(sym::deref), - )?; + let (deref_trait, deref) = (lang_items.Deref?, lang_items.Deref_deref?); self.infer() .and_then(|infer| { let expr = self.expr_id(prefix_expr.clone().into())?.as_expr()?; let (func, _) = infer.method_resolution(expr)?; - let (deref_mut_trait, deref_mut) = self.lang_trait_fn( - db, - self.lang_items(db).DerefMut, - &Name::new_symbol_root(sym::deref_mut), - )?; + let (deref_mut_trait, deref_mut) = + (lang_items.DerefMut?, lang_items.DerefMut_deref_mut?); if func == deref_mut { Some((deref_mut_trait, deref_mut)) } else { None } }) .unwrap_or((deref_trait, deref)) } - ast::UnaryOp::Not => { - self.lang_trait_fn(db, self.lang_items(db).Not, &Name::new_symbol_root(sym::not))? - } - ast::UnaryOp::Neg => { - self.lang_trait_fn(db, self.lang_items(db).Neg, &Name::new_symbol_root(sym::neg))? - } + ast::UnaryOp::Not => (lang_items.Not?, lang_items.Not_not?), + ast::UnaryOp::Neg => (lang_items.Neg?, lang_items.Neg_neg?), }; let ty = self.ty_of_expr(prefix_expr.expr()?)?; @@ -754,19 +786,16 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option<Function> { let base_ty = self.ty_of_expr(index_expr.base()?)?; let index_ty = self.ty_of_expr(index_expr.index()?)?; + let lang_items = self.lang_items(db); - let (_index_trait, index_fn) = - self.lang_trait_fn(db, self.lang_items(db).Index, &Name::new_symbol_root(sym::index))?; + let (_index_trait, index_fn) = (lang_items.Index?, lang_items.Index_index?); let op_fn = self .infer() .and_then(|infer| { let expr = self.expr_id(index_expr.clone().into())?.as_expr()?; let (func, _) = infer.method_resolution(expr)?; - let (_index_mut_trait, index_mut_fn) = self.lang_trait_fn( - db, - self.lang_items(db).IndexMut, - &Name::new_symbol_root(sym::index_mut), - )?; + let (_index_mut_trait, index_mut_fn) = + (lang_items.IndexMut_index_mut?, lang_items.IndexMut_index_mut?); if func == index_mut_fn { Some(index_mut_fn) } else { None } }) .unwrap_or(index_fn); @@ -785,10 +814,8 @@ impl<'db> SourceAnalyzer<'db> { let lhs = self.ty_of_expr(binop_expr.lhs()?)?; let rhs = self.ty_of_expr(binop_expr.rhs()?)?; - let (_op_trait, op_fn) = - lang_items_for_bin_op(self.lang_items(db), op).and_then(|(name, lang_item)| { - self.lang_trait_fn(db, lang_item, &Name::new_symbol_root(name)) - })?; + let (op_fn, _op_trait) = lang_items_for_bin_op(self.lang_items(db), op) + .and_then(|(method, trait_)| method.zip(trait_))?; // HACK: subst for `index()` coincides with that for `Index` because `index()` itself // doesn't have any generic parameters, so we skip building another subst for `index()`. let substs = GenericArgs::new_from_slice(&[lhs.into(), rhs.into()]); @@ -836,9 +863,11 @@ impl<'db> SourceAnalyzer<'db> { &path, name_hygiene(db, InFile::new(self.file_id, ast_name.syntax())), ) { - Some(ValueNs::LocalBinding(binding_id)) => { - Some(Local { binding_id, parent: self.resolver.expression_store_owner()? }) - } + Some(ValueNs::LocalBinding(binding_id)) => Some(Local { + binding_id, + parent: self.owner()?, + parent_infer: self.infer_body?, + }), _ => None, } }; @@ -846,8 +875,10 @@ impl<'db> SourceAnalyzer<'db> { let variant = self.infer()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.fields(db); let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; - let field_ty = - (*db.field_types(variant).get(field.local_id)?).get().instantiate(interner, subst); + let field_ty = (*db.field_types(variant).get(field.local_id)?) + .get() + .instantiate(interner, subst) + .skip_norm_wip(); Some(( field.into(), local, @@ -869,8 +900,10 @@ impl<'db> SourceAnalyzer<'db> { let variant_data = variant.fields(db); let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; let (adt, subst) = self.infer()?.pat_ty(pat_id.as_pat()?).as_adt()?; - let field_ty = - (*db.field_types(variant).get(field.local_id)?).get().instantiate(interner, subst); + let field_ty = (*db.field_types(variant).get(field.local_id)?) + .get() + .instantiate(interner, subst) + .skip_norm_wip(); Some(( field.into(), Type::new_with_resolver(db, &self.resolver, field_ty), @@ -898,7 +931,14 @@ impl<'db> SourceAnalyzer<'db> { }; let store_owner = self.resolver.expression_store_owner(); - let res = resolve_hir_value_path(db, &self.resolver, store_owner, path, HygieneId::ROOT)?; + let res = resolve_hir_value_path( + db, + &self.resolver, + store_owner, + self.infer_body, + path, + HygieneId::ROOT, + )?; match res { PathResolution::Def(def) => Some(def), _ => None, @@ -932,24 +972,25 @@ impl<'db> SourceAnalyzer<'db> { if let Either::Right(container) = &mut container { *container = structurally_normalize_ty(&infcx, *container, trait_env.param_env); } - let handle_variants = |variant: VariantId, - subst: GenericArgs<'db>, - container: &mut _| { - let fields = variant.fields(db); - let field = fields.field(&field_name.as_name())?; - let field_types = db.field_types(variant); - *container = Either::Right(field_types[field].get().instantiate(interner, subst)); - let generic_def = match variant { - VariantId::EnumVariantId(it) => it.loc(db).parent.into(), - VariantId::StructId(it) => it.into(), - VariantId::UnionId(it) => it.into(), + let handle_variants = + |variant: VariantId, subst: GenericArgs<'db>, container: &mut _| { + let fields = variant.fields(db); + let field = fields.field(&field_name.as_name())?; + let field_types = db.field_types(variant); + *container = Either::Right( + field_types[field].get().instantiate(interner, subst).skip_norm_wip(), + ); + let generic_def = match variant { + VariantId::EnumVariantId(it) => it.loc(db).parent.into(), + VariantId::StructId(it) => it.into(), + VariantId::UnionId(it) => it.into(), + }; + Some(( + Either::Right(Field { parent: variant.into(), id: field }), + generic_def, + subst, + )) }; - Some(( - Either::Right(Field { parent: variant.into(), id: field }), - generic_def, - subst, - )) - }; let temp_ty = Ty::new_error(interner, ErrorGuaranteed); let (field_def, generic_def, subst) = match std::mem::replace(&mut container, Either::Right(temp_ty)) { @@ -957,7 +998,7 @@ impl<'db> SourceAnalyzer<'db> { handle_variants(VariantId::from(variant_id), subst, &mut container)? } Either::Right(container_ty) => match container_ty.kind() { - TyKind::Adt(adt_def, subst) => match adt_def.def_id().0 { + TyKind::Adt(adt_def, subst) => match adt_def.def_id() { AdtId::StructId(id) => { handle_variants(id.into(), subst, &mut container)? } @@ -1120,7 +1161,7 @@ impl<'db> SourceAnalyzer<'db> { } // FIXME: collectiong here shouldnt be necessary? - let mut collector = ExprCollector::body(db, self.resolver.module(), self.file_id); + let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id); let hir_path = collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?; let parent_hir_path = path @@ -1202,21 +1243,15 @@ impl<'db> SourceAnalyzer<'db> { } if let Some(attr) = meta_path.parent_attr() { - let adt = if let Some(field) = - attr.syntax().parent().and_then(ast::RecordField::cast) - { - field.syntax().ancestors().take(4).find_map(ast::Adt::cast) - } else if let Some(field) = - attr.syntax().parent().and_then(ast::TupleField::cast) - { - field.syntax().ancestors().take(4).find_map(ast::Adt::cast) - } else if let Some(variant) = - attr.syntax().parent().and_then(ast::Variant::cast) - { - variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast) - } else { - None - }; + let adt = + attr.syntax().ancestors().find_map(ast::Item::cast).and_then( + |it| match it { + ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), + ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), + ast::Item::Union(it) => Some(ast::Adt::Union(it)), + _ => None, + }, + ); if let Some(adt) = adt { let ast_id = db.ast_id_map(self.file_id).ast_id(&adt); if let Some(helpers) = self @@ -1266,6 +1301,7 @@ impl<'db> SourceAnalyzer<'db> { let res = resolve_hir_path_( db, &self.resolver, + self.infer_body, &hir_path, prefer_value_ns, name_hygiene(db, InFile::new(self.file_id, path.syntax())), @@ -1287,7 +1323,7 @@ impl<'db> SourceAnalyzer<'db> { let env = self.trait_environment(db); let (subst, expected_resolution) = match ty.kind() { TyKind::Adt(adt_def, subst) => { - let adt_id = adt_def.def_id().0; + let adt_id = adt_def.def_id(); ( GenericSubstitution::new(adt_id.into(), subst, env), PathResolution::Def(ModuleDef::Adt(adt_id.into())), @@ -1298,7 +1334,7 @@ impl<'db> SourceAnalyzer<'db> { args, .. }) => { - let assoc_id = def_id.expect_type_alias(); + let assoc_id = def_id.0; ( GenericSubstitution::new(assoc_id.into(), args, env), PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), @@ -1332,13 +1368,14 @@ impl<'db> SourceAnalyzer<'db> { db: &dyn HirDatabase, path: &ast::Path, ) -> Option<PathResolutionPerNs> { - let mut collector = ExprCollector::body(db, self.resolver.module(), self.file_id); + let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id); let hir_path = collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?; let (store, _) = collector.store.finish(); Some(resolve_hir_path_( db, &self.resolver, + self.infer_body, &hir_path, false, name_hygiene(db, InFile::new(self.file_id, path.syntax())), @@ -1428,7 +1465,7 @@ impl<'db> SourceAnalyzer<'db> { .into_iter() .map(|local_id| { let field = FieldId { parent: variant, local_id }; - let ty = field_types[local_id].get().instantiate(interner, substs); + let ty = field_types[local_id].get().instantiate(interner, substs).skip_norm_wip(); (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) }) .collect() @@ -1456,9 +1493,7 @@ impl<'db> SourceAnalyzer<'db> { }; match expanded_expr { ExprOrPatId::ExprId(expanded_expr) => walk_expr(expanded_expr), - ExprOrPatId::PatId(expanded_pat) => { - body.walk_exprs_in_pat(expanded_pat, &mut walk_expr) - } + ExprOrPatId::PatId(expanded_pat) => body.walk_exprs_in_pat(expanded_pat, walk_expr), } return is_unsafe; } @@ -1480,6 +1515,7 @@ impl<'db> SourceAnalyzer<'db> { db, &self.resolver, self.resolver.expression_store_owner(), + self.infer_body, &Path::from_known_path_with_no_generic(ModPath::from_segments( PathKind::Plain, Some(name.clone()), @@ -1519,6 +1555,7 @@ impl<'db> SourceAnalyzer<'db> { db, &self.resolver, self.resolver.expression_store_owner(), + self.infer_body, &Path::from_known_path_with_no_generic(ModPath::from_segments( PathKind::Plain, Some(name.clone()), @@ -1586,28 +1623,19 @@ impl<'db> SourceAnalyzer<'db> { hir_def::lang_item::lang_items(db, self.resolver.krate()) } - fn lang_trait_fn( - &self, - db: &'db dyn HirDatabase, - lang_trait: Option<TraitId>, - method_name: &Name, - ) -> Option<(TraitId, FunctionId)> { - let trait_id = lang_trait?; - let fn_id = trait_id.trait_items(db).method_by_name(method_name)?; - Some((trait_id, fn_id)) - } - fn ty_of_expr(&self, expr: ast::Expr) -> Option<Ty<'db>> { self.infer()?.type_of_expr_or_pat(self.expr_id(expr)?) } } +// Note: the `ExprId` here does not need to be accurate, what's important is that it points at the same +// inference root. fn scope_for( db: &dyn HirDatabase, scopes: &ExprScopes, source_map: &ExpressionStoreSourceMap, node: InFile<&SyntaxNode>, -) -> Option<ScopeId> { +) -> Option<(ScopeId, ExprId)> { node.ancestors_with_macros(db) .take_while(|it| { let kind = it.kind(); @@ -1618,7 +1646,7 @@ fn scope_for( }) .filter_map(|it| it.map(ast::Expr::cast).transpose()) .filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr()) - .find_map(|it| scopes.scope_for(it)) + .find_map(|expr| scopes.scope_for(expr).map(|scope| (scope, expr))) } fn scope_for_offset( @@ -1627,14 +1655,14 @@ fn scope_for_offset( source_map: &ExpressionStoreSourceMap, from_file: HirFileId, offset: TextSize, -) -> Option<ScopeId> { +) -> Option<(ScopeId, ExprId)> { scopes .scope_by_expr() .iter() .filter_map(|(id, scope)| { let InFile { file_id, value } = source_map.expr_syntax(id).ok()?; if from_file == file_id { - return Some((value.text_range(), scope)); + return Some((value.text_range(), scope, id)); } // FIXME handle attribute expansion @@ -1643,13 +1671,15 @@ fn scope_for_offset( }) .find(|it| it.file_id == from_file) .filter(|it| it.kind() == SyntaxKind::MACRO_CALL)?; - Some((source.text_range(), scope)) + Some((source.text_range(), scope, id)) + }) + .filter(|(expr_range, _scope, _expr)| { + expr_range.start() <= offset && offset <= expr_range.end() }) - .filter(|(expr_range, _scope)| expr_range.start() <= offset && offset <= expr_range.end()) // find containing scope - .min_by_key(|(expr_range, _scope)| expr_range.len()) - .map(|(expr_range, scope)| { - adjust(db, scopes, source_map, expr_range, from_file, offset).unwrap_or(*scope) + .min_by_key(|(expr_range, _scope, _expr)| expr_range.len()) + .map(|(expr_range, scope, expr)| { + adjust(db, scopes, source_map, expr_range, from_file, offset).unwrap_or((*scope, expr)) }) } @@ -1662,7 +1692,7 @@ fn adjust( expr_range: TextRange, from_file: HirFileId, offset: TextSize, -) -> Option<ScopeId> { +) -> Option<(ScopeId, ExprId)> { let child_scopes = scopes .scope_by_expr() .iter() @@ -1674,14 +1704,14 @@ fn adjust( } let root = source.file_syntax(db); let node = source.value.to_node(&root); - Some((node.syntax().text_range(), scope)) + Some((node.syntax().text_range(), scope, id)) }) - .filter(|&(range, _)| { + .filter(|&(range, _, _)| { range.start() <= offset && expr_range.contains_range(range) && range != expr_range }); child_scopes - .max_by(|&(r1, _), &(r2, _)| { + .max_by(|&(r1, _, _), &(r2, _, _)| { if r1.contains_range(r2) { std::cmp::Ordering::Greater } else if r2.contains_range(r1) { @@ -1690,18 +1720,19 @@ fn adjust( r1.start().cmp(&r2.start()) } }) - .map(|(_ptr, scope)| *scope) + .map(|(_ptr, scope, expr)| (*scope, expr)) } #[inline] pub(crate) fn resolve_hir_path( db: &dyn HirDatabase, resolver: &Resolver<'_>, + infer_body: Option<InferBodyId>, path: &Path, hygiene: HygieneId, store: Option<&ExpressionStore>, ) -> Option<PathResolution> { - resolve_hir_path_(db, resolver, path, false, hygiene, store, false).any() + resolve_hir_path_(db, resolver, infer_body, path, false, hygiene, store, false).any() } #[inline] @@ -1719,6 +1750,7 @@ pub(crate) fn resolve_hir_path_as_attr_macro( fn resolve_hir_path_( db: &dyn HirDatabase, resolver: &Resolver<'_>, + infer_body: Option<InferBodyId>, path: &Path, prefer_value_ns: bool, hygiene: HygieneId, @@ -1728,9 +1760,17 @@ fn resolve_hir_path_( let types = || { let (ty, unresolved) = match path.type_anchor() { Some(type_ref) => resolver.generic_def().and_then(|def| { - let (_, res) = - TyLoweringContext::new(db, resolver, store?, def, LifetimeElisionKind::Infer) - .lower_ty_ext(type_ref); + let generics = OnceCell::new(); + let (_, res) = TyLoweringContext::new( + db, + resolver, + store?, + def.into(), + def, + &generics, + LifetimeElisionKind::Infer, + ) + .lower_ty_ext(type_ref); res.map(|ty_ns| (ty_ns, path.segments().first())) }), None => { @@ -1788,7 +1828,7 @@ fn resolve_hir_path_( }; let body_owner = resolver.expression_store_owner(); - let values = || resolve_hir_value_path(db, resolver, body_owner, path, hygiene); + let values = || resolve_hir_value_path(db, resolver, body_owner, infer_body, path, hygiene); let items = || { resolver @@ -1834,13 +1874,14 @@ fn resolve_hir_value_path( db: &dyn HirDatabase, resolver: &Resolver<'_>, store_owner: Option<ExpressionStoreOwnerId>, + infer_body: Option<InferBodyId>, path: &Path, hygiene: HygieneId, ) -> Option<PathResolution> { resolver.resolve_path_in_value_ns_fully(db, path, hygiene).and_then(|val| { let res = match val { ValueNs::LocalBinding(binding_id) => { - let var = Local { parent: store_owner?, binding_id }; + let var = Local { parent: store_owner?, parent_infer: infer_body?, binding_id }; PathResolution::Local(var) } ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), @@ -1877,9 +1918,17 @@ fn resolve_hir_path_qualifier( (|| { let (ty, unresolved) = match path.type_anchor() { Some(type_ref) => resolver.generic_def().and_then(|def| { - let (_, res) = - TyLoweringContext::new(db, resolver, store, def, LifetimeElisionKind::Infer) - .lower_ty_ext(type_ref); + let generics = OnceCell::new(); + let (_, res) = TyLoweringContext::new( + db, + resolver, + store, + def.into(), + def, + &generics, + LifetimeElisionKind::Infer, + ) + .lower_ty_ext(type_ref); res.map(|ty_ns| (ty_ns, path.segments().first())) }), None => { diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs index f2dc1ce798..af2371d493 100644 --- a/crates/hir/src/term_search.rs +++ b/crates/hir/src/term_search.rs @@ -214,11 +214,11 @@ impl<'db> LookupTable<'db> { /// Context for the `term_search` function #[derive(Debug)] -pub struct TermSearchCtx<'db, DB: HirDatabase> { +pub struct TermSearchCtx<'a, 'db, DB: HirDatabase> { /// Semantics for the program - pub sema: &'db Semantics<'db, DB>, + pub sema: &'a Semantics<'db, DB>, /// Semantic scope, captures context for the term search - pub scope: &'db SemanticsScope<'db>, + pub scope: &'a SemanticsScope<'db>, /// Target / expected output type pub goal: Type<'db>, /// Configuration for term search @@ -263,7 +263,7 @@ impl Default for TermSearchConfig { /// Note that there are usually more ways we can get to the `goal` type but some are discarded to /// reduce the memory consumption. It is also unlikely anyone is willing ti browse through /// thousands of possible responses so we currently take first 10 from every tactic. -pub fn term_search<'db, DB: HirDatabase>(ctx: &'db TermSearchCtx<'db, DB>) -> Vec<Expr<'db>> { +pub fn term_search<'db, DB: HirDatabase>(ctx: &TermSearchCtx<'_, 'db, DB>) -> Vec<Expr<'db>> { let module = ctx.scope.module(); let mut defs = FxHashSet::default(); defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(module))); diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index c7ef4e5d5d..8700326e17 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -41,7 +41,7 @@ use super::{LookupTable, NewTypesKey, TermSearchCtx}; /// _Note that there is no use of calling this tactic in every iteration as the output does not /// depend on the current state of `lookup`_ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, defs: &'a FxHashSet<ScopeDef>, lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> { @@ -53,14 +53,15 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(Expr::ConstParam(*it)), ScopeDef::Local(it) => { if ctx.config.enable_borrowcheck { - let borrowck = db.borrowck(it.parent.as_def_with_body()?).ok()?; + let borrowck = db.borrowck(it.parent_infer).ok()?; let invalid = borrowck.iter().any(|b| { + let mir_body = b.mir_body(ctx.sema.db); b.partially_moved.iter().any(|moved| { - Some(&moved.local) == b.mir_body.binding_locals.get(it.binding_id) + Some(&moved.local) == mir_body.binding_locals.get(it.binding_id) }) || b.borrow_regions.iter().any(|region| { // Shared borrows are fine - Some(®ion.local) == b.mir_body.binding_locals.get(it.binding_id) + Some(®ion.local) == mir_body.binding_locals.get(it.binding_id) && region.kind != BorrowKind::Shared }) }); @@ -105,7 +106,7 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( /// _Note that there is no use of calling this tactic in every iteration as the output does not /// depend on the current state of `lookup`_ pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, defs: &'a FxHashSet<ScopeDef>, lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> { @@ -153,7 +154,7 @@ pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet<ScopeDef>, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, @@ -302,7 +303,7 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, defs: &'a FxHashSet<ScopeDef>, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, @@ -437,7 +438,7 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet<ScopeDef>, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, @@ -556,7 +557,7 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet<ScopeDef>, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, @@ -598,7 +599,7 @@ pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>( /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet<ScopeDef>, lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> { @@ -632,7 +633,7 @@ pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet<ScopeDef>, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, @@ -738,7 +739,7 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( /// * `lookup` - Lookup table for types /// * `should_continue` - Function that indicates when to stop iterating pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( - ctx: &'a TermSearchCtx<'db, DB>, + ctx: &'a TermSearchCtx<'_, 'db, DB>, _defs: &'a FxHashSet<ScopeDef>, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, diff --git a/crates/ide-assists/src/assist_context.rs b/crates/ide-assists/src/assist_context.rs index 207a7548f4..d8e097f0e2 100644 --- a/crates/ide-assists/src/assist_context.rs +++ b/crates/ide-assists/src/assist_context.rs @@ -13,7 +13,7 @@ use crate::{ Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, assist_config::AssistConfig, }; -pub(crate) use ide_db::source_change::{SourceChangeBuilder, TreeMutator}; +pub(crate) use ide_db::source_change::SourceChangeBuilder; /// `AssistContext` allows to apply an assist or check if it could be applied. /// @@ -45,9 +45,9 @@ pub(crate) use ide_db::source_change::{SourceChangeBuilder, TreeMutator}; /// Note, however, that we don't actually use such two-phase logic at the /// moment, because the LSP API is pretty awkward in this place, and it's much /// easier to just compute the edit eagerly :-) -pub(crate) struct AssistContext<'a> { +pub(crate) struct AssistContext<'a, 'db> { pub(crate) config: &'a AssistConfig, - pub(crate) sema: Semantics<'a, RootDatabase>, + pub(crate) sema: Semantics<'db, RootDatabase>, frange: FileRange, trimmed_range: TextRange, source_file: SourceFile, @@ -57,12 +57,12 @@ pub(crate) struct AssistContext<'a> { covering_element: SyntaxElement, } -impl<'a> AssistContext<'a> { +impl<'a, 'db> AssistContext<'a, 'db> { pub(crate) fn new( - sema: Semantics<'a, RootDatabase>, + sema: Semantics<'db, RootDatabase>, config: &'a AssistConfig, frange: FileRange, - ) -> AssistContext<'a> { + ) -> AssistContext<'a, 'db> { let source_file = sema.parse(frange.file_id); let start = frange.range.start(); @@ -95,7 +95,7 @@ impl<'a> AssistContext<'a> { } } - pub(crate) fn db(&self) -> &'a RootDatabase { + pub(crate) fn db(&self) -> &'db RootDatabase { self.sema.db } @@ -165,7 +165,7 @@ pub(crate) struct Assists { } impl Assists { - pub(crate) fn new(ctx: &AssistContext<'_>, resolve: AssistResolveStrategy) -> Assists { + pub(crate) fn new(ctx: &AssistContext<'_, '_>, resolve: AssistResolveStrategy) -> Assists { Assists { resolve, file: ctx.frange.file_id.file_id(ctx.db()), diff --git a/crates/ide-assists/src/handlers/add_braces.rs b/crates/ide-assists/src/handlers/add_braces.rs index c5ec88ffb8..4bd987a371 100644 --- a/crates/ide-assists/src/handlers/add_braces.rs +++ b/crates/ide-assists/src/handlers/add_braces.rs @@ -44,7 +44,7 @@ use crate::{AssistContext, AssistId, Assists}; // }; // } // ``` -pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (expr_type, expr) = get_replacement_node(ctx)?; acc.add( @@ -74,7 +74,7 @@ enum ParentType { Assignment, } -fn get_replacement_node(ctx: &AssistContext<'_>) -> Option<(ParentType, ast::Expr)> { +fn get_replacement_node(ctx: &AssistContext<'_, '_>) -> Option<(ParentType, ast::Expr)> { let node = ctx.find_node_at_offset::<Either<ast::MatchArm, ast::ClosureExpr>>(); let (parent_type, body) = if let Some(eq_token) = ctx.find_token_syntax_at_offset(T![=]) { let parent = eq_token.parent()?; diff --git a/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs b/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs index 1809b8f305..40d48bee3c 100644 --- a/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs +++ b/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs @@ -29,7 +29,7 @@ use crate::{AssistContext, Assists}; // ``` pub(crate) fn add_explicit_method_call_deref( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { if ctx.has_empty_selection() { return None; diff --git a/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs b/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs index 75c5f84b85..db9f9dc074 100644 --- a/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs +++ b/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs @@ -30,7 +30,7 @@ use crate::{AssistContext, Assists, utils::add_group_separators}; // ``` pub(crate) fn add_explicit_enum_discriminant( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let enum_node = ctx.find_node_at_offset::<ast::Enum>()?; let enum_def = ctx.sema.to_def(&enum_node)?; diff --git a/crates/ide-assists/src/handlers/add_explicit_type.rs b/crates/ide-assists/src/handlers/add_explicit_type.rs index 0dd01b67e8..e543005e67 100644 --- a/crates/ide-assists/src/handlers/add_explicit_type.rs +++ b/crates/ide-assists/src/handlers/add_explicit_type.rs @@ -20,7 +20,7 @@ use crate::{AssistContext, AssistId, Assists}; // let x: i32 = 92; // } // ``` -pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let syntax_node = ctx.find_node_at_offset::<Either<LetStmt, Param>>()?; let (ascribed_ty, expr, pat) = if let Either::Left(let_stmt) = syntax_node { let cursor_in_range = { @@ -208,8 +208,6 @@ fn main() { } "#, ); - // note: this may break later if we add more consteval. it just needs to be something that our - // consteval engine doesn't understand check_assist_not_applicable( add_explicit_type, r#" diff --git a/crates/ide-assists/src/handlers/add_label_to_loop.rs b/crates/ide-assists/src/handlers/add_label_to_loop.rs index 41e9b6cc84..b2194ab3dc 100644 --- a/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -30,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let loop_expr = ctx.find_node_at_offset::<ast::AnyHasLoopBody>()?; let loop_kw = loop_token(&loop_expr)?; if loop_expr.label().is_some() || !loop_kw.text_range().contains_inclusive(ctx.offset()) { @@ -86,7 +86,7 @@ fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option<syntax::SyntaxToken> { fn insert_label_after_token( editor: &SyntaxEditor, token: &SyntaxToken, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, ) { let make = editor.make(); diff --git a/crates/ide-assists/src/handlers/add_lifetime_to_type.rs b/crates/ide-assists/src/handlers/add_lifetime_to_type.rs index 265ee3d2d4..dc847dcdbe 100644 --- a/crates/ide-assists/src/handlers/add_lifetime_to_type.rs +++ b/crates/ide-assists/src/handlers/add_lifetime_to_type.rs @@ -22,7 +22,7 @@ use crate::{AssistContext, AssistId, Assists}; // y: u32, // } // ``` -pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let ref_type_focused = ctx.find_node_at_offset::<ast::RefType>()?; if ref_type_focused.lifetime().is_some_and(|lifetime| lifetime.text() != "'_") { return None; diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index d1f1f9f123..efbe3817e1 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -45,7 +45,10 @@ use crate::{ // } // } // ``` -pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_missing_impl_members( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { add_missing_impl_members_inner( acc, ctx, @@ -89,7 +92,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext<'_ // ``` pub(crate) fn add_missing_default_members( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { add_missing_impl_members_inner( acc, @@ -103,7 +106,7 @@ pub(crate) fn add_missing_default_members( fn add_missing_impl_members_inner( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, mode: DefaultMethods, ignore_items: IgnoreAssocItems, assist_id: &'static str, @@ -224,7 +227,7 @@ fn add_missing_impl_members_inner( fn try_gen_trait_body( make: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, func: &ast::Fn, trait_ref: hir::TraitRef<'_>, impl_def: &ast::Impl, @@ -2620,4 +2623,22 @@ impl Allocator for System { "#, ); } + + #[test] + fn does_not_include_defaulted_assoc_types() { + check_assist_not_applicable( + add_missing_impl_members, + r#" +trait Trait { + type NotRequired = (); +} + +struct Struct; + +impl Trait for Struct { + $0 +} + "#, + ); + } } diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 3c33ddec31..667a1d7813 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -4,7 +4,7 @@ use either::Either; use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics}; use ide_db::RootDatabase; use ide_db::syntax_helpers::suggest_name; -use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast}; +use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory}; use itertools::Itertools; use syntax::ast::edit::IndentLevel; use syntax::ast::syntax_factory::SyntaxFactory; @@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, Assists, utils}; // } // } // ``` -pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let match_expr = ctx.find_node_at_offset_with_descend::<ast::MatchExpr>()?; let match_arm_list = match_expr.match_arm_list()?; let arm_list_range = ctx.sema.original_range_opt(match_arm_list.syntax())?; @@ -303,7 +303,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) } fn cursor_at_trivial_match_arm_list( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, match_expr: &MatchExpr, match_arm_list: &MatchArmList, ) -> Option<()> { @@ -357,7 +357,7 @@ struct ArmsEdit { } impl ArmsEdit { - fn remove_wildcard_arms(&mut self, ctx: &AssistContext<'_>, editor: &SyntaxEditor) { + fn remove_wildcard_arms(&mut self, ctx: &AssistContext<'_, '_>, editor: &SyntaxEditor) { for arm in self.match_arm_list.arms() { if !matches!(arm.pat(), Some(Pat::WildcardPat(_))) { self.last_arm = Some(arm); @@ -417,7 +417,7 @@ impl ArmsEdit { fn add_comma_after_last_arm( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, editor: &SyntaxEditor, ) { @@ -432,7 +432,7 @@ impl ArmsEdit { fn cover_edit_range( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, node: &impl AstNode, ) -> Option<std::ops::RangeInclusive<syntax::SyntaxElement>> { let range = ctx.sema.original_range_opt(node.syntax())?; @@ -581,7 +581,7 @@ fn resolve_array_of_enum_def( } fn build_pat( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, module: hir::Module, var: ExtendedVariant, @@ -602,7 +602,11 @@ fn build_pat( false, ) } else { - mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition) + mod_path_to_ast_with_factory( + make, + &module.find_path(db, ModuleDef::from(var), cfg)?, + edition, + ) }; let fields = var.fields(db); let pat: ast::Pat = match var.kind(db) { diff --git a/crates/ide-assists/src/handlers/add_return_type.rs b/crates/ide-assists/src/handlers/add_return_type.rs index 7934a80bfa..e7203a96bb 100644 --- a/crates/ide-assists/src/handlers/add_return_type.rs +++ b/crates/ide-assists/src/handlers/add_return_type.rs @@ -16,7 +16,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // fn foo() -> i32 { 42i32 } // ``` -pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?; let module = ctx.sema.scope(tail_expr.syntax())?.module(); let ty = ctx.sema.type_of_expr(&peel_blocks(tail_expr.clone()))?.adjusted(); @@ -133,7 +133,7 @@ fn peel_blocks(mut expr: ast::Expr) -> ast::Expr { expr } -fn extract_tail(ctx: &AssistContext<'_>) -> Option<(FnType, ast::Expr, InsertOrReplace)> { +fn extract_tail(ctx: &AssistContext<'_, '_>) -> Option<(FnType, ast::Expr, InsertOrReplace)> { let node = ctx.find_node_at_offset::<Either<ast::ClosureExpr, ast::Fn>>()?; let (fn_type, tail_expr, return_type_range, action) = match node { Either::Left(closure) => { diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs index dcd2124f7b..53205910c8 100644 --- a/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -28,7 +28,7 @@ use crate::{ // let x = make::<${0:_}>(); // } // ``` -pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let turbofish_target = ctx.find_node_at_offset::<ast::PathSegment>().map(Either::Left).or_else(|| { let callable_expr = ctx.find_node_at_offset::<ast::CallableExpr>()?; diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index b87a757047..10262445a2 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -37,7 +37,7 @@ use crate::{AssistContext, AssistId, Assists, utils::invert_boolean_expression}; // if !(x == 4 && y >= 3.14) {} // } // ``` -pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let mut bin_expr = if let Some(not) = ctx.find_token_syntax_at_offset(T![!]) && let Some(NodeOrToken::Node(next)) = not.next_sibling_or_token() && let Some(paren) = ast::ParenExpr::cast(next) @@ -189,8 +189,17 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti // } // } // ``` -pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; +pub(crate) fn apply_demorgan_iterator( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { + let method_call: ast::MethodCallExpr = ctx.find_node_at_offset().or_else(|| { + let parent = ctx.find_token_syntax_at_offset(T![!])?.parent()?; + match ast::PrefixExpr::cast(parent)?.expr()? { + ast::Expr::MethodCallExpr(method_call) => Some(method_call), + _ => None, + } + })?; let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?; let ast::Expr::ClosureExpr(closure_expr) = arg_expr else { return None }; @@ -210,6 +219,8 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> let new_name = match name.text().as_str() { "all" => make.name_ref("any"), "any" => make.name_ref("all"), + "is_some_and" => make.name_ref("is_none_or"), + "is_none_or" => make.name_ref("is_some_and"), _ => unreachable!(), }; editor.replace(name.syntax(), new_name.syntax()); @@ -245,14 +256,17 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_> /// Ensures that the method call is to `Iterator::all` or `Iterator::any`. fn validate_method_call_expr( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, method_call: &ast::MethodCallExpr, ) -> Option<(ast::NameRef, ast::Expr)> { let name_ref = method_call.name_ref()?; + let arg_expr = method_call.arg_list()?.args().next()?; + if name_ref.text() == "is_some_and" || name_ref.text() == "is_none_or" { + return Some((name_ref, arg_expr)); + } if name_ref.text() != "all" && name_ref.text() != "any" { return None; } - let arg_expr = method_call.arg_list()?.args().next()?; let sema = &ctx.sema; @@ -394,6 +408,26 @@ fn f() { if let 1 = 1 &&$0 true { } } } #[test] + fn demorgan_iterator_on_not() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: iterator +fn main() { + let arr = [1, 2, 3]; + let cond = $0!arr.into_iter().all(|num| num != 4); +} +"#, + r#" +fn main() { + let arr = [1, 2, 3]; + let cond = arr.into_iter().any(|num| num == 4); +} +"#, + ); + } + + #[test] fn demorgan_keep_pars_for_op_precedence() { check_assist( apply_demorgan, @@ -644,6 +678,51 @@ fn main() { } #[test] + fn demorgan_option_is_some_and() { + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: option +fn main() { + let cond = Some(2); + if !cond.$0is_some_and(|num| num > 3) { + println!("foo"); + } +} +"#, + r#" +fn main() { + let cond = Some(2); + if cond.is_none_or(|num| num <= 3) { + println!("foo"); + } +} +"#, + ); + + check_assist( + apply_demorgan_iterator, + r#" +//- minicore: option +fn main() { + let cond = Some(2); + if !cond.$0is_none_or(|num| num > 3) { + println!("foo"); + } +} +"#, + r#" +fn main() { + let cond = Some(2); + if cond.is_some_and(|num| num <= 3) { + println!("foo"); + } +} +"#, + ); + } + + #[test] fn demorgan_method_call_receiver() { check_assist( apply_demorgan, diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index f9d618790c..ac0bae7cd9 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -7,7 +7,7 @@ use ide_db::{ helpers::mod_path_to_ast, imports::{ import_assets::{ImportAssets, ImportCandidate, LocatedImport, TraitImportCandidate}, - insert_use::{ImportScope, insert_use, insert_use_as_alias}, + insert_use::{ImportScope, insert_use_as_alias_with_editor, insert_use_with_editor}, }, }; use syntax::{AstNode, Edition, SyntaxNode, ast, match_ast}; @@ -91,7 +91,7 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel}; // } // # pub mod std { pub mod collections { pub struct HashMap { } } } // ``` -pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let cfg = ctx.config.import_path_config(); let (import_assets, syntax_under_caret, expected) = find_importable_node(ctx)?; @@ -125,8 +125,14 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< (AssistId::quick_fix("auto_import"), import_path.display(ctx.db(), edition)); let add_normal_import = |acc: &mut Assists, label| { acc.add_group(&group_label, assist_id, label, range, |builder| { - let scope = builder.make_import_scope_mut(scope.clone()); - insert_use(&scope, mod_path_to_ast(&import_path, edition), &ctx.config.insert_use); + let editor = builder.make_editor(scope.as_syntax_node()); + insert_use_with_editor( + &scope, + mod_path_to_ast(&import_path, edition), + &ctx.config.insert_use, + &editor, + ); + builder.add_file_edits(ctx.vfs_file_id(), editor); }) }; let add_underscore_import = |acc: &mut Assists, name: &TraitImportCandidate<'_>, label| { @@ -139,13 +145,15 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< name.assoc_item_name.text() )); acc.add_group(&group_label, assist_id, label, range, |builder| { - let scope = builder.make_import_scope_mut(scope.clone()); - insert_use_as_alias( + let editor = builder.make_editor(scope.as_syntax_node()); + insert_use_as_alias_with_editor( &scope, mod_path_to_ast(&import_path, edition), &ctx.config.insert_use, edition, + &editor, ); + builder.add_file_edits(ctx.vfs_file_id(), editor); }); }; @@ -170,8 +178,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< Some(()) } -pub(super) fn find_importable_node<'a: 'db, 'db>( - ctx: &'a AssistContext<'db>, +pub(super) fn find_importable_node<'db>( + ctx: &AssistContext<'_, 'db>, ) -> Option<(ImportAssets<'db>, SyntaxNode, Option<Type<'db>>)> { // Deduplicate this with the `expected_type_and_name` logic for completions let expected = |expr_or_pat: Either<ast::Expr, ast::Pat>| match expr_or_pat { @@ -248,7 +256,7 @@ fn group_label(import_candidate: &ImportCandidate<'_>) -> GroupLabel { /// Determine how relevant a given import is in the current context. Higher scores are more /// relevant. pub(crate) fn relevance_score( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, import: &LocatedImport, expected: Option<&Type<'_>>, current_module: Option<&Module>, diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs index 50e4a367e9..5d3d8127ba 100644 --- a/crates/ide-assists/src/handlers/bind_unused_param.rs +++ b/crates/ide-assists/src/handlers/bind_unused_param.rs @@ -18,7 +18,7 @@ use syntax::{ // let _ = x; // } // ``` -pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let param: ast::Param = ctx.find_node_at_offset()?; let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None }; diff --git a/crates/ide-assists/src/handlers/change_visibility.rs b/crates/ide-assists/src/handlers/change_visibility.rs index 7119d5b9c2..f17197a750 100644 --- a/crates/ide-assists/src/handlers/change_visibility.rs +++ b/crates/ide-assists/src/handlers/change_visibility.rs @@ -21,14 +21,14 @@ use crate::{AssistContext, AssistId, Assists, utils::vis_offset}; // ``` // pub(crate) fn frobnicate() {} // ``` -pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { if let Some(vis) = ctx.find_node_at_offset::<ast::Visibility>() { return change_vis(acc, vis); } add_vis(acc, ctx) } -fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let item_keyword = ctx.token_at_offset().find(|leaf| { matches!( leaf.kind(), diff --git a/crates/ide-assists/src/handlers/convert_bool_then.rs b/crates/ide-assists/src/handlers/convert_bool_then.rs index a2a71bcba6..5ecc11ccc5 100644 --- a/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -38,7 +38,10 @@ use crate::{ // cond.then(|| val) // } // ``` -pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_if_to_bool_then( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { // FIXME applies to match as well let expr = ctx.find_node_at_offset::<ast::IfExpr>()?; if !expr.if_token()?.text_range().contains_inclusive(ctx.offset()) { @@ -153,7 +156,10 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> // } // } // ``` -pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_bool_then_to_if( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let name_ref = ctx.find_node_at_offset::<ast::NameRef>()?; let mcall = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; let receiver = mcall.receiver()?; diff --git a/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index e88778a62e..4c3168219f 100644 --- a/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -1,12 +1,13 @@ use either::Either; use hir::ModuleDef; +use ide_db::imports::insert_use::insert_use_with_editor; use ide_db::text_edit::TextRange; use ide_db::{ - FxHashSet, + FileId, FxHashSet, assists::AssistId, defs::Definition, - helpers::mod_path_to_ast, - imports::insert_use::{ImportScope, insert_use}, + helpers::mod_path_to_ast_with_factory, + imports::insert_use::ImportScope, search::{FileReference, UsageSearchResult}, source_change::SourceChangeBuilder, }; @@ -52,7 +53,7 @@ use crate::{ // } // } // ``` -pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let BoolNodeData { target_node, name, ty_annotation, initializer, definition } = find_bool_node(ctx)?; let target_module = ctx.sema.scope(&target_node)?.module().nearest_non_block_module(ctx.db()); @@ -85,8 +86,10 @@ pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) - &mut delayed_mutations, &make, ); - for (scope, path) in delayed_mutations { - insert_use(&scope, path, &ctx.config.insert_use); + for (file_id, scope, path) in delayed_mutations { + let editor = edit.make_editor(scope.as_syntax_node()); + insert_use_with_editor(&scope, path, &ctx.config.insert_use, &editor); + edit.add_file_edits(file_id, editor); } }, ) @@ -101,7 +104,7 @@ struct BoolNodeData { } /// Attempts to find an appropriate node to apply the action to. -fn find_bool_node(ctx: &AssistContext<'_>) -> Option<BoolNodeData> { +fn find_bool_node(ctx: &AssistContext<'_, '_>) -> Option<BoolNodeData> { let name = ctx.find_node_at_offset::<ast::Name>()?; if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { @@ -208,15 +211,16 @@ fn bool_expr_to_enum_expr(expr: ast::Expr, make: &SyntaxFactory) -> ast::Expr { /// Replaces all usages of the target identifier, both when read and written to. fn replace_usages( edit: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, usages: UsageSearchResult, target_definition: Definition, target_module: &hir::Module, - delayed_mutations: &mut Vec<(ImportScope, ast::Path)>, + delayed_mutations: &mut Vec<(FileId, ImportScope, ast::Path)>, make: &SyntaxFactory, ) { for (file_id, references) in usages { - edit.edit_file(file_id.file_id(ctx.db())); + let vfs_file_id = file_id.file_id(ctx.db()); + edit.edit_file(vfs_file_id); let refs_with_imports = augment_references_with_imports(ctx, references, target_module, make); @@ -323,8 +327,7 @@ fn replace_usages( // add imports across modules where needed if let Some((scope, path)) = import_data { - let scope = edit.make_import_scope_mut(scope); - delayed_mutations.push((scope, path)); + delayed_mutations.push((vfs_file_id, scope, path)); } }, ) @@ -338,7 +341,7 @@ struct FileReferenceWithImport { } fn augment_references_with_imports( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, references: Vec<FileReference>, target_module: &hir::Module, make: &SyntaxFactory, @@ -373,7 +376,7 @@ fn augment_references_with_imports( ) .map(|mod_path| { make.path_concat( - mod_path_to_ast(&mod_path, edition), + mod_path_to_ast_with_factory(make, &mod_path, edition), make.path_from_text("Bool"), ) })?; @@ -469,7 +472,7 @@ fn find_method_call_expr_usage(name: &ast::NameLike) -> Option<ast::Expr> { /// Adds the definition of the new enum before the target node. fn add_enum_def( edit: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, usages: &UsageSearchResult, target_node: SyntaxNode, target_module: &hir::Module, diff --git a/crates/ide-assists/src/handlers/convert_char_literal.rs b/crates/ide-assists/src/handlers/convert_char_literal.rs index 0a50ba86ba..b23525665a 100644 --- a/crates/ide-assists/src/handlers/convert_char_literal.rs +++ b/crates/ide-assists/src/handlers/convert_char_literal.rs @@ -12,7 +12,7 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel}; // ``` // const _: char = '\x61'; // ``` -pub(crate) fn convert_char_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_char_literal(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { if !ctx.has_empty_selection() { return None; } diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index acade43397..e6d31b9660 100644 --- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -11,10 +11,10 @@ use syntax::{ ast::{ self, HasArgList, HasGenericParams, HasName, edit::{AstNodeEdit, IndentLevel}, - make, + syntax_factory::SyntaxFactory, }, hacks::parse_expr_from_str, - ted, + syntax_editor::SyntaxEditor, }; use crate::assist_context::{AssistContext, Assists}; @@ -51,7 +51,7 @@ use crate::assist_context::{AssistContext, Assists}; // closure("abc", &mut s); // } // ``` -pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let closure = ctx.find_node_at_offset::<ast::ClosureExpr>()?; if ctx.find_node_at_offset::<ast::Expr>() != Some(ast::Expr::ClosureExpr(closure.clone())) { // Not inside the parameter list. @@ -64,6 +64,10 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) _ => None, } }); + + let (editor, source_root) = SyntaxEditor::new(ctx.source_file().syntax().clone()); + let make = editor.make(); + let module = ctx.sema.scope(closure.syntax())?.module(); let closure_ty = ctx.sema.type_of_expr(&closure.clone().into())?; let callable = closure_ty.original.as_callable(ctx.db())?; @@ -85,14 +89,15 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) let ty = param_ty .display_source_code(ctx.db(), module.into(), true) .unwrap_or_else(|_| "_".to_owned()); - Some(make::param(node.pat()?, make::ty(&ty))) + Some(make.param(node.pat()?, make.ty(&ty))) } } }) .collect::<Option<Vec<_>>>()?; let capture_params_start = params.len(); - let mut body = closure.body()?.clone_for_update(); + let closure_param_list = syntax::AstPtr::new(&closure.param_list()?); + let body = closure.body()?; let mut is_gen = false; let mut is_async = closure.async_token().is_some(); if is_async { @@ -107,37 +112,30 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) { is_async = true; ret_ty = ret_ty.future_output(ctx.db())?; - let token_idx = async_token.index(); - let whitespace_tokens_after_count = async_token + let end = async_token .siblings_with_tokens(Direction::Next) .skip(1) - .take_while(|token| token.kind() == SyntaxKind::WHITESPACE) - .count(); - body.syntax().splice_children( - token_idx..token_idx + whitespace_tokens_after_count + 1, - Vec::new(), - ); + .take_while(|it| it.kind() == SyntaxKind::WHITESPACE) + .last() + .unwrap_or_else(|| async_token.clone().into()); + editor.delete_all(async_token.into()..=end); } if let Some(gen_token) = block.gen_token() { is_gen = true; ret_ty = ret_ty.iterator_item(ctx.db())?; - let token_idx = gen_token.index(); - let whitespace_tokens_after_count = gen_token + let end = gen_token .siblings_with_tokens(Direction::Next) .skip(1) - .take_while(|token| token.kind() == SyntaxKind::WHITESPACE) - .count(); - body.syntax().splice_children( - token_idx..token_idx + whitespace_tokens_after_count + 1, - Vec::new(), - ); + .take_while(|it| it.kind() == SyntaxKind::WHITESPACE) + .last() + .unwrap_or_else(|| gen_token.clone().into()); + editor.delete_all(gen_token.into()..=end); } if block.try_block_modifier().is_none() && block.unsafe_token().is_none() && block.label().is_none() && block.const_token().is_none() - && block.async_token().is_none() { wrap_body_in_block = false; } @@ -148,17 +146,17 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) "Convert closure to fn", closure.param_list()?.syntax().text_range(), |builder| { + let make = editor.make(); let closure_name_or_default = closure_name .as_ref() .map(|(_, _, it)| it.clone()) - .unwrap_or_else(|| make::name("fun_name")); + .unwrap_or_else(|| make.name("fun_name")); let captures = closure_ty.captured_items(ctx.db()); let capture_tys = captures.iter().map(|capture| capture.captured_ty(ctx.db())).collect::<Vec<_>>(); let mut captures_as_args = Vec::with_capacity(captures.len()); - let body_root = body.syntax().ancestors().last().unwrap(); // We need to defer this work because otherwise the text range of elements is being messed up, and // replacements for the next captures won't work. let mut capture_usages_replacement_map = Vec::with_capacity(captures.len()); @@ -167,9 +165,9 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) // FIXME: Allow configuring the replacement of `self`. let is_self = capture.local().is_self(ctx.db()) && !capture.has_field_projections(); let capture_name = if is_self { - make::name("this") + make.name("this") } else { - make::name(&capture.place_to_name(ctx.db(), ctx.edition())) + make.name(&capture.place_to_name(ctx.db(), ctx.edition())) }; closure_mentioned_generic_params.extend(capture_ty.generic_params(ctx.db())); @@ -177,9 +175,9 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) let capture_ty = capture_ty .display_source_code(ctx.db(), module.into(), true) .unwrap_or_else(|_| "_".to_owned()); - let param = make::param( - ast::Pat::IdentPat(make::ident_pat(false, false, capture_name.clone_subtree())), - make::ty(&capture_ty), + let param = make.param( + ast::Pat::IdentPat(make.ident_pat(false, false, capture_name.clone())), + make.ty(&capture_ty), ); if is_self { // Always put `this` first. @@ -195,7 +193,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) } let capture_usage_source = capture_usage.source(); - let capture_usage_source = capture_usage_source.to_node(&body_root); + let capture_usage_source = capture_usage_source.to_node(&source_root); let mut expr = match capture_usage_source { Either::Left(expr) => expr, Either::Right(pat) => { @@ -207,16 +205,16 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) expr = peel_ref(expr); } let replacement = wrap_capture_in_deref_if_needed( + make, &expr, &capture_name, capture.kind(), matches!(expr, ast::Expr::RefExpr(_)) || capture_usage.is_ref(), - ) - .clone_for_update(); + ); capture_usages_replacement_map.push((expr, replacement)); } - let capture_as_arg = capture_as_arg(ctx, capture); + let capture_as_arg = capture_as_arg(make, ctx, capture); if is_self { captures_as_args.insert(0, capture_as_arg); } else { @@ -225,32 +223,38 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) } let (closure_type_params, closure_where_clause) = - compute_closure_type_params(ctx, closure_mentioned_generic_params, &closure); + compute_closure_type_params(make, ctx, closure_mentioned_generic_params, &closure); for (old, new) in capture_usages_replacement_map { - if old == body { - body = new; - } else { - ted::replace(old.syntax(), new.syntax()); - } + editor.replace(old.syntax(), new.syntax()); } + let body = closure_param_list + .to_node(editor.finish().new_root()) + .syntax() + .parent() + .and_then(ast::ClosureExpr::cast) + .and_then(|closure| closure.body()) + .unwrap(); + + let make = SyntaxFactory::without_mappings(); + let body = if wrap_body_in_block { - make::block_expr([], Some(body.reset_indent().indent(1.into()))) + make.block_expr([], Some(body.reset_indent().indent(1.into()))) } else { ast::BlockExpr::cast(body.syntax().clone()).unwrap() }; - let params = make::param_list(None, params); + let params = make.param_list(None, params); let ret_ty = if ret_ty.is_unit() { None } else { let ret_ty = ret_ty .display_source_code(ctx.db(), module.into(), true) .unwrap_or_else(|_| "_".to_owned()); - Some(make::ret_type(make::ty(&ret_ty))) + Some(make.ret_type(make.ty(&ret_ty))) }; - let mut fn_ = make::fn_( + let mut fn_ = make.fn_( None, None, closure_name_or_default.clone(), @@ -266,7 +270,6 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) ); fn_ = fn_.dedent(IndentLevel::from_token(&fn_.syntax().last_token().unwrap())); - builder.edit_file(ctx.vfs_file_id()); match &closure_name { Some((closure_decl, _, _)) => { fn_ = fn_.indent(closure_decl.indent_level()); @@ -346,7 +349,8 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) } fn compute_closure_type_params( - ctx: &AssistContext<'_>, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, mentioned_generic_params: FxHashSet<hir::GenericParam>, closure: &ast::ClosureExpr, ) -> (Option<ast::GenericParamList>, Option<ast::WhereClause>) { @@ -472,11 +476,11 @@ fn compute_closure_type_params( })) .collect::<Vec<_>>(); let where_clause = - (!include_where_bounds.is_empty()).then(|| make::where_clause(include_where_bounds)); + (!include_where_bounds.is_empty()).then(|| make.where_clause(include_where_bounds)); // FIXME: Consider generic parameters that do not appear in params/return type/captures but // written explicitly inside the closure. - (Some(make::generic_param_list(include_params)), where_clause) + (Some(make.generic_param_list(include_params)), where_clause) } fn peel_parens(mut expr: ast::Expr) -> ast::Expr { @@ -497,12 +501,13 @@ fn peel_ref(mut expr: ast::Expr) -> ast::Expr { } fn wrap_capture_in_deref_if_needed( + make: &SyntaxFactory, expr: &ast::Expr, capture_name: &ast::Name, capture_kind: CaptureKind, is_ref: bool, ) -> ast::Expr { - let capture_name = make::expr_path(make::path_from_text(&capture_name.text())); + let capture_name = make.expr_path(make.path_from_text(&capture_name.text())); if capture_kind == CaptureKind::Move || is_ref { return capture_name; } @@ -524,10 +529,14 @@ fn wrap_capture_in_deref_if_needed( if does_autoderef { return capture_name; } - make::expr_prefix(T![*], capture_name).into() + make.expr_prefix(T![*], capture_name).into() } -fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture<'_>) -> ast::Expr { +fn capture_as_arg( + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, + capture: &ClosureCapture<'_>, +) -> ast::Expr { let place = parse_expr_from_str( &capture.display_place_source_code(ctx.db(), ctx.edition()), ctx.edition(), @@ -543,12 +552,12 @@ fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture<'_>) -> ast: { return expr.expr().expect("`display_place_source_code()` produced an invalid expr"); } - make::expr_ref(place, needs_mut) + make.expr_ref(place, needs_mut) } fn handle_calls( builder: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, closure_name: Option<&ast::IdentPat>, captures_as_args: &[ast::Expr], closure: &ast::ClosureExpr, @@ -590,7 +599,7 @@ fn handle_calls( fn handle_call( builder: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, closure_ref: ast::Expr, captures_as_args: &[ast::Expr], ) -> Option<()> { diff --git a/crates/ide-assists/src/handlers/convert_comment_block.rs b/crates/ide-assists/src/handlers/convert_comment_block.rs index f242fe8314..d950b6df21 100644 --- a/crates/ide-assists/src/handlers/convert_comment_block.rs +++ b/crates/ide-assists/src/handlers/convert_comment_block.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists}; // comment // */ // ``` -pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let comment = ctx.find_token_at_offset::<ast::Comment>()?; // Only allow comments which are alone on their line if let Some(prev) = comment.syntax().prev_token() { diff --git a/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs b/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs index 187cc74306..11a3c64188 100644 --- a/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs +++ b/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn convert_comment_from_or_to_doc( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let comment = ctx.find_token_at_offset::<ast::Comment>()?; diff --git a/crates/ide-assists/src/handlers/convert_for_to_while_let.rs b/crates/ide-assists/src/handlers/convert_for_to_while_let.rs index 9eb4c0584b..d45c4292b8 100644 --- a/crates/ide-assists/src/handlers/convert_for_to_while_let.rs +++ b/crates/ide-assists/src/handlers/convert_for_to_while_let.rs @@ -32,7 +32,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn convert_for_loop_to_while_let( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?; let iterable = for_loop.iterable()?; diff --git a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index 18f3ced414..3e11ee804a 100644 --- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -33,7 +33,10 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +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_()?; diff --git a/crates/ide-assists/src/handlers/convert_integer_literal.rs b/crates/ide-assists/src/handlers/convert_integer_literal.rs index bc76ade97f..c92ad2a7c9 100644 --- a/crates/ide-assists/src/handlers/convert_integer_literal.rs +++ b/crates/ide-assists/src/handlers/convert_integer_literal.rs @@ -13,7 +13,10 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel}; // ``` // const _: i32 = 0b1010; // ``` -pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_integer_literal( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { if !ctx.has_empty_selection() { return None; } diff --git a/crates/ide-assists/src/handlers/convert_into_to_from.rs b/crates/ide-assists/src/handlers/convert_into_to_from.rs index e330102423..a01a66e7b1 100644 --- a/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -1,4 +1,6 @@ -use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast, traits::resolve_target_trait}; +use ide_db::{ + famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory, traits::resolve_target_trait, +}; use syntax::ast::{self, AstNode, HasGenericArgs, HasName}; use crate::{AssistContext, AssistId, Assists}; @@ -31,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let impl_ = ctx.find_node_at_offset::<ast::Impl>()?; let src_type = impl_.self_ty()?; let ast_trait = impl_.trait_()?; @@ -44,17 +46,15 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - } let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.sema.db))); + let current_edition = module.krate(ctx.db()).edition(ctx.db()); - let src_type_path = { + let src_type_mod_path = { let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?; let src_type_def = match ctx.sema.resolve_path(&src_type_path) { Some(hir::PathResolution::Def(module_def)) => module_def, _ => return None, }; - mod_path_to_ast( - &module.find_path(ctx.db(), src_type_def, cfg)?, - module.krate(ctx.db()).edition(ctx.db()), - ) + module.find_path(ctx.db(), src_type_def, cfg)? }; let dest_type = match &ast_trait { @@ -89,19 +89,45 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - "Convert Into to From", impl_.syntax().text_range(), |builder| { - builder.replace(src_type.syntax().text_range(), dest_type.to_string()); - builder.replace(ast_trait.syntax().text_range(), format!("From<{src_type}>")); - builder.replace(into_fn_return.syntax().text_range(), "-> Self"); - builder.replace(into_fn_params.syntax().text_range(), format!("(val: {src_type})")); - builder.replace(into_fn_name.syntax().text_range(), "from"); + let editor = builder.make_editor(impl_.syntax()); + let make = editor.make(); + let src_type_path = + mod_path_to_ast_with_factory(make, &src_type_mod_path, current_edition); + + editor.replace(src_type.syntax(), make.ty(&dest_type.to_string()).syntax()); + editor.replace(ast_trait.syntax(), make.ty(&format!("From<{src_type}>")).syntax()); + editor.replace(into_fn_return.syntax(), make.ret_type(make.ty("Self")).syntax()); + + editor.replace( + into_fn_params.syntax(), + make.param_list( + None, + [make.param(make.simple_ident_pat(make.name("val")).into(), src_type.clone())], + ) + .syntax(), + ); + editor.replace(into_fn_name.syntax(), make.name("from").syntax()); for s in selfs { match s.text().as_ref() { - "self" => builder.replace(s.syntax().text_range(), "val"), - "Self" => builder.replace(s.syntax().text_range(), src_type_path.to_string()), + "self" => editor.replace(s.syntax(), make.name_ref("val").syntax()), + "Self" => { + if let Some(path_segment) = + s.syntax().parent().and_then(ast::PathSegment::cast) + { + let self_path = path_segment.parent_path(); + if self_path.qualifier().is_none() + && path_segment.generic_arg_list().is_none() + { + editor.replace(self_path.syntax(), src_type_path.syntax()); + } + } + } _ => {} } } + + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index cc5cc490f1..9506a96318 100644 --- a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists, utils::wrap_paren}; // ``` pub(crate) fn convert_iter_for_each_to_for( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let method = ctx.find_node_at_offset::<ast::MethodCallExpr>()?; @@ -98,7 +98,7 @@ pub(crate) fn convert_iter_for_each_to_for( // ``` pub(crate) fn convert_for_loop_with_for_each( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?; let iterable = for_loop.iterable()?; @@ -205,7 +205,7 @@ fn impls_core_iter(sema: &hir::Semantics<'_, ide_db::RootDatabase>, iterable: &a } fn validate_method_call_expr( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, expr: ast::MethodCallExpr, ) -> Option<(ast::Expr, ast::Expr)> { let name_ref = expr.name_ref()?; diff --git a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 1ae12390ee..07e12f0320 100644 --- a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -24,7 +24,10 @@ use crate::{AssistContext, AssistId, Assists}; // }; // } // ``` -pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_let_else_to_match( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); // Should focus on the `else` token to trigger let let_stmt = ctx @@ -143,6 +146,10 @@ fn remove_mut_and_collect_idents( let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?; make.box_pat(pat).into() } + ast::Pat::DerefPat(p) => { + let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?; + make.deref_pat(pat) + } ast::Pat::OrPat(p) => { let pats = p .pats() diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index bc49acc1ef..a93ab138e5 100644 --- a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -29,7 +29,10 @@ use crate::{ // let Some(val) = opt else { return }; // } // ``` -pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_match_to_let_else( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let let_stmt: ast::LetStmt = ctx.find_node_at_offset()?; let pat = let_stmt.pat()?; if ctx.offset() > pat.syntax().text_range().end() { @@ -61,9 +64,17 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<' |builder| { let extracting_arm_pat = rename_variable(&extracting_arm_pat, &extracted_variable_positions, pat); + let (open_paren, close_paren) = if ast::OrPat::can_cast(extracting_arm_pat.kind()) { + // Or patterns cannot put put directly under let statements. + // FIXME: Do this with `SyntaxEditor` in `rename_variable()`, it's just difficult right now + // since it re-roots nodes. + ("(", ")") + } else { + ("", "") + }; builder.replace( let_stmt.syntax().text_range(), - format!("let {extracting_arm_pat} = {initializer_expr} else {diverging_arm_expr};"), + format!("let {open_paren}{extracting_arm_pat}{close_paren} = {initializer_expr} else {diverging_arm_expr};"), ) }, ) @@ -71,7 +82,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<' // Given a match expression, find extracting and diverging arms. fn find_arms( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, match_expr: &ast::MatchExpr, ) -> Option<(ast::MatchArm, ast::MatchArm)> { let arms = match_expr.match_arm_list()?.arms().collect::<Vec<_>>(); @@ -99,7 +110,7 @@ fn find_arms( } // Given an extracting arm, find the extracted variable. -fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option<Vec<Name>> { +fn find_extracted_variable(ctx: &AssistContext<'_, '_>, arm: &ast::MatchArm) -> Option<Vec<Name>> { match arm.expr()? { ast::Expr::PathExpr(path) => { let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?; @@ -543,4 +554,38 @@ fn f() { "#, ); } + + #[test] + fn top_level_or_pat() { + check_assist( + convert_match_to_let_else, + r#" +enum E { + A(u32), + B(u32), + C, +} + +fn foo() { + let e = E::A(0); + let _$0 = match e { + E::A(v) | E::B(v) => v, + _ => return, + }; +} + "#, + r#" +enum E { + A(u32), + B(u32), + C, +} + +fn foo() { + let e = E::A(0); + let (E::A(_) | E::B(_)) = e else { return }; +} + "#, + ); + } } diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 5b691dba5e..72418f1559 100644 --- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -54,7 +54,7 @@ use crate::{ // ``` pub(crate) fn convert_named_struct_to_tuple_struct( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { // XXX: We don't currently provide this assist for struct definitions inside macros, but if we // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files @@ -92,7 +92,7 @@ pub(crate) fn convert_named_struct_to_tuple_struct( } fn edit_struct_def( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, strukt: &Either<ast::Struct, ast::Variant>, record_fields: ast::RecordFieldList, @@ -153,7 +153,7 @@ fn edit_struct_def( } fn edit_struct_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, strukt: Either<hir::Struct, hir::EnumVariant>, ) { @@ -174,7 +174,7 @@ fn edit_struct_references( } fn process_struct_name_reference( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, r: FileReference, edit: &SyntaxEditor, source: &ast::SourceFile, @@ -227,7 +227,7 @@ fn process_struct_name_reference( } fn record_to_tuple_struct_like<T, I>( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, source: &ast::SourceFile, editor: &SyntaxEditor, field_list: T, @@ -282,7 +282,7 @@ where } fn edit_field_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, fields: impl Iterator<Item = ast::RecordField>, ) { diff --git a/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs index c0fd69779a..d0e82d69eb 100644 --- a/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs +++ b/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs @@ -29,7 +29,7 @@ use crate::assist_context::{AssistContext, Assists}; // ``` pub(crate) fn convert_nested_function_to_closure( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let name = ctx.find_node_at_offset::<ast::Name>()?; let function = name.syntax().parent().and_then(ast::Fn::cast)?; diff --git a/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/crates/ide-assists/src/handlers/convert_range_for_to_while.rs index c83f8b0765..7026b5bafd 100644 --- a/crates/ide-assists/src/handlers/convert_range_for_to_while.rs +++ b/crates/ide-assists/src/handlers/convert_range_for_to_while.rs @@ -35,7 +35,10 @@ use crate::assist_context::{AssistContext, Assists}; // } // } // ``` -pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_range_for_to_while( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); let make = editor.make(); let for_kw = ctx.find_token_syntax_at_offset(T![for])?; diff --git a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 791a6a26af..74392ca7c1 100644 --- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -59,7 +59,10 @@ use crate::{ // let Some(x) = foo() else { return }; // } // ``` -pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_to_guarded_return( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { match ctx.find_node_at_offset::<Either<ast::LetStmt, ast::IfExpr>>()? { Either::Left(let_stmt) => let_stmt_to_guarded_return(let_stmt, acc, ctx), Either::Right(if_expr) => if_expr_to_guarded_return(if_expr, acc, ctx), @@ -69,15 +72,9 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' fn if_expr_to_guarded_return( if_expr: ast::IfExpr, acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let make = SyntaxFactory::without_mappings(); - let else_block = match if_expr.else_branch() { - Some(ast::ElseBranch::Block(block_expr)) => Some(block_expr), - Some(_) => return None, - _ => None, - }; - let cond = if_expr.condition()?; let if_token_range = if_expr.if_token()?.text_range(); @@ -102,7 +99,7 @@ fn if_expr_to_guarded_return( } let container = container_of(&parent_block)?; - let else_block = ElseBlock::new(&ctx.sema, else_block, &container)?; + let else_block = ElseBlock::new(&ctx.sema, if_expr.else_branch(), &container)?; if parent_block.tail_expr() != Some(if_expr.clone().into()) && !(else_block.is_never_block @@ -180,7 +177,7 @@ fn if_expr_to_guarded_return( fn let_stmt_to_guarded_return( let_stmt: ast::LetStmt, acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let pat = let_stmt.pat()?; let expr = let_stmt.initializer()?; @@ -237,7 +234,7 @@ fn container_of(block: &ast::BlockExpr) -> Option<SyntaxNode> { } struct ElseBlock<'db> { - exist_else_block: Option<ast::BlockExpr>, + exist_else_branch: Option<ast::ElseBranch>, is_never_block: bool, kind: EarlyKind<'db>, } @@ -245,13 +242,23 @@ struct ElseBlock<'db> { impl<'db> ElseBlock<'db> { fn new( sema: &Semantics<'db, RootDatabase>, - exist_else_block: Option<ast::BlockExpr>, + exist_else_branch: Option<ast::ElseBranch>, parent_container: &SyntaxNode, ) -> Option<Self> { - let is_never_block = exist_else_block.as_ref().is_some_and(|it| is_never_block(sema, it)); + let is_never_block = + exist_else_branch.as_ref().is_some_and(|it| is_never_else_branch(sema, it)); let kind = EarlyKind::from_node(parent_container, sema)?; - Some(Self { exist_else_block, is_never_block, kind }) + Some(Self { exist_else_branch, is_never_block, kind }) + } + + fn make_else_block_from_exist_branch(&self, make: &SyntaxFactory) -> Option<ast::BlockExpr> { + match self.exist_else_branch.as_ref()? { + ast::ElseBranch::Block(block_expr) => Some(block_expr.reset_indent()), + ast::ElseBranch::IfExpr(if_expr) => { + Some(make.block_expr(None, Some(if_expr.reset_indent().indent(1.into()).into()))) + } + } } fn make_early_block( @@ -259,15 +266,15 @@ impl<'db> ElseBlock<'db> { sema: &Semantics<'_, RootDatabase>, make: &SyntaxFactory, ) -> ast::BlockExpr { - let Some(block_expr) = self.exist_else_block else { + let Some(block_expr) = self.make_else_block_from_exist_branch(make) else { return make.tail_only_block_expr(self.kind.make_early_expr(sema, make, None)); }; if self.is_never_block { - return block_expr.reset_indent(); + return block_expr; } - let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr.reset_indent()); + let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr); let make = editor.make(); let last_stmt = block_expr.statements().last().map(|it| it.syntax().clone()); @@ -284,8 +291,8 @@ impl<'db> ElseBlock<'db> { editor.replace(tail_expr.syntax(), early_expr.syntax()); } else { let last_stmt = match block_expr.tail_expr() { - Some(expr) => make.expr_stmt(expr).syntax().clone(), - None => last_element.clone(), + Some(expr) if !expr.is_block_like() => make.expr_stmt(expr).syntax().clone(), + _ => last_element.clone(), }; let whitespace = make.whitespace(&whitespace.map_or(String::new(), |it| it.to_string())); @@ -401,6 +408,27 @@ fn is_early_block(then_block: &ast::StmtList) -> bool { || then_block.statements().filter_map(into_expr).any(is_early_expr) } +fn is_never_else_branch(sema: &Semantics<'_, RootDatabase>, it: &ast::ElseBranch) -> bool { + match it { + ast::ElseBranch::Block(block_expr) => is_never_block(sema, block_expr), + ast::ElseBranch::IfExpr(if_expr) => { + let mut if_exprs = + std::iter::successors(Some(if_expr.clone()), |it| match it.else_branch()? { + ast::ElseBranch::Block(_) => None, + ast::ElseBranch::IfExpr(if_expr) => Some(if_expr), + }); + if_exprs.all(|it| { + let else_is_never = match it.else_branch() { + None => false, + Some(ast::ElseBranch::IfExpr(_)) => true, + Some(ast::ElseBranch::Block(block)) => is_never_block(sema, &block), + }; + else_is_never && it.then_branch().is_some_and(|it| is_never_block(sema, &it)) + }) + } + } +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -1025,6 +1053,42 @@ fn main() { } #[test] + fn convert_inside_loop_with_else_if() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + loop { + if$0 guard() { + foo(); + bar(); + } else if cond() { + break; + } else { + return + } + } +} +"#, + r#" +fn main() { + loop { + if !guard() { + if cond() { + break; + } else { + return + } + } + foo(); + bar(); + } +} +"#, + ); + } + + #[test] fn convert_let_inside_loop() { check_assist( convert_to_guarded_return, @@ -1055,6 +1119,7 @@ fn main() { check_assist( convert_to_guarded_return, r#" +//- minicore: iterator fn main() { for n in ns { if$0 let Some(n) = n { @@ -1107,6 +1172,7 @@ fn main() { check_assist( convert_to_guarded_return, r#" +//- minicore: iterator fn main() { for n in ns { if$0 let Some(n) = n { @@ -1134,6 +1200,109 @@ fn main() { } #[test] + fn convert_let_inside_for_with_else_if() { + check_assist( + convert_to_guarded_return, + r#" +//- minicore: iterator +fn main() { + for n in ns { + if$0 let Some(n) = n { + foo(n); + bar(); + } else if cond() { + return + } else { + baz() + } + } +} +"#, + r#" +fn main() { + for n in ns { + let Some(n) = n else { + if cond() { + return + } else { + baz() + } + continue + }; + foo(n); + bar(); + } +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +//- minicore: iterator +fn main() { + for n in ns { + if$0 let Some(n) = n { + foo(n); + bar(); + } else if cond() { + return + } + } +} +"#, + r#" +fn main() { + for n in ns { + let Some(n) = n else { + if cond() { + return + } + continue + }; + foo(n); + bar(); + } +} +"#, + ); + + check_assist( + convert_to_guarded_return, + r#" +//- minicore: iterator +fn main() { + for n in ns { + if$0 let Some(n) = n { + foo(n); + bar(); + } else if cond() { + return + } else { + break + } + } +} +"#, + r#" +fn main() { + for n in ns { + let Some(n) = n else { + if cond() { + return + } else { + break + } + }; + foo(n); + bar(); + } +} +"#, + ); + } + + #[test] fn convert_let_stmt_inside_fn() { check_assist( convert_to_guarded_return, @@ -1415,7 +1584,7 @@ fn main() { } #[test] - fn ignore_else_if() { + fn ignore_on_else_if() { check_assist_not_applicable( convert_to_guarded_return, r#" diff --git a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 0af0cbc32a..46e2917f72 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -4,7 +4,7 @@ use ide_db::{ FxHashSet, assists::AssistId, defs::Definition, - helpers::mod_path_to_ast, + helpers::mod_path_to_ast_with_factory, imports::insert_use::{ImportScope, insert_use_with_editor}, search::{FileReference, UsageSearchResult}, source_change::SourceChangeBuilder, @@ -51,7 +51,7 @@ use crate::assist_context::{AssistContext, Assists}; // ``` pub(crate) fn convert_tuple_return_type_to_struct( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; let type_ref = ret_type.ty()?; @@ -104,7 +104,7 @@ pub(crate) fn convert_tuple_return_type_to_struct( /// Replaces tuple usages with the corresponding tuple struct pattern. fn replace_usages( edit: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, usages: &UsageSearchResult, struct_name: &str, target_module: &hir::Module, @@ -176,8 +176,8 @@ fn node_to_pats(node: SyntaxNode) -> Option<Vec<ast::Pat>> { } fn augment_references_with_imports( - syntax_factory: &SyntaxFactory, - ctx: &AssistContext<'_>, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, references: &[FileReference], struct_name: &str, target_module: &hir::Module, @@ -208,12 +208,13 @@ fn augment_references_with_imports( cfg, ) .map(|mod_path| { - syntax_factory.path_concat( - mod_path_to_ast( + make.path_concat( + mod_path_to_ast_with_factory( + make, &mod_path, target_module.krate(ctx.db()).edition(ctx.db()), ), - syntax_factory.path_from_text(struct_name), + make.path_from_text(struct_name), ) }); @@ -231,7 +232,7 @@ fn augment_references_with_imports( fn add_tuple_struct_def( edit: &mut SourceChangeBuilder, syntax_factory: &SyntaxFactory, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, usages: &UsageSearchResult, parent: &SyntaxNode, tuple_ty: &ast::TupleType, diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index afbcf024b9..a6a47d78a8 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -58,7 +58,7 @@ use crate::{ // ``` pub(crate) fn convert_tuple_struct_to_named_struct( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let strukt_or_variant = ctx .find_node_at_offset::<ast::Struct>() @@ -140,7 +140,7 @@ fn edit_struct_def( } fn edit_struct_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, strukt: Either<hir::Struct, hir::EnumVariant>, names: &[ast::Name], @@ -164,7 +164,7 @@ fn edit_struct_references( } fn process_struct_name_reference( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, r: FileReference, editor: &SyntaxEditor, source: &ast::SourceFile, @@ -229,7 +229,7 @@ fn process_struct_name_reference( } fn process_delimiter( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, source: &ast::SourceFile, editor: &SyntaxEditor, list: &impl AstNode, @@ -270,7 +270,7 @@ fn process_delimiter( } fn edit_field_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, fields: impl Iterator<Item = ast::TupleField>, names: &[ast::Name], diff --git a/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs b/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs index 1af5db17f0..73b373dbcb 100644 --- a/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs +++ b/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn convert_two_arm_bool_match_to_matches_macro( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { use ArmBodyExpression::*; let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?; diff --git a/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/crates/ide-assists/src/handlers/convert_while_to_loop.rs index 793e7465c1..94920569a0 100644 --- a/crates/ide-assists/src/handlers/convert_while_to_loop.rs +++ b/crates/ide-assists/src/handlers/convert_while_to_loop.rs @@ -38,7 +38,7 @@ use crate::{ // } // } // ``` -pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let while_kw = ctx.find_token_syntax_at_offset(T![while])?; let while_expr = while_kw.parent().and_then(ast::WhileExpr::cast)?; let while_body = while_expr.loop_body()?; diff --git a/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 9ffce445d1..2b5b2ca13b 100644 --- a/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -3,7 +3,7 @@ use ide_db::{ FxHashMap, FxHashSet, RootDatabase, assists::AssistId, defs::Definition, - helpers::mod_path_to_ast, + helpers::mod_path_to_ast_with_factory, search::{FileReference, SearchScope}, }; use itertools::Itertools; @@ -47,7 +47,10 @@ use crate::{ // let baz2 = &baz; // } // ``` -pub(crate) fn destructure_struct_binding(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn destructure_struct_binding( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let target = ctx.find_node_at_offset::<Target>()?; let data = collect_data(target, ctx)?; @@ -119,7 +122,7 @@ impl AstNode for Target { } fn destructure_struct_binding_impl( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, data: &StructEditData, ) { @@ -173,7 +176,7 @@ impl StructEditData { } } -fn collect_data(target: Target, ctx: &AssistContext<'_>) -> Option<StructEditData> { +fn collect_data(target: Target, ctx: &AssistContext<'_, '_>) -> Option<StructEditData> { let ty = target.ty(&ctx.sema)?; let hir::Adt::Struct(struct_type) = ty.strip_references().as_adt()? else { return None }; @@ -246,7 +249,7 @@ fn collect_data(target: Target, ctx: &AssistContext<'_>) -> Option<StructEditDat } fn get_names_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target: &Target, usages: &[FileReference], ) -> Option<FxHashSet<SmolStr>> { @@ -270,13 +273,13 @@ fn get_names_in_scope( } fn destructure_pat( - _ctx: &AssistContext<'_>, + _ctx: &AssistContext<'_, '_>, editor: &SyntaxEditor, data: &StructEditData, field_names: &[(SmolStr, SmolStr)], ) { let make = editor.make(); - let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition); + let struct_path = mod_path_to_ast_with_factory(make, &data.struct_def_path, data.edition); let is_ref = data.target.is_ref(); let is_mut = data.target.is_mut(); @@ -313,7 +316,10 @@ fn destructure_pat( data.apply_to_destruct(new_pat, editor); } -fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(SmolStr, SmolStr)> { +fn generate_field_names( + ctx: &AssistContext<'_, '_>, + data: &StructEditData, +) -> Vec<(SmolStr, SmolStr)> { match data.kind { hir::StructKind::Tuple => data .visible_fields @@ -348,7 +354,7 @@ fn new_field_name(base_name: SmolStr, names_in_scope: &FxHashSet<SmolStr>) -> Sm } fn update_usages( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, editor: &SyntaxEditor, data: &StructEditData, field_names: &FxHashMap<SmolStr, SmolStr>, @@ -367,7 +373,7 @@ fn update_usages( } fn build_usage_edit( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, data: &StructEditData, usage: &FileReference, diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 291605056b..2755962362 100644 --- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -34,7 +34,10 @@ use crate::{ // let v = _0; // } // ``` -pub(crate) fn destructure_tuple_binding(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn destructure_tuple_binding( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { destructure_tuple_binding_impl(acc, ctx, false) } @@ -58,7 +61,7 @@ pub(crate) fn destructure_tuple_binding(acc: &mut Assists, ctx: &AssistContext<' // ``` pub(crate) fn destructure_tuple_binding_impl( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, with_sub_pattern: bool, ) -> Option<()> { let ident_pat = ctx.find_node_at_offset::<ast::IdentPat>()?; @@ -84,7 +87,7 @@ pub(crate) fn destructure_tuple_binding_impl( } fn destructure_tuple_edit_impl( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, data: &TupleData, in_sub_pattern: bool, @@ -102,7 +105,7 @@ fn destructure_tuple_edit_impl( edit.add_file_edits(ctx.vfs_file_id(), editor); } -fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleData> { +fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_, '_>) -> Option<TupleData> { if ident_pat.at_token().is_some() { // Cannot destructure pattern with sub-pattern: // Only IdentPat can have sub-pattern, @@ -168,7 +171,7 @@ struct TupleData { usages: Option<Vec<FileReference>>, } fn edit_tuple_assignment( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, editor: &SyntaxEditor, data: &TupleData, @@ -235,7 +238,7 @@ impl AssignmentEdit { fn edit_tuple_usages( data: &TupleData, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, in_sub_pattern: bool, ) -> Option<Vec<EditTupleUsage>> { @@ -258,7 +261,7 @@ fn edit_tuple_usages( Some(edits) } fn edit_tuple_usage( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, usage: &FileReference, data: &TupleData, @@ -275,7 +278,7 @@ fn edit_tuple_usage( } fn edit_tuple_field_usage( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, data: &TupleData, index: TupleIndex, @@ -305,7 +308,7 @@ enum EditTupleUsage { impl EditTupleUsage { fn apply( self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, syntax_editor: &SyntaxEditor, ) { @@ -371,7 +374,7 @@ mod tests { // Tests for direct tuple destructure: // `let $0t = (1,2);` -> `let (_0, _1) = (1,2);` - fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + fn assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { destructure_tuple_binding_impl(acc, ctx, false) } @@ -1180,10 +1183,10 @@ fn main { use super::*; use crate::tests::check_assist_by_label; - fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + fn assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { destructure_tuple_binding_impl(acc, ctx, true) } - fn in_place_assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + fn in_place_assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { destructure_tuple_binding_impl(acc, ctx, false) } @@ -1251,7 +1254,7 @@ fn main() { #[test] fn trigger_both_destructure_tuple_assists() { - fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + fn assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { destructure_tuple_binding_impl(acc, ctx, true) } let text = r#" @@ -1787,7 +1790,7 @@ fn main() { // * `?` check_in_place_assist( r#" -//- minicore: option +//- minicore: try, option fn f1(v: i32) {} fn f2(v: &i32) {} trait T { diff --git a/crates/ide-assists/src/handlers/desugar_doc_comment.rs b/crates/ide-assists/src/handlers/desugar_doc_comment.rs index 74bb0ba3f6..e6784a0c3b 100644 --- a/crates/ide-assists/src/handlers/desugar_doc_comment.rs +++ b/crates/ide-assists/src/handlers/desugar_doc_comment.rs @@ -24,7 +24,7 @@ use crate::{ // #[doc = r"Multi-line // comment"] // ``` -pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let comment = ctx.find_token_at_offset::<ast::Comment>()?; // Only allow doc comments let placement = comment.kind().doc?; diff --git a/crates/ide-assists/src/handlers/desugar_try_expr.rs b/crates/ide-assists/src/handlers/desugar_try_expr.rs index fc894f0fe9..5e1cd7700d 100644 --- a/crates/ide-assists/src/handlers/desugar_try_expr.rs +++ b/crates/ide-assists/src/handlers/desugar_try_expr.rs @@ -50,7 +50,7 @@ use crate::assist_context::{AssistContext, Assists}; // }; // } // ``` -pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let question_tok = ctx.find_token_syntax_at_offset(T![?])?; let try_expr = question_tok.parent().and_then(ast::TryExpr::cast)?; diff --git a/crates/ide-assists/src/handlers/expand_glob_import.rs b/crates/ide-assists/src/handlers/expand_glob_import.rs index 1c8cbf5af5..f5d2a6cfc5 100644 --- a/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -41,7 +41,7 @@ use crate::{ // // fn qux(bar: Bar, baz: Baz) {} // ``` -pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let star = ctx.find_token_syntax_at_offset(T![*])?; let use_tree = star.parent().and_then(ast::UseTree::cast)?; let use_item = star.parent_ancestors().find_map(ast::Use::cast)?; @@ -99,7 +99,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> // // pub use foo::{Bar, Baz}; // ``` -pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let star = ctx.find_token_syntax_at_offset(T![*])?; let use_tree = star.parent().and_then(ast::UseTree::cast)?; let use_item = star.parent_ancestors().find_map(ast::Use::cast)?; @@ -140,7 +140,7 @@ pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_>) - } fn build_expanded_import( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, use_tree: UseTree, use_item: Use, @@ -162,7 +162,8 @@ fn build_expanded_import( } }; - let refs_in_target = find_refs_in_mod(ctx, target_module, visible_from, must_be_pub); + let refs_in_target = + find_refs_in_mod(ctx, target_module, current_module, visible_from, must_be_pub); let imported_defs = find_imported_defs(ctx, use_item); let filtered_defs = @@ -237,7 +238,7 @@ fn find_parent_and_path( } } -fn def_is_referenced_in(def: Definition, ctx: &AssistContext<'_>) -> bool { +fn def_is_referenced_in(def: Definition, ctx: &AssistContext<'_, '_>) -> bool { let search_scope = SearchScope::single_file(ctx.file_id()); def.usages(&ctx.sema).in_scope(&search_scope).at_least_one() } @@ -251,7 +252,11 @@ struct Ref { } impl Ref { - fn from_scope_def(ctx: &AssistContext<'_>, name: Name, scope_def: ScopeDef) -> Option<Self> { + fn from_scope_def( + ctx: &AssistContext<'_, '_>, + name: Name, + scope_def: ScopeDef, + ) -> Option<Self> { match scope_def { ScopeDef::ModuleDef(def) => Some(Ref { visible_name: name, @@ -267,7 +272,7 @@ impl Ref { struct Refs(Vec<Ref>); impl Refs { - fn used_refs(&self, ctx: &AssistContext<'_>) -> Refs { + fn used_refs(&self, ctx: &AssistContext<'_, '_>) -> Refs { Refs( self.0 .clone() @@ -297,8 +302,9 @@ impl Refs { } fn find_refs_in_mod( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, expandable: Expandable, + current_module: Module, visible_from: Module, must_be_pub: bool, ) -> Refs { @@ -308,6 +314,10 @@ fn find_refs_in_mod( let refs = module_scope .into_iter() .filter_map(|(n, d)| Ref::from_scope_def(ctx, n, d)) + .filter(|r| match r.def { + Definition::Module(it) => it != current_module, + _ => r.def.module(ctx.db()).map_or(false, |it| it != current_module), + }) .filter(|r| !must_be_pub || r.is_pub) .collect(); Refs(refs) @@ -325,8 +335,8 @@ fn find_refs_in_mod( } } -fn is_visible_from(ctx: &AssistContext<'_>, expandable: &Expandable, from: Module) -> bool { - fn is_mod_visible_from(ctx: &AssistContext<'_>, module: Module, from: Module) -> bool { +fn is_visible_from(ctx: &AssistContext<'_, '_>, expandable: &Expandable, from: Module) -> bool { + fn is_mod_visible_from(ctx: &AssistContext<'_, '_>, module: Module, from: Module) -> bool { match module.parent(ctx.db()) { Some(parent) => { module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into()) @@ -366,7 +376,7 @@ fn is_visible_from(ctx: &AssistContext<'_>, expandable: &Expandable, from: Modul // use foo::*$0; // use baz::Baz; // ↑ --------------- -fn find_imported_defs(ctx: &AssistContext<'_>, use_item: Use) -> Vec<Definition> { +fn find_imported_defs(ctx: &AssistContext<'_, '_>, use_item: Use) -> Vec<Definition> { [Direction::Prev, Direction::Next] .into_iter() .flat_map(|dir| { @@ -440,6 +450,31 @@ fn qux(bar: Bar, baz: Baz) { } #[test] + fn expanding_glob_import_on_cycle_import() { + check_assist( + expand_glob_import, + r" +mod foo { + pub use crate::*$0; + pub struct Foo; + pub fn bar() -> Bar { _ = Foo; Bar } +} +pub use foo::*; +pub struct Bar; +", + r" +mod foo { + pub use crate::Bar; + pub struct Foo; + pub fn bar() -> Bar { _ = Foo; Bar } +} +pub use foo::*; +pub struct Bar; +", + ) + } + + #[test] fn expanding_glob_import_unused() { check_assist( expand_glob_import, diff --git a/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/crates/ide-assists/src/handlers/expand_rest_pattern.rs index dc4976e8c2..4aa11b4e03 100644 --- a/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -29,7 +29,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` fn expand_record_rest_pattern( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, record_pat: ast::RecordPat, rest_pat: ast::RestPat, ) -> Option<()> { @@ -92,7 +92,7 @@ fn expand_record_rest_pattern( // ``` fn expand_tuple_struct_rest_pattern( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, pat: ast::TupleStructPat, rest_pat: ast::RestPat, ) -> Option<()> { @@ -171,7 +171,7 @@ fn expand_tuple_struct_rest_pattern( // ``` fn expand_tuple_rest_pattern( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, pat: ast::TuplePat, rest_pat: ast::RestPat, ) -> Option<()> { @@ -233,7 +233,7 @@ fn expand_tuple_rest_pattern( // ``` fn expand_slice_rest_pattern( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, pat: ast::SlicePat, rest_pat: ast::RestPat, ) -> Option<()> { @@ -277,7 +277,7 @@ fn expand_slice_rest_pattern( ) } -pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?; let parent = rest_pat.syntax().parent()?; match_ast! { @@ -292,7 +292,7 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> } fn gen_unnamed_pat( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, name_gen: &mut NameGenerator, ty: &hir::Type<'_>, diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index c87ded9dc4..178477c746 100644 --- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -30,7 +30,7 @@ use syntax::{ pub(crate) fn extract_expressions_from_format_string( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let fmt_string = ctx.find_token_at_offset::<ast::String>()?; let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?; diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 4219e6845f..44123dc20d 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -1,6 +1,5 @@ use std::{iter, ops::RangeInclusive}; -use ast::make; use either::Either; use hir::{ HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, PathResolution, Semantics, @@ -11,10 +10,9 @@ use ide_db::{ assists::GroupLabel, defs::Definition, famous_defs::FamousDefs, - helpers::mod_path_to_ast, - imports::insert_use::{ImportScope, insert_use}, + helpers::mod_path_to_ast_with_factory, + imports::insert_use::{ImportScope, insert_use_with_editor}, search::{FileReference, ReferenceCategory, SearchScope}, - source_change::SourceChangeBuilder, syntax_helpers::node_ext::{ for_each_tail_expr, preorder_expr, walk_pat, walk_patterns_in_expr, }, @@ -25,16 +23,18 @@ use syntax::{ SyntaxKind::{self, COMMENT}, SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent, ast::{ - self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, edit::IndentLevel, - edit_in_place::Indent, + self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, + edit::{AstNodeEdit, IndentLevel}, + syntax_factory::SyntaxFactory, }, - match_ast, ted, + match_ast, + syntax_editor::{Position, SyntaxEditor}, }; use crate::{ AssistId, - assist_context::{AssistContext, Assists, TreeMutator}, - utils::generate_impl, + assist_context::{AssistContext, Assists}, + utils::generate_impl_with_item, }; // Assist: extract_function @@ -64,7 +64,7 @@ use crate::{ // let k = m + n; // } // ``` -pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let range = ctx.selection_trimmed(); if range.is_empty() { return None; @@ -97,8 +97,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let module = semantics_scope.module(); let edition = semantics_scope.krate().edition(ctx.db()); + let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); let (container_info, contains_tail_expr) = - body.analyze_container(&ctx.sema, edition, trait_name)?; + body.analyze_container(editor.make(), &ctx.sema, edition, trait_name)?; let ret_ty = body.return_ty(ctx)?; let control_flow = body.external_control_flow(ctx, &container_info)?; @@ -114,6 +115,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op "Extract into function", target_range, move |builder| { + let make = editor.make(); let outliving_locals: Vec<_> = ret_values.collect(); if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) { // We should not have variables that outlive body if we have expression block @@ -122,7 +124,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let params = body.extracted_function_params(ctx, &container_info, locals_used); - let name = make_function_name(&semantics_scope, &body); + let name = make_function_name(make, &semantics_scope, &body); let fun = Function { name, @@ -139,67 +141,47 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let new_indent = IndentLevel::from_node(&insert_after); let old_indent = fun.body.indent_level(); - let insert_after = builder.make_syntax_mut(insert_after); + let call_expr = make_call(make, ctx, &fun, old_indent); - let call_expr = make_call(ctx, &fun, old_indent); - - // Map the element range to replace into the mutable version let elements = match &fun.body { FunctionBody::Expr(expr) => { - // expr itself becomes the replacement target - let expr = &builder.make_mut(expr.clone()); let node = SyntaxElement::Node(expr.syntax().clone()); - node.clone()..=node } - FunctionBody::Span { parent, elements, .. } => { - // Map the element range into the mutable versions - let parent = builder.make_mut(parent.clone()); - - let start = parent - .syntax() - .children_with_tokens() - .nth(elements.start().index()) - .expect("should be able to find mutable start element"); - - let end = parent - .syntax() - .children_with_tokens() - .nth(elements.end().index()) - .expect("should be able to find mutable end element"); - - start..=end - } + FunctionBody::Span { elements, .. } => elements.clone(), }; let has_impl_wrapper = insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after); - let fn_def = format_function(ctx, module, &fun, old_indent).clone_for_update(); - - if let Some(cap) = ctx.config.snippet_cap - && let Some(name) = fn_def.name() - { - builder.add_tabstop_before(cap, name); - } + let fn_def = format_function(ctx, module, &fun, old_indent, make); // FIXME: wrap non-adt types let fn_def = match fun.self_param_adt(ctx) { Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => { - fn_def.indent(1.into()); + let fn_def = fn_def.indent_with_mapping(1.into(), make); - let impl_ = generate_impl(&adt); - impl_.indent(new_indent); - impl_.get_or_create_assoc_item_list().add_item(fn_def.into()); + let body = make.assoc_item_list([fn_def.into()]); + let impl_ = generate_impl_with_item(make, &adt, Some(body)).indent(new_indent); impl_.syntax().clone() } - _ => { - fn_def.indent(new_indent); - - fn_def.syntax().clone() - } + _ => fn_def.indent_with_mapping(new_indent, make).syntax().clone(), }; + if let Some(cap) = ctx.config.snippet_cap { + let extracted_fn = fn_def.descendants().find_map(ast::Fn::cast); + if let Some(fn_) = extracted_fn { + if let Some(ws) = fn_ + .fn_token() + .and_then(|tok| tok.next_token()) + .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE) + { + editor.add_annotation(ws, builder.make_tabstop_after(cap)); + } else if let Some(name) = fn_.name() { + editor.add_annotation(name.syntax(), builder.make_tabstop_before(cap)); + } + } + } // There are external control flows if fun @@ -207,7 +189,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op .kind .is_some_and(|kind| matches!(kind, FlowKind::Break(_, _) | FlowKind::Continue(_))) { - let scope = builder.make_import_scope_mut(scope); let control_flow_enum = FamousDefs(&ctx.sema, module.krate(ctx.db())).core_ops_ControlFlow(); @@ -222,29 +203,45 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op ); if let Some(mod_path) = mod_path { - insert_use( + insert_use_with_editor( &scope, - mod_path_to_ast(&mod_path, edition), + mod_path_to_ast_with_factory(make, &mod_path, edition), &ctx.config.insert_use, + &editor, ); } } } // Replace the call site with the call to the new function - fixup_call_site(builder, &fun.body); - ted::replace_all(elements, vec![call_expr.into()]); + let needs_match_arm_comma = fun + .body + .parent() + .and_then(ast::MatchArm::cast) + .is_some_and(|arm| arm.comma_token().is_none()); + match &fun.body { + FunctionBody::Expr(expr) => { + let mut replacement = vec![call_expr.clone().into()]; + if needs_match_arm_comma { + replacement.push(make.token(T![,]).into()); + } + editor.replace_with_many(expr.syntax(), replacement); + } + FunctionBody::Span { .. } => editor.replace_all(elements, vec![call_expr.into()]), + } // Insert the newly extracted function (or impl) - ted::insert_all_raw( - ted::Position::after(insert_after), - vec![make::tokens::whitespace(&format!("\n\n{new_indent}")).into(), fn_def.into()], + editor.insert_all( + Position::after(insert_after), + vec![make.whitespace(&format!("\n\n{new_indent}")).into(), fn_def.into()], ); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } fn make_function_name( + make: &SyntaxFactory, semantics_scope: &hir::SemanticsScope<'_>, body: &FunctionBody, ) -> ast::NameRef { @@ -267,7 +264,7 @@ fn make_function_name( counter += 1; name = format!("{default_name}{counter}") } - make::name_ref(&name) + make.name_ref(&name) } /// Try to guess what user wants to extract @@ -455,7 +452,7 @@ struct OutlivedLocal { struct LocalUsages(ide_db::search::UsageSearchResult); impl LocalUsages { - fn find_local_usages(ctx: &AssistContext<'_>, var: Local) -> Self { + fn find_local_usages(ctx: &AssistContext<'_, '_>, var: Local) -> Self { Self( Definition::Local(var) .usages(&ctx.sema) @@ -470,7 +467,7 @@ impl LocalUsages { } impl<'db> Function<'db> { - fn return_type(&self, ctx: &AssistContext<'db>) -> FunType<'db> { + fn return_type(&self, ctx: &AssistContext<'_, 'db>) -> FunType<'db> { match &self.ret_ty { RetType::Expr(ty) if ty.is_unit() => FunType::Unit, RetType::Expr(ty) => FunType::Single(ty.clone()), @@ -485,7 +482,7 @@ impl<'db> Function<'db> { } } - fn self_param_adt(&self, ctx: &AssistContext<'_>) -> Option<ast::Adt> { + fn self_param_adt(&self, ctx: &AssistContext<'_, '_>) -> Option<ast::Adt> { let self_param = self.self_param.as_ref()?; let def = ctx.sema.to_def(self_param)?; let adt = def.ty(ctx.db()).strip_references().as_adt()?; @@ -510,45 +507,51 @@ impl<'db> Param<'db> { } } - fn to_arg(&self, ctx: &AssistContext<'db>, edition: Edition) -> ast::Expr { - let var = path_expr_from_local(ctx, self.var, edition); + fn to_arg( + &self, + make: &SyntaxFactory, + ctx: &AssistContext<'_, 'db>, + edition: Edition, + ) -> ast::Expr { + let var = path_expr_from_local(make, ctx, self.var, edition); match self.kind() { ParamKind::Value | ParamKind::MutValue => var, - ParamKind::SharedRef => make::expr_ref(var, false), - ParamKind::MutRef => make::expr_ref(var, true), + ParamKind::SharedRef => make.expr_ref(var, false), + ParamKind::MutRef => make.expr_ref(var, true), } } fn to_param( &self, - ctx: &AssistContext<'_>, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, module: hir::Module, edition: Edition, ) -> ast::Param { let var = self.var.name(ctx.db()).display(ctx.db(), edition).to_string(); - let var_name = make::name(&var); + let var_name = make.name(&var); let pat = match self.kind() { - ParamKind::MutValue => make::ident_pat(false, true, var_name), + ParamKind::MutValue => make.ident_pat(false, true, var_name), ParamKind::Value | ParamKind::SharedRef | ParamKind::MutRef => { - make::ext::simple_ident_pat(var_name) + make.simple_ident_pat(var_name) } }; - let ty = make_ty(&self.ty, ctx, module); + let ty = make_ty(make, &self.ty, ctx, module); let ty = match self.kind() { ParamKind::Value | ParamKind::MutValue => ty, - ParamKind::SharedRef => make::ty_ref(ty, false), - ParamKind::MutRef => make::ty_ref(ty, true), + ParamKind::SharedRef => make.ty_ref(ty, false), + ParamKind::MutRef => make.ty_ref(ty, true), }; - make::param(pat.into(), ty) + make.param(pat.into(), ty) } } impl<'db> TryKind<'db> { fn of_ty( ty: hir::Type<'db>, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, edition: Edition, ) -> Option<TryKind<'db>> { if ty.is_unknown() { @@ -569,22 +572,22 @@ impl<'db> TryKind<'db> { } impl<'db> FlowKind<'db> { - fn make_result_handler(&self, expr: Option<ast::Expr>) -> ast::Expr { + fn make_result_handler(&self, make: &SyntaxFactory, expr: Option<ast::Expr>) -> ast::Expr { match self { - FlowKind::Return(_) => make::expr_return(expr), - FlowKind::Break(label, _) => make::expr_break(label.clone(), expr), + FlowKind::Return(_) => make.expr_return(expr).into(), + FlowKind::Break(label, _) => make.expr_break(label.clone(), expr).into(), FlowKind::Try { .. } => { stdx::never!("cannot have result handler with try"); - expr.unwrap_or_else(|| make::expr_return(None)) + expr.unwrap_or_else(|| make.expr_return(None).into()) } FlowKind::Continue(label) => { stdx::always!(expr.is_none(), "continue with value is not possible"); - make::expr_continue(label.clone()) + make.expr_continue(label.clone()).into() } } } - fn expr_ty(&self, ctx: &AssistContext<'db>) -> Option<hir::Type<'db>> { + fn expr_ty(&self, ctx: &AssistContext<'_, 'db>) -> Option<hir::Type<'db>> { match self { FlowKind::Return(Some(expr)) | FlowKind::Break(_, Some(expr)) => { ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted) @@ -840,6 +843,7 @@ impl FunctionBody { fn analyze_container<'db>( &self, + make: &SyntaxFactory, sema: &Semantics<'db, RootDatabase>, edition: Edition, trait_name: Option<ast::Name>, @@ -930,7 +934,7 @@ impl FunctionBody { }; // FIXME: make trait arguments - let trait_name = trait_name.map(|name| make::ty_path(make::ext::ident_path(&name.text()))); + let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(&name.text())).into()); let parent = self.parent()?; let parents = generic_parents(&parent); @@ -953,7 +957,7 @@ impl FunctionBody { )) } - fn return_ty<'db>(&self, ctx: &AssistContext<'db>) -> Option<RetType<'db>> { + fn return_ty<'db>(&self, ctx: &AssistContext<'_, 'db>) -> Option<RetType<'db>> { match self.tail_expr() { Some(expr) => ctx.sema.type_of_expr(&expr).map(TypeInfo::original).map(RetType::Expr), None => Some(RetType::Stmt), @@ -963,7 +967,7 @@ impl FunctionBody { /// Local variables defined inside `body` that are accessed outside of it fn ret_values<'a>( &self, - ctx: &'a AssistContext<'_>, + ctx: &'a AssistContext<'_, '_>, parent: &SyntaxNode, ) -> impl Iterator<Item = OutlivedLocal> + 'a { let parent = parent.clone(); @@ -976,7 +980,7 @@ impl FunctionBody { /// Analyses the function body for external control flow. fn external_control_flow<'db>( &self, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, container_info: &ContainerInfo<'db>, ) -> Option<ControlFlow<'db>> { let mut ret_expr = None; @@ -1066,7 +1070,7 @@ impl FunctionBody { /// Computes additional info that affects param type and mutability fn extracted_function_params<'db>( &self, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, container_info: &ContainerInfo<'db>, locals: FxIndexSet<Local>, ) -> Vec<Param<'db>> { @@ -1163,7 +1167,7 @@ fn generic_parents(parent: &SyntaxNode) -> Vec<GenericParent> { /// checks if relevant var is used with `&mut` access inside body fn has_exclusive_usages( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, usages: &LocalUsages, body: &FunctionBody, ) -> bool { @@ -1177,7 +1181,7 @@ fn has_exclusive_usages( fn reference_is_exclusive( reference: &FileReference, node: &dyn HasTokenAtOffset, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> bool { // FIXME: this quite an incorrect way to go about doing this :-) // `FileReference` is an IDE-type --- it encapsulates data communicated to the human, @@ -1191,7 +1195,7 @@ fn reference_is_exclusive( } // we take `&mut` reference to variable: `&mut v` - let path = match path_element_of_reference(node, reference) { + let path = match path_element_of_reference(node, reference.range) { Some(path) => path, None => return false, }; @@ -1200,7 +1204,7 @@ fn reference_is_exclusive( } /// checks if this expr requires `&mut` access, recurses on field access -fn expr_require_exclusive_access(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option<bool> { +fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option<bool> { if let ast::Expr::MacroExpr(_) = expr { // FIXME: expand macro and check output for mutable usages of the variable? return None; @@ -1282,10 +1286,10 @@ impl HasTokenAtOffset for FunctionBody { /// `node` must cover `reference`, that is `node.text_range().contains_range(reference.range)` fn path_element_of_reference( node: &dyn HasTokenAtOffset, - reference: &FileReference, + reference_range: TextRange, ) -> Option<ast::Expr> { - let token = node.token_at_offset(reference.range.start()).right_biased().or_else(|| { - stdx::never!(false, "cannot find token at variable usage: {:?}", reference); + let token = node.token_at_offset(reference_range.start()).right_biased().or_else(|| { + stdx::never!(false, "cannot find token at variable usage: {:?}", reference_range); None })?; let path = token.parent_ancestors().find_map(ast::Expr::cast).or_else(|| { @@ -1320,7 +1324,7 @@ fn locals_defined_in_body( /// Returns usage details if local variable is used after(outside of) body fn local_outlives_body( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, body_range: TextRange, local: Local, parent: &SyntaxNode, @@ -1345,7 +1349,7 @@ fn local_outlives_body( /// checks if the relevant local was defined before(outside of) body fn is_defined_outside_of_body( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, body: &FunctionBody, src: &LocalSource, ) -> bool { @@ -1413,58 +1417,50 @@ fn impl_type_name(impl_node: &ast::Impl) -> Option<String> { Some(impl_node.self_ty()?.to_string()) } -/// Fixes up the call site before the target expressions are replaced with the call expression -fn fixup_call_site(builder: &mut SourceChangeBuilder, body: &FunctionBody) { - let parent_match_arm = body.parent().and_then(ast::MatchArm::cast); - - if let Some(parent_match_arm) = parent_match_arm - && parent_match_arm.comma_token().is_none() - { - let parent_match_arm = builder.make_mut(parent_match_arm); - ted::append_child_raw(parent_match_arm.syntax(), make::token(T![,])); - } -} - -fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) -> SyntaxNode { +fn make_call<'db>( + make: &SyntaxFactory, + ctx: &AssistContext<'_, 'db>, + fun: &Function<'db>, + indent: IndentLevel, +) -> SyntaxNode { let ret_ty = fun.return_type(ctx); let name = fun.name.clone(); - let args = fun.params.iter().map(|param| param.to_arg(ctx, fun.mods.edition)); + let args = fun.params.iter().map(|param| param.to_arg(make, ctx, fun.mods.edition)); let mut call_expr = if fun.make_this_param().is_some() { - let self_arg = make::expr_path(make::ext::ident_path("self")); - let func = make::expr_path(make::path_unqualified(make::path_segment(name))); - make::expr_call(func, make::arg_list(Some(self_arg).into_iter().chain(args))).into() + let self_arg = make.expr_path(make.ident_path("self")); + let func = make.expr_path(make.path_unqualified(make.path_segment(name))); + make.expr_call(func, make.arg_list(Some(self_arg).into_iter().chain(args))).into() } else if fun.self_param.is_some() { - let self_arg = make::expr_path(make::ext::ident_path("self")); - make::expr_method_call(self_arg, name, make::arg_list(args)).into() + let self_arg = make.expr_path(make.ident_path("self")); + make.expr_method_call(self_arg, name, make.arg_list(args)).into() } else { - let func = make::expr_path(make::path_unqualified(make::path_segment(name))); - make::expr_call(func, make::arg_list(args)).into() + let func = make.expr_path(make.path_unqualified(make.path_segment(name))); + make.expr_call(func, make.arg_list(args)).into() }; let handler = FlowHandler::from_ret_ty(fun, &ret_ty); if fun.control_flow.is_async { - call_expr = make::expr_await(call_expr); + call_expr = make.expr_await(call_expr).into(); } - let expr = handler.make_call_expr(call_expr).clone_for_update(); - expr.indent(indent); + let expr = handler.make_call_expr(make, call_expr).indent_with_mapping(indent, make); let outliving_bindings = match fun.outliving_locals.as_slice() { [] => None, [var] => { let name = var.local.name(ctx.db()); - let name = make::name(&name.display(ctx.db(), fun.mods.edition).to_string()); - Some(ast::Pat::IdentPat(make::ident_pat(false, var.mut_usage_outside_body, name))) + let name = make.name(&name.display(ctx.db(), fun.mods.edition).to_string()); + Some(ast::Pat::IdentPat(make.ident_pat(false, var.mut_usage_outside_body, name))) } vars => { let binding_pats = vars.iter().map(|var| { let name = var.local.name(ctx.db()); - let name = make::name(&name.display(ctx.db(), fun.mods.edition).to_string()); - make::ident_pat(false, var.mut_usage_outside_body, name).into() + let name = make.name(&name.display(ctx.db(), fun.mods.edition).to_string()); + make.ident_pat(false, var.mut_usage_outside_body, name).into() }); - Some(ast::Pat::TuplePat(make::tuple_pat(binding_pats))) + Some(ast::Pat::TuplePat(make.tuple_pat(binding_pats))) } }; @@ -1472,7 +1468,7 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) - if let Some(bindings) = outliving_bindings { // with bindings that outlive it - make::let_stmt(bindings, None, Some(expr)).syntax().clone_for_update() + make.let_stmt(bindings, None, Some(expr)).syntax().clone() } else if parent_match_arm.as_ref().is_some() { // as a tail expr for a match arm expr.syntax().clone() @@ -1481,7 +1477,7 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) - && (!fun.outliving_locals.is_empty() || !expr.is_block_like()) { // as an expr stmt - make::expr_stmt(expr).syntax().clone_for_update() + make.expr_stmt(expr).syntax().clone() } else { // as a tail expr, or a block expr.syntax().clone() @@ -1527,100 +1523,106 @@ impl<'db> FlowHandler<'db> { } } - fn make_call_expr(&self, call_expr: ast::Expr) -> ast::Expr { + fn make_call_expr(&self, make: &SyntaxFactory, call_expr: ast::Expr) -> ast::Expr { match self { FlowHandler::None => call_expr, - FlowHandler::Try { kind: _ } => make::expr_try(call_expr), + FlowHandler::Try { kind: _ } => make.expr_try(call_expr), FlowHandler::If { action } => { - let action = action.make_result_handler(None); - let stmt = make::expr_stmt(action); - let block = make::block_expr(iter::once(stmt.into()), None); - let controlflow_break_path = make::path_from_text("ControlFlow::Break"); - let condition = make::expr_let( - make::tuple_struct_pat( + let action = action.make_result_handler(make, None); + let stmt = make.expr_stmt(action); + let block = make.block_expr(iter::once(stmt.into()), None); + let controlflow_break_path = make.path_from_text("ControlFlow::Break"); + let condition = make.expr_let( + make.tuple_struct_pat( controlflow_break_path, - iter::once(make::wildcard_pat().into()), + iter::once(make.wildcard_pat().into()), ) .into(), call_expr, ); - make::expr_if(condition.into(), block, None).into() + make.expr_if(condition.into(), block, None).into() } FlowHandler::IfOption { action } => { - let path = make::ext::ident_path("Some"); - let value_pat = make::ext::simple_ident_pat(make::name("value")); - let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); - let cond = make::expr_let(pattern.into(), call_expr); - let value = make::expr_path(make::ext::ident_path("value")); - let action_expr = action.make_result_handler(Some(value)); - let action_stmt = make::expr_stmt(action_expr); - let then = make::block_expr(iter::once(action_stmt.into()), None); - make::expr_if(cond.into(), then, None).into() + let path = make.ident_path("Some"); + let value_pat = make.simple_ident_pat(make.name("value")); + let pattern = make.tuple_struct_pat(path, iter::once(value_pat.into())); + let cond = make.expr_let(pattern.into(), call_expr); + let value = make.expr_path(make.ident_path("value")); + let action_expr = action.make_result_handler(make, Some(value)); + let action_stmt = make.expr_stmt(action_expr); + let then = make.block_expr(iter::once(action_stmt.into()), None); + make.expr_if(cond.into(), then, None).into() } FlowHandler::MatchOption { none } => { let some_name = "value"; let some_arm = { - let path = make::ext::ident_path("Some"); - let value_pat = make::ext::simple_ident_pat(make::name(some_name)); - let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); - let value = make::expr_path(make::ext::ident_path(some_name)); - make::match_arm(pat.into(), None, value) + let path = make.ident_path("Some"); + let value_pat = make.simple_ident_pat(make.name(some_name)); + let pat = make.tuple_struct_pat(path, iter::once(value_pat.into())); + let value = make.expr_path(make.ident_path(some_name)); + make.match_arm(pat.into(), None, value) }; let none_arm = { - let path = make::ext::ident_path("None"); - let pat = make::path_pat(path); - make::match_arm(pat, None, none.make_result_handler(None)) + let path = make.ident_path("None"); + let pat = make.path_pat(path); + make.match_arm(pat, None, none.make_result_handler(make, None)) }; - let arms = make::match_arm_list(vec![some_arm, none_arm]); - make::expr_match(call_expr, arms).into() + let arms = make.match_arm_list(vec![some_arm, none_arm]); + make.expr_match(call_expr, arms).into() } FlowHandler::MatchResult { err } => { let ok_name = "value"; let err_name = "value"; let ok_arm = { - let path = make::ext::ident_path("Ok"); - let value_pat = make::ext::simple_ident_pat(make::name(ok_name)); - let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); - let value = make::expr_path(make::ext::ident_path(ok_name)); - make::match_arm(pat.into(), None, value) + let path = make.ident_path("Ok"); + let value_pat = make.simple_ident_pat(make.name(ok_name)); + let pat = make.tuple_struct_pat(path, iter::once(value_pat.into())); + let value = make.expr_path(make.ident_path(ok_name)); + make.match_arm(pat.into(), None, value) }; let err_arm = { - let path = make::ext::ident_path("Err"); - let value_pat = make::ext::simple_ident_pat(make::name(err_name)); - let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); - let value = make::expr_path(make::ext::ident_path(err_name)); - make::match_arm(pat.into(), None, err.make_result_handler(Some(value))) + let path = make.ident_path("Err"); + let value_pat = make.simple_ident_pat(make.name(err_name)); + let pat = make.tuple_struct_pat(path, iter::once(value_pat.into())); + let value = make.expr_path(make.ident_path(err_name)); + make.match_arm(pat.into(), None, err.make_result_handler(make, Some(value))) }; - let arms = make::match_arm_list(vec![ok_arm, err_arm]); - make::expr_match(call_expr, arms).into() + let arms = make.match_arm_list(vec![ok_arm, err_arm]); + make.expr_match(call_expr, arms).into() } } } } -fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local, edition: Edition) -> ast::Expr { +fn path_expr_from_local( + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, + var: Local, + edition: Edition, +) -> ast::Expr { let name = var.name(ctx.db()).display(ctx.db(), edition).to_string(); - make::expr_path(make::ext::ident_path(&name)) + make.expr_path(make.ident_path(&name)) } -fn format_function( - ctx: &AssistContext<'_>, +fn format_function<'db>( + ctx: &AssistContext<'_, 'db>, module: hir::Module, - fun: &Function<'_>, + fun: &Function<'db>, old_indent: IndentLevel, + make: &SyntaxFactory, ) -> ast::Fn { - let fun_name = make::name(&fun.name.text()); - let params = fun.make_param_list(ctx, module, fun.mods.edition); - let ret_ty = fun.make_ret_ty(ctx, module); - let body = make_body(ctx, old_indent, fun); - let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun); + let fun_name = make.name(&fun.name.text()); + let params = fun.make_param_list(make, ctx, module, fun.mods.edition); + let ret_ty = fun.make_ret_ty(make, ctx, module); + let body = make_body(make, ctx, old_indent, fun); + let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, make, fun); - make::fn_( + make.fn_( fun.mods.attrs.clone(), None, - fun_name, + fun_name.clone(), generic_params, where_clause, params, @@ -1633,21 +1635,23 @@ fn format_function( ) } -fn make_generic_params_and_where_clause( - ctx: &AssistContext<'_>, - fun: &Function<'_>, +fn make_generic_params_and_where_clause<'db>( + ctx: &AssistContext<'_, 'db>, + make: &SyntaxFactory, + fun: &Function<'db>, ) -> (Option<ast::GenericParamList>, Option<ast::WhereClause>) { let used_type_params = fun.type_params(ctx); - let generic_param_list = make_generic_param_list(ctx, fun, &used_type_params); - let where_clause = make_where_clause(ctx, fun, &used_type_params); + let generic_param_list = make_generic_param_list(ctx, make, fun, &used_type_params); + let where_clause = make_where_clause(ctx, make, fun, &used_type_params); (generic_param_list, where_clause) } -fn make_generic_param_list( - ctx: &AssistContext<'_>, - fun: &Function<'_>, +fn make_generic_param_list<'db>( + ctx: &AssistContext<'_, 'db>, + make: &SyntaxFactory, + fun: &Function<'db>, used_type_params: &[TypeParam], ) -> Option<ast::GenericParamList> { let mut generic_params = fun @@ -1662,14 +1666,14 @@ fn make_generic_param_list( .peekable(); if generic_params.peek().is_some() { - Some(make::generic_param_list(generic_params)) + Some(make.generic_param_list(generic_params)) } else { None } } fn param_is_required( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, param: &ast::GenericParam, used_type_params: &[TypeParam], ) -> bool { @@ -1683,7 +1687,8 @@ fn param_is_required( } fn make_where_clause( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, + make: &SyntaxFactory, fun: &Function<'_>, used_type_params: &[TypeParam], ) -> Option<ast::WhereClause> { @@ -1698,11 +1703,11 @@ fn make_where_clause( }) .peekable(); - if predicates.peek().is_some() { Some(make::where_clause(predicates)) } else { None } + if predicates.peek().is_some() { Some(make.where_clause(predicates)) } else { None } } fn pred_is_required( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, pred: &ast::WherePred, used_type_params: &[TypeParam], ) -> bool { @@ -1712,7 +1717,7 @@ fn pred_is_required( } } -fn resolved_type_param(ctx: &AssistContext<'_>, pred: &ast::WherePred) -> Option<TypeParam> { +fn resolved_type_param(ctx: &AssistContext<'_, '_>, pred: &ast::WherePred) -> Option<TypeParam> { let path = match pred.ty()? { ast::Type::PathType(path_type) => path_type.path(), _ => None, @@ -1726,7 +1731,7 @@ fn resolved_type_param(ctx: &AssistContext<'_>, pred: &ast::WherePred) -> Option impl<'db> Function<'db> { /// Collect all the `TypeParam`s used in the `body` and `params`. - fn type_params(&self, ctx: &AssistContext<'db>) -> Vec<TypeParam> { + fn type_params(&self, ctx: &AssistContext<'_, 'db>) -> Vec<TypeParam> { let type_params_in_descendant_paths = self.body.descendant_paths().filter_map(|it| match ctx.sema.resolve_path(&it) { Some(PathResolution::TypeParam(type_param)) => Some(type_param), @@ -1738,35 +1743,41 @@ impl<'db> Function<'db> { fn make_param_list( &self, - ctx: &AssistContext<'_>, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, module: hir::Module, edition: Edition, ) -> ast::ParamList { - let this_param = self.make_this_param().map(|f| f()); + let this_param = self.make_this_param().map(|f| f(make)); let self_param = self.self_param.clone().filter(|_| this_param.is_none()); - let params = self.params.iter().map(|param| param.to_param(ctx, module, edition)); - make::param_list(self_param, this_param.into_iter().chain(params)) + let params = self.params.iter().map(|param| param.to_param(make, ctx, module, edition)); + make.param_list(self_param, this_param.into_iter().chain(params)) } - fn make_this_param(&self) -> Option<impl FnOnce() -> ast::Param> { + fn make_this_param(&self) -> Option<impl FnOnce(&SyntaxFactory) -> ast::Param> { if let Some(name) = self.mods.trait_name.clone() && let Some(self_param) = &self.self_param { - Some(|| { - let bounds = make::type_bound_list([make::type_bound(name)]); - let pat = make::path_pat(make::ext::ident_path("this")); - let mut ty = make::impl_trait_type(bounds.unwrap()).into(); + Some(move |make: &SyntaxFactory| { + let bounds = make.type_bound_list([make.type_bound(name)]); + let pat = make.path_pat(make.ident_path("this")); + let mut ty = make.impl_trait_type(bounds.unwrap()).into(); if self_param.amp_token().is_some() { - ty = make::ty_ref(ty, self_param.mut_token().is_some()); + ty = make.ty_ref(ty, self_param.mut_token().is_some()); } - make::param(pat, ty) + make.param(pat, ty) }) } else { None } } - fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> { + fn make_ret_ty( + &self, + make: &SyntaxFactory, + ctx: &AssistContext<'_, 'db>, + module: hir::Module, + ) -> Option<ast::RetType> { let fun_ty = self.return_type(ctx); let handler = FlowHandler::from_ret_ty(self, &fun_ty); let ret_ty = match &handler { @@ -1774,67 +1785,75 @@ impl<'db> Function<'db> { if matches!(fun_ty, FunType::Unit) { return None; } - fun_ty.make_ty(ctx, module) + fun_ty.make_ty(make, ctx, module) } FlowHandler::Try { kind: TryKind::Option } => { - make::ext::ty_option(fun_ty.make_ty(ctx, module)) + make.ty_option(fun_ty.make_ty(make, ctx, module)).into() } FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { let handler_ty = parent_ret_ty .type_arguments() .nth(1) - .map(|ty| make_ty(&ty, ctx, module)) - .unwrap_or_else(make::ty_placeholder); - make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty) + .map(|ty| make_ty(make, &ty, ctx, module)) + .unwrap_or_else(|| make.ty_placeholder()); + make.ty_result(fun_ty.make_ty(make, ctx, module), handler_ty).into() } - FlowHandler::If { .. } => make::ty("ControlFlow<()>"), + FlowHandler::If { .. } => make.ty("ControlFlow<()>"), FlowHandler::IfOption { action } => { let handler_ty = action .expr_ty(ctx) - .map(|ty| make_ty(&ty, ctx, module)) - .unwrap_or_else(make::ty_placeholder); - make::ext::ty_option(handler_ty) + .map(|ty| make_ty(make, &ty, ctx, module)) + .unwrap_or_else(|| make.ty_placeholder()); + make.ty_option(handler_ty).into() + } + FlowHandler::MatchOption { .. } => { + make.ty_option(fun_ty.make_ty(make, ctx, module)).into() } - FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)), FlowHandler::MatchResult { err } => { let handler_ty = err .expr_ty(ctx) - .map(|ty| make_ty(&ty, ctx, module)) - .unwrap_or_else(make::ty_placeholder); - make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty) + .map(|ty| make_ty(make, &ty, ctx, module)) + .unwrap_or_else(|| make.ty_placeholder()); + make.ty_result(fun_ty.make_ty(make, ctx, module), handler_ty).into() } }; - Some(make::ret_type(ret_ty)) + Some(make.ret_type(ret_ty)) } } impl<'db> FunType<'db> { - fn make_ty(&self, ctx: &AssistContext<'db>, module: hir::Module) -> ast::Type { + fn make_ty( + &self, + make: &SyntaxFactory, + ctx: &AssistContext<'_, 'db>, + module: hir::Module, + ) -> ast::Type { match self { - FunType::Unit => make::ty_unit(), - FunType::Single(ty) => make_ty(ty, ctx, module), + FunType::Unit => make.ty_unit(), + FunType::Single(ty) => make_ty(make, ty, ctx, module), FunType::Tuple(types) => match types.as_slice() { [] => { stdx::never!("tuple type with 0 elements"); - make::ty_unit() + make.ty_unit() } [ty] => { stdx::never!("tuple type with 1 element"); - make_ty(ty, ctx, module) + make_ty(make, ty, ctx, module) } types => { - let types = types.iter().map(|ty| make_ty(ty, ctx, module)); - make::ty_tuple(types) + let types = types.iter().map(|ty| make_ty(make, ty, ctx, module)); + make.ty_tuple(types) } }, } } } -fn make_body( - ctx: &AssistContext<'_>, +fn make_body<'db>( + make: &SyntaxFactory, + ctx: &AssistContext<'_, 'db>, old_indent: IndentLevel, - fun: &Function<'_>, + fun: &Function<'db>, ) -> ast::BlockExpr { let ret_ty = fun.return_type(ctx); let handler = FlowHandler::from_ret_ty(fun, &ret_ty); @@ -1848,7 +1867,7 @@ fn make_body( match expr { ast::Expr::BlockExpr(block) => { // If the extracted expression is itself a block, there is no need to wrap it inside another block. - block.dedent(old_indent); + let block = block.dedent(old_indent); let elements = block.stmt_list().map_or_else( || Either::Left(iter::empty()), |stmt_list| { @@ -1865,12 +1884,12 @@ fn make_body( Either::Right(elements) }, ); - make::hacky_block_expr(elements, block.tail_expr()) + make.hacky_block_expr(elements, block.tail_expr()) } _ => { - expr.reindent_to(1.into()); + let expr = expr.dedent(old_indent).indent(1.into()); - make::block_expr(Vec::new(), Some(expr)) + make.block_expr(Vec::new(), Some(expr)) } } } @@ -1901,13 +1920,14 @@ fn make_body( None => match fun.outliving_locals.as_slice() { [] => {} [var] => { - tail_expr = Some(path_expr_from_local(ctx, var.local, fun.mods.edition)); + tail_expr = + Some(path_expr_from_local(make, ctx, var.local, fun.mods.edition)); } vars => { - let exprs = vars - .iter() - .map(|var| path_expr_from_local(ctx, var.local, fun.mods.edition)); - let expr = make::expr_tuple(exprs); + let exprs = vars.iter().map(|var| { + path_expr_from_local(make, ctx, var.local, fun.mods.edition) + }); + let expr = make.expr_tuple(exprs); tail_expr = Some(expr.into()); } }, @@ -1919,80 +1939,90 @@ fn make_body( .map(|node_or_token| match &node_or_token { syntax::NodeOrToken::Node(node) => match ast::Stmt::cast(node.clone()) { Some(stmt) => { - stmt.reindent_to(body_indent); - let ast_node = stmt.syntax().clone_subtree(); - syntax::NodeOrToken::Node(ast_node) + let stmt = stmt.dedent(old_indent).indent(body_indent); + syntax::NodeOrToken::Node(stmt.syntax().clone()) } _ => node_or_token, }, _ => node_or_token, }) .collect::<Vec<SyntaxElement>>(); - if let Some(tail_expr) = &mut tail_expr { - tail_expr.reindent_to(body_indent); - } + tail_expr = tail_expr.map(|expr| expr.dedent(old_indent).indent(body_indent)); - make::hacky_block_expr(elements, tail_expr) + make.hacky_block_expr(elements, tail_expr) } }; match &handler { FlowHandler::None => block, FlowHandler::Try { kind } => { - let block = with_default_tail_expr(block, make::ext::expr_unit()); - map_tail_expr(block, |tail_expr| { + let block = with_default_tail_expr(make, block, make.expr_unit()); + map_tail_expr(make, block, |tail_expr| { let constructor = match kind { TryKind::Option => "Some", TryKind::Result { .. } => "Ok", }; - let func = make::expr_path(make::ext::ident_path(constructor)); - let args = make::arg_list(iter::once(tail_expr)); - make::expr_call(func, args).into() + let func = make.expr_path(make.ident_path(constructor)); + let args = make.arg_list(iter::once(tail_expr)); + make.expr_call(func, args).into() }) } FlowHandler::If { .. } => { - let controlflow_continue = make::expr_call( - make::expr_path(make::path_from_text("ControlFlow::Continue")), - make::arg_list([make::ext::expr_unit()]), - ) - .into(); - with_tail_expr(block, controlflow_continue) + let controlflow_continue = make + .expr_call( + make.expr_path(make.path_from_text("ControlFlow::Continue")), + make.arg_list([make.expr_unit()]), + ) + .into(); + with_tail_expr(make, block, controlflow_continue) } FlowHandler::IfOption { .. } => { - let none = make::expr_path(make::ext::ident_path("None")); - with_tail_expr(block, none) + let none = make.expr_path(make.ident_path("None")); + with_tail_expr(make, block, none) } - FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { - let some = make::expr_path(make::ext::ident_path("Some")); - let args = make::arg_list(iter::once(tail_expr)); - make::expr_call(some, args).into() + FlowHandler::MatchOption { .. } => map_tail_expr(make, block, |tail_expr| { + let some = make.expr_path(make.ident_path("Some")); + let args = make.arg_list(iter::once(tail_expr)); + make.expr_call(some, args).into() }), - FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { - let ok = make::expr_path(make::ext::ident_path("Ok")); - let args = make::arg_list(iter::once(tail_expr)); - make::expr_call(ok, args).into() + FlowHandler::MatchResult { .. } => map_tail_expr(make, block, |tail_expr| { + let ok = make.expr_path(make.ident_path("Ok")); + let args = make.arg_list(iter::once(tail_expr)); + make.expr_call(ok, args).into() }), } } -fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr { +fn map_tail_expr( + make: &SyntaxFactory, + block: ast::BlockExpr, + f: impl FnOnce(ast::Expr) -> ast::Expr, +) -> ast::BlockExpr { let tail_expr = match block.tail_expr() { Some(tail_expr) => tail_expr, None => return block, }; - make::block_expr(block.statements(), Some(f(tail_expr))) + make.block_expr(block.statements(), Some(f(tail_expr))) } -fn with_default_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr { +fn with_default_tail_expr( + make: &SyntaxFactory, + block: ast::BlockExpr, + tail_expr: ast::Expr, +) -> ast::BlockExpr { match block.tail_expr() { Some(_) => block, - None => make::block_expr(block.statements(), Some(tail_expr)), + None => make.block_expr(block.statements(), Some(tail_expr)), } } -fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr { +fn with_tail_expr( + make: &SyntaxFactory, + block: ast::BlockExpr, + tail_expr: ast::Expr, +) -> ast::BlockExpr { let stmt_tail_opt: Option<ast::Stmt> = - block.tail_expr().map(|expr| make::expr_stmt(expr).into()); + block.tail_expr().map(|expr| make.expr_stmt(expr).into()); let mut elements: Vec<SyntaxElement> = vec![]; @@ -2012,10 +2042,10 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr elements.push(syntax::NodeOrToken::Node(stmt_tail.syntax().clone())); } - make::hacky_block_expr(elements, Some(tail_expr)) + make.hacky_block_expr(elements, Some(tail_expr)) } -fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> String { +fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_, '_>, module: hir::Module) -> String { ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_owned()) } @@ -2024,42 +2054,51 @@ fn is_inherit_attr(attr: &ast::Attr) -> bool { matches!(name.as_str(), "track_caller" | "cfg") } -fn make_ty(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type { +fn make_ty( + make: &SyntaxFactory, + ty: &hir::Type<'_>, + ctx: &AssistContext<'_, '_>, + module: hir::Module, +) -> ast::Type { let ty_str = format_type(ty, ctx, module); - make::ty(&ty_str) + make.ty(&ty_str) } fn rewrite_body_segment( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, to_this_param: Option<ast::SelfParam>, params: &[Param<'_>], handler: &FlowHandler<'_>, syntax: &SyntaxNode, ) -> SyntaxNode { let to_this_param = to_this_param.and_then(|it| ctx.sema.to_def(&it)); - let syntax = fix_param_usages(ctx, to_this_param, params, syntax); - update_external_control_flow(handler, &syntax); - syntax + let (param_editor, param_root) = SyntaxEditor::new(syntax.clone()); + fix_param_usages(¶m_editor, syntax, ¶m_root, ctx, to_this_param, params); + let syntax = param_editor.finish().new_root().clone(); + + let (flow_editor, flow_root) = SyntaxEditor::new(syntax); + update_external_control_flow(&flow_editor, &flow_root, handler); + flow_editor.finish().new_root().clone() } /// change all usages to account for added `&`/`&mut` for some params fn fix_param_usages( - ctx: &AssistContext<'_>, + editor: &SyntaxEditor, + source_syntax: &SyntaxNode, + syntax: &SyntaxNode, + ctx: &AssistContext<'_, '_>, to_this_param: Option<Local>, params: &[Param<'_>], - syntax: &SyntaxNode, -) -> SyntaxNode { +) { let mut usages_for_param: Vec<(&Param<'_>, Vec<ast::Expr>)> = Vec::new(); let mut usages_for_self_param: Vec<ast::Expr> = Vec::new(); + let source_range = source_syntax.text_range(); + let source_start = source_range.start(); - let tm = TreeMutator::new(syntax); let reference_filter = |reference: &FileReference| { - syntax - .text_range() - .contains_range(reference.range) - .then_some(()) - .and_then(|_| path_element_of_reference(syntax, reference)) - .map(|expr| tm.make_mut(&expr)) + source_range.contains_range(reference.range).then_some(())?; + let local_range = reference.range - source_start; + path_element_of_reference(syntax, local_range) }; if let Some(self_param) = to_this_param { @@ -2079,11 +2118,11 @@ fn fix_param_usages( usages_for_param.push((param, usages.unique().collect())); } - let res = tm.make_syntax_mut(syntax); + let make = editor.make(); for self_usage in usages_for_self_param { - let this_expr = make::expr_path(make::ext::ident_path("this")).clone_for_update(); - ted::replace(self_usage.syntax(), this_expr.syntax()); + let this_expr = make.expr_path(make.ident_path("this")); + editor.replace(self_usage.syntax(), this_expr.syntax()); } for (param, usages) in usages_for_param { for usage in usages { @@ -2098,7 +2137,7 @@ fn fix_param_usages( Some(ast::Expr::RefExpr(node)) if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => { - ted::replace( + editor.replace( node.syntax(), node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); @@ -2106,23 +2145,25 @@ fn fix_param_usages( Some(ast::Expr::RefExpr(node)) if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => { - ted::replace( + editor.replace( node.syntax(), node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); } Some(_) | None => { - let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); - ted::replace(usage.syntax(), p.syntax()) + let p = make.expr_prefix(T![*], usage.clone()); + editor.replace(usage.syntax(), p.syntax()) } } } } - - res } -fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode) { +fn update_external_control_flow( + editor: &SyntaxEditor, + syntax: &SyntaxNode, + handler: &FlowHandler<'_>, +) { let mut nested_loop = None; let mut nested_scope = None; for event in syntax.preorder() { @@ -2151,19 +2192,25 @@ fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode) match expr { ast::Expr::ReturnExpr(return_expr) => { let expr = return_expr.expr(); - if let Some(replacement) = make_rewritten_flow(handler, expr) { - ted::replace(return_expr.syntax(), replacement.syntax()) + if let Some(replacement) = + make_rewritten_flow(handler, expr, editor.make()) + { + editor.replace(return_expr.syntax(), replacement.syntax()) } } ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => { let expr = break_expr.expr(); - if let Some(replacement) = make_rewritten_flow(handler, expr) { - ted::replace(break_expr.syntax(), replacement.syntax()) + if let Some(replacement) = + make_rewritten_flow(handler, expr, editor.make()) + { + editor.replace(break_expr.syntax(), replacement.syntax()) } } ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => { - if let Some(replacement) = make_rewritten_flow(handler, None) { - ted::replace(continue_expr.syntax(), replacement.syntax()) + if let Some(replacement) = + make_rewritten_flow(handler, None, editor.make()) + { + editor.replace(continue_expr.syntax(), replacement.syntax()) } } _ => { @@ -2186,27 +2233,29 @@ fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode) fn make_rewritten_flow( handler: &FlowHandler<'_>, arg_expr: Option<ast::Expr>, + make: &SyntaxFactory, ) -> Option<ast::Expr> { let value = match handler { FlowHandler::None | FlowHandler::Try { .. } => return None, - FlowHandler::If { .. } => make::expr_call( - make::expr_path(make::path_from_text("ControlFlow::Break")), - make::arg_list([make::ext::expr_unit()]), - ) - .into(), + FlowHandler::If { .. } => make + .expr_call( + make.expr_path(make.path_from_text("ControlFlow::Break")), + make.arg_list([make.expr_unit()]), + ) + .into(), FlowHandler::IfOption { .. } => { - let expr = arg_expr.unwrap_or_else(make::ext::expr_unit); - let args = make::arg_list([expr]); - make::expr_call(make::expr_path(make::ext::ident_path("Some")), args).into() + let expr = arg_expr.unwrap_or_else(|| make.expr_unit()); + let args = make.arg_list([expr]); + make.expr_call(make.expr_path(make.ident_path("Some")), args).into() } - FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")), + FlowHandler::MatchOption { .. } => make.expr_path(make.ident_path("None")), FlowHandler::MatchResult { .. } => { - let expr = arg_expr.unwrap_or_else(make::ext::expr_unit); - let args = make::arg_list([expr]); - make::expr_call(make::expr_path(make::ext::ident_path("Err")), args).into() + let expr = arg_expr.unwrap_or_else(|| make.expr_unit()); + let args = make.arg_list([expr]); + make.expr_call(make.expr_path(make.ident_path("Err")), args).into() } }; - Some(make::expr_return(Some(value)).clone_for_update()) + Some(make.expr_return(Some(value)).into()) } #[cfg(test)] @@ -2450,6 +2499,7 @@ fn $0fun_name() { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { $0for v in &[0, 1] { }$0 } @@ -4697,7 +4747,7 @@ async fn some_function() { check_assist( extract_function, r#" -//- minicore: future, result +//- minicore: future, result, try async fn foo() -> Result<(), ()> { $0async {}.await; Err(())?$0 diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs index dcbeaefa21..9e06a17337 100644 --- a/crates/ide-assists/src/handlers/extract_module.rs +++ b/crates/ide-assists/src/handlers/extract_module.rs @@ -52,7 +52,7 @@ use super::remove_unused_param::range_to_remove; // name + 2 // } // ``` -pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { if ctx.has_empty_selection() { return None; } @@ -267,7 +267,7 @@ fn extract_child_target( impl Module { fn get_usages_and_record_fields( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, replace_range: TextRange, ) -> (FxHashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>, FxHashMap<TextSize, ast::Use>) { @@ -356,7 +356,7 @@ impl Module { fn expand_and_group_usages_file_wise( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, replace_range: TextRange, node_def: Definition, refs_in_files: &mut FxHashMap<FileId, Vec<(TextRange, String)>>, @@ -449,7 +449,7 @@ impl Module { fn resolve_imports( &mut self, module: Option<ast::Module>, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Vec<TextRange> { let mut imports_to_remove = vec![]; let mut node_set = FxHashSet::default(); @@ -491,7 +491,7 @@ impl Module { def: Definition, use_node: &SyntaxNode, curr_parent_module: &Option<ast::Module>, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<TextRange> { //We only need to find in the current file let selection_range = ctx.selection_trimmed(); @@ -689,7 +689,7 @@ fn check_intersection_and_push( fn check_def_in_mod_and_out_sel( def: Definition, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, curr_parent_module: &Option<ast::Module>, selection_range: TextRange, curr_file_id: FileId, diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 21013e2e61..50ce8d9b4d 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -5,7 +5,7 @@ use hir::{EnumVariant, HasCrate, Module, ModuleDef, Name}; use ide_db::{ FxHashSet, RootDatabase, defs::Definition, - helpers::mod_path_to_ast, + helpers::mod_path_to_ast_with_factory, imports::insert_use::{ImportScope, InsertUseConfig, insert_use_with_editor}, path_transform::PathTransform, search::FileReference, @@ -40,7 +40,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn extract_struct_from_enum_variant( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let variant = ctx.find_node_at_offset::<ast::Variant>()?; let field_list = extract_field_list_if_applicable(&variant)?; @@ -338,7 +338,7 @@ fn update_variant( let name = variant.name()?; let generic_args = generics .filter(|generics| generics.generic_params().count() > 0) - .map(|generics| generics.to_generic_args()); + .map(|generics| generics.to_generic_args(make)); // FIXME: replace with a `ast::make` constructor let ty = match generic_args { Some(generic_args) => make.ty(&format!("{name}{generic_args}")), @@ -401,7 +401,12 @@ fn apply_references( ) { let make = editor.make(); if let Some((scope, path)) = import { - insert_use_with_editor(&scope, mod_path_to_ast(&path, edition), &insert_use_cfg, editor); + insert_use_with_editor( + &scope, + mod_path_to_ast_with_factory(make, &path, edition), + &insert_use_cfg, + editor, + ); } // deep clone to prevent cycle let path = make.path_from_segments(iter::once(segment.clone()), false); @@ -411,7 +416,7 @@ fn apply_references( } fn process_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, visited_modules: &mut FxHashSet<Module>, enum_module_def: &ModuleDef, variant_hir_name: &Name, diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs index eda35eba45..ecb031e42d 100644 --- a/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // field: Type, // } // ``` -pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { if ctx.has_empty_selection() { return None; } @@ -75,7 +75,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> generics.map(|it| make.generic_param_list(it.into_iter().cloned())); // Replace original type with the alias - let ty_args = generic_params.as_ref().map(|it| it.to_generic_args().generic_args()); + let ty_args = generic_params.as_ref().map(|it| it.to_generic_args(make).generic_args()); let new_ty = if let Some(ty_args) = ty_args { make.generic_ty_path_segment(make.name_ref(name), ty_args) } else { diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index c5c57c76b4..d4a0490f16 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/crates/ide-assists/src/handlers/extract_variable.rs @@ -65,7 +65,7 @@ use crate::{AssistContext, AssistId, Assists, utils::is_body_const}; // VAR_NAME * 4; // } // ``` -pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let node = if ctx.has_empty_selection() { if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) { t.parent().and_then(ast::ExprStmt::cast)?.syntax().clone() @@ -332,7 +332,7 @@ fn peel_parens(mut expr: ast::Expr) -> ast::Expr { /// Check whether the node is a valid expression which can be extracted to a variable. /// In general that's true for any expression, but in some cases that would produce invalid code. -fn valid_target_expr(ctx: &AssistContext<'_>) -> impl Fn(SyntaxNode) -> Option<ast::Expr> { +fn valid_target_expr(ctx: &AssistContext<'_, '_>) -> impl Fn(SyntaxNode) -> Option<ast::Expr> { let selection = ctx.selection_trimmed(); move |node| match node.kind() { SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None, @@ -383,7 +383,7 @@ impl ExtractionKind { fn get_name_and_expr( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, to_extract: &ast::Expr, ) -> (String, SyntaxNode) { // We only do this sort of extraction for fields because they should have lowercase names @@ -416,7 +416,7 @@ impl ExtractionKind { } } -fn get_literal_name(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option<String> { +fn get_literal_name(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option<String> { let ast::Expr::Literal(literal) = expr else { return None; }; @@ -512,7 +512,7 @@ impl Anchor { } } -fn like_const_value(ctx: &AssistContext<'_>, path_resolution: hir::PathResolution) -> bool { +fn like_const_value(ctx: &AssistContext<'_, '_>, path_resolution: hir::PathResolution) -> bool { let db = ctx.db(); let adt_like_const_value = |adt: Option<hir::Adt>| matches!(adt, Some(hir::Adt::Struct(s)) if s.kind(db) == hir::StructKind::Unit); match path_resolution { diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs index d8714dd49c..d0f5c7c500 100644 --- a/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/crates/ide-assists/src/handlers/fix_visibility.rs @@ -30,11 +30,11 @@ use crate::{AssistContext, AssistId, Assists}; // m::frobnicate(); // } // ``` -pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { add_vis_to_referenced_module_def(acc, ctx) } -fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let path: ast::Path = ctx.find_node_at_offset()?; let qualifier = path.qualifier()?; let name_ref = path.segment()?.name_ref()?; diff --git a/crates/ide-assists/src/handlers/flip_binexpr.rs b/crates/ide-assists/src/handlers/flip_binexpr.rs index 17911150f5..d47f5c83cd 100644 --- a/crates/ide-assists/src/handlers/flip_binexpr.rs +++ b/crates/ide-assists/src/handlers/flip_binexpr.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists}; // let _ = 2 + 90; // } // ``` -pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let expr = ctx.find_node_at_offset::<BinExpr>()?; let lhs = expr.lhs()?; let rhs = expr.rhs()?; @@ -114,7 +114,7 @@ impl From<ast::BinaryOp> for FlipAction { // let _ = ..90; // } // ``` -pub(crate) fn flip_range_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn flip_range_expr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let range_expr = ctx.find_node_at_offset::<ast::RangeExpr>()?; let op = range_expr.op_token()?; let start = range_expr.start(); diff --git a/crates/ide-assists/src/handlers/flip_comma.rs b/crates/ide-assists/src/handlers/flip_comma.rs index 65dc36cdca..00d659adc1 100644 --- a/crates/ide-assists/src/handlers/flip_comma.rs +++ b/crates/ide-assists/src/handlers/flip_comma.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists}; // ((3, 4), (1, 2)); // } // ``` -pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let comma = ctx.find_token_syntax_at_offset(T![,])?; let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?; let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?; diff --git a/crates/ide-assists/src/handlers/flip_or_pattern.rs b/crates/ide-assists/src/handlers/flip_or_pattern.rs index bd56331f41..c60c6a2a98 100644 --- a/crates/ide-assists/src/handlers/flip_or_pattern.rs +++ b/crates/ide-assists/src/handlers/flip_or_pattern.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists}; // let (b | a) = 1; // } // ``` -pub(crate) fn flip_or_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn flip_or_pattern(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { // Only flip on the `|` token let pipe = ctx.find_token_syntax_at_offset(T![|])?; diff --git a/crates/ide-assists/src/handlers/flip_trait_bound.rs b/crates/ide-assists/src/handlers/flip_trait_bound.rs index dfd280efa6..77d5c042c9 100644 --- a/crates/ide-assists/src/handlers/flip_trait_bound.rs +++ b/crates/ide-assists/src/handlers/flip_trait_bound.rs @@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // fn foo<T: Copy + Clone>() { } // ``` -pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { // Only flip on the `+` token let plus = ctx.find_token_syntax_at_offset(T![+])?; diff --git a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 0bb90f187c..4454e47013 100644 --- a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs @@ -58,7 +58,7 @@ use syntax::{ // ``` pub(crate) fn generate_blanket_trait_impl( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let name = ctx.find_node_at_offset::<ast::Name>()?; let traitd = ast::Trait::cast(name.syntax().parent()?)?; @@ -89,7 +89,24 @@ pub(crate) fn generate_blanket_trait_impl( ))]); let trait_gen_args = - traitd.generic_param_list().map(|param_list| param_list.to_generic_args()); + traitd.generic_param_list().map(|param_list| param_list.to_generic_args(make)); + + let body = traitd.assoc_item_list().and_then(|trait_assoc_list| { + let items = trait_assoc_list + .assoc_items() + .filter_map(|item| { + let item = match item { + ast::AssocItem::Fn(method) if method.body().is_none() => { + todo_fn(make, &method, ctx.config).into() + } + ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item, + _ => return None, + }; + Some(item.reset_indent().indent(1.into())) + }) + .collect::<Vec<_>>(); + (!items.is_empty()).then(|| make.assoc_item_list(items)) + }); let impl_ = make.impl_trait( cfg_attrs(&traitd), @@ -103,23 +120,9 @@ pub(crate) fn generate_blanket_trait_impl( thisty.into(), trait_where_clause, None, - None, + body, ); - if let Some(trait_assoc_list) = traitd.assoc_item_list() { - let assoc_item_list = impl_.get_or_create_assoc_item_list_with_editor(&editor); - for item in trait_assoc_list.assoc_items() { - let item = match item { - ast::AssocItem::Fn(method) if method.body().is_none() => { - todo_fn(make, &method, ctx.config).into() - } - ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item, - _ => continue, - }; - assoc_item_list.add_item(item.reset_indent().indent(1.into())); - } - } - let impl_ = impl_.indent(indent); editor.insert_all( diff --git a/crates/ide-assists/src/handlers/generate_constant.rs b/crates/ide-assists/src/handlers/generate_constant.rs index fce0ce3994..6c5042b14f 100644 --- a/crates/ide-assists/src/handlers/generate_constant.rs +++ b/crates/ide-assists/src/handlers/generate_constant.rs @@ -31,7 +31,7 @@ use syntax::{ // } // ``` -pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let constant_token = ctx.find_node_at_offset::<ast::NameRef>()?; if constant_token.to_string().chars().any(|it| !(it.is_uppercase() || it == '_')) { cov_mark::hit!(not_constant_name); @@ -113,7 +113,7 @@ fn get_text_for_generate_constant( } fn target_data_for_generate_constant( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, current_module: Module, constant_module: Module, ) -> Option<(TextSize, IndentLevel, Option<FileId>, String)> { diff --git a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs index b4a17c376a..4470791f4d 100644 --- a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs @@ -30,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn generate_default_from_enum_variant( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let variant = ctx.find_node_at_offset::<ast::Variant>()?; let variant_name = variant.name()?; diff --git a/crates/ide-assists/src/handlers/generate_default_from_new.rs b/crates/ide-assists/src/handlers/generate_default_from_new.rs index 739b631736..34ab3c4304 100644 --- a/crates/ide-assists/src/handlers/generate_default_from_new.rs +++ b/crates/ide-assists/src/handlers/generate_default_from_new.rs @@ -44,7 +44,10 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_default_from_new( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let fn_node = ctx.find_node_at_offset::<ast::Fn>()?; let fn_name = fn_node.name()?; @@ -154,7 +157,7 @@ fn generate_default_impl(make: &SyntaxFactory, impl_: &ast::Impl, self_ty: ast:: ) } -fn is_default_implemented(ctx: &AssistContext<'_>, impl_: &Impl) -> bool { +fn is_default_implemented(ctx: &AssistContext<'_, '_>, impl_: &Impl) -> bool { let db = ctx.sema.db; let impl_ = ctx.sema.to_def(impl_); let impl_def = match impl_ { diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 9486aa6f01..6c9808fb1c 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -48,7 +48,10 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_delegate_methods( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { if !ctx.config.code_action_grouping { return None; } @@ -192,7 +195,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' None => { let name = &strukt_name.to_string(); let ty_params = strukt.generic_param_list(); - let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); + let ty_args = ty_params.as_ref().map(|it| it.to_generic_args(make)); let where_clause = strukt.where_clause(); let assoc_item_list = make.assoc_item_list(vec![item]); @@ -210,7 +213,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let impl_def = impl_def.indent(indent); // Insert the impl block. - let strukt = edit.make_mut(strukt.clone()); editor.insert_all( Position::after(strukt.syntax()), vec![ diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 6639f10c1f..e21f1ab359 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -86,7 +86,10 @@ use syntax::{ // } // } // ``` -pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_delegate_trait( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { if !ctx.config.code_action_grouping { return None; } @@ -118,7 +121,7 @@ struct Field { impl Field { pub(crate) fn new( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, f: Either<ast::RecordField, (ast::TupleField, ast::TupleFieldList)>, ) -> Option<Field> { let db = ctx.sema.db; @@ -202,7 +205,7 @@ impl Struct { Some(Struct { name, strukt: s }) } - pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) { + pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_, '_>) { let db = ctx.db(); for (index, delegee) in field.impls.iter().enumerate() { @@ -249,7 +252,7 @@ impl Struct { } fn generate_impl( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, strukt: &Struct, field_ty: &ast::Type, field_name: &str, @@ -271,9 +274,9 @@ fn generate_impl( None, delegee.is_unsafe(db), bound_params.clone(), - bound_params.map(|params| params.to_generic_args()), + bound_params.map(|params| params.to_generic_args(&make)), strukt_params.clone(), - strukt_params.map(|params| params.to_generic_args()), + strukt_params.map(|params| params.to_generic_args(&make)), delegee.is_auto(db), make.ty(&delegee.name(db).display_no_db(edition).to_smolstr()), strukt_ty, @@ -315,7 +318,7 @@ fn generate_impl( let strukt_params = resolve_name_conflicts(strukt_params, &old_impl_params); let (field_ty, ty_where_clause) = match &strukt_params { Some(strukt_params) => { - let args = strukt_params.to_generic_args(); + let args = strukt_params.to_generic_args(&make); let field_ty = rename_strukt_args(ctx, ast_strukt, field_ty, &args)?; let where_clause = ast_strukt .where_clause() @@ -346,6 +349,7 @@ fn generate_impl( // 2.2) Generate generic args applied on impl. let (transform_args, trait_gen_params) = generate_args_for_impl( + &make, old_impl_params, &old_impl.self_ty()?, &field_ty, @@ -372,7 +376,7 @@ fn generate_impl( } }); - let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args()); + let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args(&make)); let path_type = make.ty(&trait_.name(db).display_no_db(edition).to_smolstr()); let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?; // 3) Generate delegate trait impl @@ -412,7 +416,7 @@ fn generate_impl( } fn transform_impl<N: ast::AstNode>( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, strukt: &ast::Struct, old_impl: &ast::Impl, args: &Option<GenericArgList>, @@ -590,13 +594,15 @@ fn finalize_delegate( // While the last two generic args `B` and `C` doesn't change, it remains // `<B, C>`. So we apply `<T, B, C>` as generic arguments to impl. fn generate_args_for_impl( + make: &SyntaxFactory, old_impl_gpl: Option<GenericParamList>, self_ty: &ast::Type, field_ty: &ast::Type, trait_params: Option<GenericParamList>, old_trait_args: &FxHashSet<String>, ) -> (Option<ast::GenericArgList>, Option<GenericParamList>) { - let Some(old_impl_args) = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args()) else { + let Some(old_impl_args) = old_impl_gpl.map(|gpl| gpl.to_generic_args(make).generic_args()) + else { return (None, trait_params); }; @@ -638,7 +644,7 @@ fn generate_args_for_impl( } fn rename_strukt_args<N>( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, strukt: &ast::Struct, item: &N, args: &GenericArgList, @@ -654,7 +660,7 @@ where N::cast(transform.apply(item.syntax())) } -fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> bool { +fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_, '_>) -> bool { ctx.sema .source(trait_) .and_then(|src| { diff --git a/crates/ide-assists/src/handlers/generate_deref.rs b/crates/ide-assists/src/handlers/generate_deref.rs index a5bdf80ac7..9d1b257af4 100644 --- a/crates/ide-assists/src/handlers/generate_deref.rs +++ b/crates/ide-assists/src/handlers/generate_deref.rs @@ -38,11 +38,11 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx)) } -fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let strukt = ctx.find_node_at_offset::<ast::Struct>()?; let field = ctx.find_node_at_offset::<ast::RecordField>()?; @@ -84,7 +84,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( ) } -fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let strukt = ctx.find_node_at_offset::<ast::Struct>()?; let field = ctx.find_node_at_offset::<ast::TupleField>()?; let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?; diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs index 0129b1db39..f293e956bc 100644 --- a/crates/ide-assists/src/handlers/generate_derive.rs +++ b/crates/ide-assists/src/handlers/generate_derive.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // y: u32, // } // ``` -pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let cap = ctx.config.snippet_cap?; let nominal = ctx.find_node_at_offset::<ast::Adt>()?; let target = nominal.syntax().text_range(); diff --git a/crates/ide-assists/src/handlers/generate_documentation_template.rs b/crates/ide-assists/src/handlers/generate_documentation_template.rs index 77232dfebd..89adda9386 100644 --- a/crates/ide-assists/src/handlers/generate_documentation_template.rs +++ b/crates/ide-assists/src/handlers/generate_documentation_template.rs @@ -43,7 +43,7 @@ use crate::assist_context::{AssistContext, Assists}; // ``` pub(crate) fn generate_documentation_template( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let name = ctx.find_node_at_offset::<ast::Name>()?; let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?; @@ -95,7 +95,7 @@ pub(crate) fn generate_documentation_template( // /// ``` // pub fn add(a: i32, b: i32) -> i32 { a + b } // ``` -pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let tok: ast::Comment = ctx.find_token_at_offset()?; let node = tok.syntax().parent()?; let last_doc_token = @@ -127,7 +127,7 @@ pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_>) - ) } -fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> { +fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option<String> { if !is_public(ast_func, ctx)? { // Doctests for private items can't actually name the item, so they're pretty useless. return None; @@ -182,7 +182,7 @@ fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<St Some(example) } -fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> { +fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option<String> { let hir_func = ctx.sema.to_def(ast_func)?; let container = hir_func.as_assoc_item(ctx.db())?.container(ctx.db()); if let hir::AssocItemContainer::Impl(imp) = container { @@ -281,7 +281,7 @@ fn safety_builder(ast_func: &ast::Fn) -> Option<Vec<String>> { } /// Checks if the function is public / exported -fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<bool> { +fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option<bool> { let hir_func = ctx.sema.to_def(ast_func)?; Some( hir_func.visibility(ctx.db()) == Visibility::Public @@ -290,7 +290,7 @@ fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<bool> { } /// Checks that all parent modules of the function are public / exported -fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext<'_>) -> bool { +fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext<'_, '_>) -> bool { let mut module = hir_func.module(ctx.db()); loop { if let Some(parent) = module.parent(ctx.db()) { @@ -305,7 +305,7 @@ fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext<'_>) -> } /// Returns the name of the current crate -fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> { +fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option<String> { let krate = ctx.sema.scope(ast_func.syntax())?.krate(); Some(krate.display_name(ctx.db())?.to_string()) } @@ -378,7 +378,7 @@ fn self_partial_type(ast_func: &ast::Fn) -> Option<String> { } /// Helper function to determine if the function is in a trait implementation -fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool { +fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> bool { ctx.sema .to_def(ast_func) .and_then(|hir_func| hir_func.as_assoc_item(ctx.db())) @@ -387,7 +387,7 @@ fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool { } /// Helper function to determine if the function definition is in a trait definition -fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool { +fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> bool { ctx.sema .to_def(ast_func) .and_then(|hir_func| hir_func.as_assoc_item(ctx.db())) @@ -490,7 +490,7 @@ fn string_vec_from(string_array: &[&str]) -> Vec<String> { } /// Helper function to build the path of the module in the which is the node -fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_>, edition: Edition) -> Option<String> { +fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>, edition: Edition) -> Option<String> { let crate_name = crate_name(ast_func, ctx)?; let leaf = self_partial_type(ast_func) .or_else(|| ast_func.name().map(|n| n.to_string())) @@ -508,7 +508,7 @@ fn return_type(ast_func: &ast::Fn) -> Option<ast::Type> { } /// Helper function to determine if the function returns some data -fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool { +fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> bool { ctx.sema .to_def(ast_func) .map(|hir_func| hir_func.ret_type(ctx.db())) diff --git a/crates/ide-assists/src/handlers/generate_enum_is_method.rs b/crates/ide-assists/src/handlers/generate_enum_is_method.rs index e2783811f7..867eaf4c29 100644 --- a/crates/ide-assists/src/handlers/generate_enum_is_method.rs +++ b/crates/ide-assists/src/handlers/generate_enum_is_method.rs @@ -40,7 +40,10 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_enum_is_method( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let variant = ctx.find_node_at_offset::<ast::Variant>()?; let parent_enum = ast::Adt::Enum(variant.parent_enum()); let variants = variant diff --git a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs index 9a97ad1e8f..4cdc801ec1 100644 --- a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs +++ b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs @@ -40,7 +40,7 @@ use crate::{ // ``` pub(crate) fn generate_enum_try_into_method( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { generate_enum_projection_method( acc, @@ -85,7 +85,10 @@ pub(crate) fn generate_enum_try_into_method( // } // } // ``` -pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_enum_as_method( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { generate_enum_projection_method( acc, ctx, @@ -113,7 +116,7 @@ struct ProjectionProps { fn generate_enum_projection_method( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, assist_id: &'static str, assist_description: &str, props: ProjectionProps, diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs index 9b4d44d8b5..fb43e3eaa3 100644 --- a/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -32,7 +32,7 @@ use crate::assist_context::{AssistContext, Assists}; // let country = Countries::Lesotho; // } // ``` -pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let path: ast::Path = ctx.find_node_at_offset()?; let parent = PathParent::new(&path)?; @@ -104,7 +104,7 @@ impl PathParent { fn make_field_list( &self, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, ) -> Option<ast::FieldList> { let scope = ctx.sema.scope(self.syntax())?; @@ -156,7 +156,7 @@ fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option<String> { } fn expr_ty( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, arg: ast::Expr, scope: &hir::SemanticsScope<'_>, diff --git a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index 55e5083811..a9f5ab6976 100644 --- a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -36,7 +36,7 @@ use crate::{AssistContext, Assists}; // unsafe fn foo(n: i32) -> i32 { 42i32 } // ``` -pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let name = ctx.find_node_at_offset::<ast::Name>()?; let func = &name.syntax().parent()?; let func_node = ast::Fn::cast(func.clone())?; diff --git a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index 76246c3e8e..52df6182ac 100644 --- a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -27,7 +27,7 @@ use crate::{ // ``` pub(crate) fn generate_from_impl_for_enum( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let variant = ctx.find_node_at_offset::<ast::Variant>()?; let adt = ast::Adt::Enum(variant.parent_enum()); @@ -107,7 +107,10 @@ struct VariantInfo { ty: ast::Type, } -fn selected_variants(ctx: &AssistContext<'_>, variant: &ast::Variant) -> Option<Vec<VariantInfo>> { +fn selected_variants( + ctx: &AssistContext<'_, '_>, + variant: &ast::Variant, +) -> Option<Vec<VariantInfo>> { variant .parent_enum() .variant_list()? diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 6ef492619b..14dd4061e7 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -16,9 +16,11 @@ use syntax::{ Edition, SyntaxKind, SyntaxNode, T, TextRange, ast::{ self, AstNode, BlockExpr, CallExpr, HasArgList, HasGenericParams, HasModuleItem, - HasTypeBounds, edit::IndentLevel, edit_in_place::Indent, make, + HasTypeBounds, + edit::{AstNodeEdit, IndentLevel}, + syntax_factory::SyntaxFactory, }, - ted, + syntax_editor::{Position, SyntaxEditor}, }; use crate::{ @@ -51,11 +53,11 @@ use crate::{ // } // // ``` -pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { gen_fn(acc, ctx).or_else(|| gen_method(acc, ctx)) } -fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; let path = path_expr.path()?; @@ -74,9 +76,10 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { { return None; } + let make = SyntaxFactory::without_mappings(); let function_builder = - FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target, &adt_info)?; + FunctionBuilder::from_call(&make, ctx, &call, fn_name, target_module, target, &adt_info)?; let text_range = call.syntax().text_range(); let label = format!("Generate {} function", function_builder.fn_name); add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_info, label) @@ -101,7 +104,7 @@ impl TargetInfo { } fn fn_target_info( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, path: ast::Path, call: &CallExpr, fn_name: &str, @@ -131,7 +134,7 @@ fn fn_target_info( } } -fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; if ctx.sema.resolve_method_call(&call).is_some() { return None; @@ -148,7 +151,9 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let enclosing_impl = ctx.find_node_at_offset::<ast::Impl>(); let cursor_impl = enclosing_impl.filter(|impl_| { - ctx.sema.to_def(impl_).map_or(false, |def| def.self_ty(ctx.sema.db).as_adt() == Some(adt)) + ctx.sema.to_def(impl_).is_some_and(|def| { + def.self_ty(ctx.sema.db).as_adt() == Some(adt) && def.trait_(ctx.sema.db).is_none() + }) }); let (impl_, file) = if let Some(impl_) = cursor_impl { @@ -158,7 +163,10 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { }; let target = get_method_target(ctx, &impl_, &adt)?; + let make = SyntaxFactory::without_mappings(); + let function_builder = FunctionBuilder::from_method_call( + &make, ctx, &call, &fn_name, @@ -174,7 +182,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { fn add_func_to_accumulator( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, text_range: TextRange, function_builder: FunctionBuilder, file: FileId, @@ -182,34 +190,21 @@ fn add_func_to_accumulator( label: String, ) -> Option<()> { acc.add(AssistId::generate("generate_function"), label, text_range, |edit| { - edit.edit_file(file); - let target = function_builder.target.clone(); - let edition = function_builder.target_edition; - let func = function_builder.render(ctx.config.snippet_cap, edit); + let snippet_cap = ctx.config.snippet_cap; if let Some(adt) = adt_info .and_then(|adt_info| if adt_info.impl_exists { None } else { Some(adt_info.adt) }) { - let name = make::ty_path(make::ext::ident_path(&format!( - "{}", - adt.name(ctx.db()).display(ctx.db(), edition) - ))); - - // FIXME: adt may have generic params. - let impl_ = make::impl_(None, None, None, name, None, None).clone_for_update(); - - func.indent(IndentLevel(1)); - impl_.get_or_create_assoc_item_list().add_item(func.into()); - target.insert_impl_at(edit, impl_); + target.insert_impl_at(edit, file, ctx, &function_builder, adt, snippet_cap); } else { - target.insert_fn_at(edit, func); + target.insert_fn_at(edit, file, &function_builder, snippet_cap); } }) } fn get_adt_source( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, adt: &hir::Adt, fn_name: &str, ) -> Option<(Option<ast::Impl>, FileId)> { @@ -240,7 +235,8 @@ impl FunctionBuilder { /// Prepares a generated function that matches `call`. /// The function is generated in `target_module` or next to `call` fn from_call( - ctx: &AssistContext<'_>, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, call: &ast::CallExpr, fn_name: &str, target_module: Option<Module>, @@ -253,9 +249,10 @@ impl FunctionBuilder { let current_module = ctx.sema.scope(call.syntax())?.module(); let visibility = calculate_necessary_visibility(current_module, target_module, ctx); - let fn_name = make::name(fn_name); + let fn_name = make.name(fn_name); let mut necessary_generic_params = FxHashSet::default(); let params = fn_args( + make, ctx, target_module, ast::CallableExpr::Call(call.clone()), @@ -272,25 +269,26 @@ impl FunctionBuilder { // If generated function has the name "new" and is an associated function, we generate fn body // as a constructor and assume a "Self" return type. if let Some(body) = - make_fn_body_as_new_function(ctx, &fn_name.text(), adt_info, target_edition) + make_fn_body_as_new_function(make, ctx, &fn_name.text(), adt_info, target_edition) { - ret_type = Some(make::ret_type(make::ty_path(make::ext::ident_path("Self")))); + ret_type = Some(make.ret_type(make.ty_path(make.ident_path("Self")).into())); should_focus_return_type = false; fn_body = body; } else { let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into()); (ret_type, should_focus_return_type) = make_return_type( + make, ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params, ); let placeholder_expr = expr_fill_default(ctx.config); - fn_body = make::block_expr(vec![], Some(placeholder_expr)); + fn_body = make.block_expr(vec![], Some(placeholder_expr)); }; let (generic_param_list, where_clause) = - fn_generic_params(ctx, necessary_generic_params, &target)?; + fn_generic_params(make, ctx, necessary_generic_params, &target)?; Some(Self { target, @@ -308,7 +306,8 @@ impl FunctionBuilder { } fn from_method_call( - ctx: &AssistContext<'_>, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, call: &ast::MethodCallExpr, name: &ast::NameRef, receiver_ty: Type<'_>, @@ -320,10 +319,11 @@ impl FunctionBuilder { let current_module = ctx.sema.scope(call.syntax())?.module(); let visibility = calculate_necessary_visibility(current_module, target_module, ctx); - let fn_name = make::name(name.ident_token()?.text()); + let fn_name = make.name(name.ident_token()?.text()); let mut necessary_generic_params = FxHashSet::default(); necessary_generic_params.extend(receiver_ty.generic_params(ctx.db())); let params = fn_args( + make, ctx, target_module, ast::CallableExpr::MethodCall(call.clone()), @@ -334,14 +334,19 @@ impl FunctionBuilder { let is_async = await_expr.is_some(); let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into()); - let (ret_type, should_focus_return_type) = - make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params); + let (ret_type, should_focus_return_type) = make_return_type( + make, + ctx, + &expr_for_ret_ty, + target_module, + &mut necessary_generic_params, + ); let (generic_param_list, where_clause) = - fn_generic_params(ctx, necessary_generic_params, &target)?; + fn_generic_params(make, ctx, necessary_generic_params, &target)?; let placeholder_expr = expr_fill_default(ctx.config); - let fn_body = make::block_expr(vec![], Some(placeholder_expr)); + let fn_body = make.block_expr(vec![], Some(placeholder_expr)); Some(Self { target, @@ -358,55 +363,28 @@ impl FunctionBuilder { }) } - fn render(self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn { + fn render(&self, make: &SyntaxFactory) -> ast::Fn { let visibility = match self.visibility { Visibility::None => None, - Visibility::Crate => Some(make::visibility_pub_crate()), - Visibility::Pub => Some(make::visibility_pub()), + Visibility::Crate => Some(make.visibility_pub_crate()), + Visibility::Pub => Some(make.visibility_pub()), }; let type_params = - self.generic_param_list.filter(|list| list.generic_params().next().is_some()); - let fn_def = make::fn_( + self.generic_param_list.clone().filter(|list| list.generic_params().next().is_some()); + make.fn_( None, visibility, - self.fn_name, + self.fn_name.clone(), type_params, - self.where_clause, - self.params, - self.fn_body, - self.ret_type, + self.where_clause.clone(), + self.params.clone(), + self.fn_body.clone(), + self.ret_type.clone(), self.is_async, false, // FIXME : const and unsafe are not handled yet. false, false, ) - .clone_for_update(); - - let ret_type = fn_def.ret_type(); - // PANIC: we guarantee we always create a function body with a tail expr - let tail_expr = fn_def - .body() - .expect("generated function should have a body") - .tail_expr() - .expect("function body should have a tail expression"); - - if let Some(cap) = cap { - 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); - } - None => { - edit.add_placeholder_snippet(cap, tail_expr); - } - } - } else { - edit.add_placeholder_snippet(cap, tail_expr); - } - } - - fn_def } } @@ -420,32 +398,33 @@ impl FunctionBuilder { /// * If we could infer the return type, don't focus it (and thus focus the function body) so the /// user can change the `todo!` function body. fn make_return_type( - ctx: &AssistContext<'_>, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, expr: &ast::Expr, target_module: Module, necessary_generic_params: &mut FxHashSet<hir::GenericParam>, ) -> (Option<ast::RetType>, bool) { - let (ret_ty, should_focus_return_type) = { + let (ret_ty, should_focus_return_type) = match ctx.sema.type_of_expr(expr).map(TypeInfo::original) { - Some(ty) if ty.is_unknown() => (Some(make::ty_placeholder()), true), - None => (Some(make::ty_placeholder()), true), + Some(ty) if ty.is_unknown() => (Some(make.ty_placeholder()), true), + None => (Some(make.ty_placeholder()), true), Some(ty) if ty.is_unit() => (None, false), Some(ty) => { necessary_generic_params.extend(ty.generic_params(ctx.db())); let rendered = ty.display_source_code(ctx.db(), target_module.into(), true); match rendered { - Ok(rendered) => (Some(make::ty(&rendered)), false), - Err(_) => (Some(make::ty_placeholder()), true), + Ok(rendered) => (Some(make.ty(&rendered)), false), + Err(_) => (Some(make.ty_placeholder()), true), } } - } - }; - let ret_type = ret_ty.map(make::ret_type); + }; + let ret_type = ret_ty.map(|ty| make.ret_type(ty)); (ret_type, should_focus_return_type) } fn make_fn_body_as_new_function( - ctx: &AssistContext<'_>, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, fn_name: &str, adt_info: &Option<AdtInfo>, edition: Edition, @@ -455,7 +434,7 @@ fn make_fn_body_as_new_function( }; let adt_info = adt_info.as_ref()?; - let path_self = make::ext::ident_path("Self"); + let path_self = make.ident_path("Self"); let placeholder_expr = expr_fill_default(ctx.config); let tail_expr = if let Some(strukt) = adt_info.adt.as_struct() { match strukt.kind(ctx.db()) { @@ -464,8 +443,8 @@ fn make_fn_body_as_new_function( .fields(ctx.db()) .iter() .map(|field| { - make::record_expr_field( - make::name_ref(&format!( + make.record_expr_field( + make.name_ref(&format!( "{}", field.name(ctx.db()).display(ctx.db(), edition) )), @@ -474,7 +453,7 @@ fn make_fn_body_as_new_function( }) .collect::<Vec<_>>(); - make::record_expr(path_self, make::record_expr_field_list(fields)).into() + make.record_expr(path_self, make.record_expr_field_list(fields)).into() } StructKind::Tuple => { let args = strukt @@ -483,20 +462,20 @@ fn make_fn_body_as_new_function( .map(|_| placeholder_expr.clone()) .collect::<Vec<_>>(); - make::expr_call(make::expr_path(path_self), make::arg_list(args)).into() + make.expr_call(make.expr_path(path_self), make.arg_list(args)).into() } - StructKind::Unit => make::expr_path(path_self), + StructKind::Unit => make.expr_path(path_self), } } else { placeholder_expr }; - let fn_body = make::block_expr(vec![], Some(tail_expr)); + let fn_body = make.block_expr(vec![], Some(tail_expr)); Some(fn_body) } fn get_fn_target_info( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Option<Module>, call: CallExpr, ) -> Option<TargetInfo> { @@ -505,7 +484,7 @@ fn get_fn_target_info( } fn get_fn_target( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Option<Module>, call: CallExpr, ) -> Option<(GeneratedFunctionTarget, FileId)> { @@ -522,7 +501,7 @@ fn get_fn_target( } fn get_method_target( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, impl_: &Option<ast::Impl>, adt: &Adt, ) -> Option<GeneratedFunctionTarget> { @@ -534,7 +513,7 @@ fn get_method_target( } fn assoc_fn_target_info( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, call: &CallExpr, adt: hir::Adt, fn_name: &str, @@ -575,92 +554,236 @@ impl GeneratedFunctionTarget { } } - fn insert_impl_at(&self, edit: &mut SourceChangeBuilder, impl_: ast::Impl) { + fn insert_impl_at( + &self, + edit: &mut SourceChangeBuilder, + file: FileId, + ctx: &AssistContext<'_, '_>, + function_builder: &FunctionBuilder, + adt: Adt, + cap: Option<SnippetCap>, + ) { + let editor = edit.make_editor(self.syntax()); + match self { GeneratedFunctionTarget::AfterItem(item) => { - let item = edit.make_syntax_mut(item.clone()); let position = if item.parent().is_some() { - ted::Position::after(&item) + Position::after(item) } else { - ted::Position::first_child_of(&item) + Position::first_child_of(item) }; - let indent = IndentLevel::from_node(&item); - let leading_ws = make::tokens::whitespace(&format!("\n{indent}")); - impl_.indent(indent); - - ted::insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]); + let indent = IndentLevel::from_node(item); + insert_rendered_impl( + &editor, + edit, + ctx, + function_builder, + adt, + position, + indent, + indent, + cap, + ); } GeneratedFunctionTarget::InEmptyItemList(item_list) => { - let item_list = edit.make_syntax_mut(item_list.clone()); let insert_after = item_list.children_with_tokens().find_or_first(|child| child.kind() == T!['{']); let position = match insert_after { - Some(child) => ted::Position::after(child), - None => ted::Position::first_child_of(&item_list), + Some(child) => Position::after(child), + None => Position::first_child_of(item_list), }; - let indent = IndentLevel::from_node(&item_list); + let indent = IndentLevel::from_node(item_list); let leading_indent = indent + 1; - let leading_ws = make::tokens::whitespace(&format!("\n{leading_indent}")); - impl_.indent(indent); - - ted::insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]); + insert_rendered_impl( + &editor, + edit, + ctx, + function_builder, + adt, + position, + indent, + leading_indent, + cap, + ); } GeneratedFunctionTarget::InImpl(_) => { unreachable!("can't insert an impl inside an impl") } } + edit.add_file_edits(file, editor); } - fn insert_fn_at(&self, edit: &mut SourceChangeBuilder, func: ast::Fn) { + fn insert_fn_at( + &self, + edit: &mut SourceChangeBuilder, + file: FileId, + function_builder: &FunctionBuilder, + cap: Option<SnippetCap>, + ) { + let editor = edit.make_editor(self.syntax()); + let make = editor.make(); + match self { GeneratedFunctionTarget::AfterItem(item) => { - let item = edit.make_syntax_mut(item.clone()); let position = if item.parent().is_some() { - ted::Position::after(&item) + Position::after(item) } else { - ted::Position::first_child_of(&item) + Position::first_child_of(item) }; - let indent = IndentLevel::from_node(&item); - let leading_ws = make::tokens::whitespace(&format!("\n\n{indent}")); - func.indent(indent); - - ted::insert_all_raw( + let indent = IndentLevel::from_node(item); + insert_rendered_fn( + &editor, + edit, + function_builder, position, - vec![leading_ws.into(), func.syntax().clone().into()], + indent, + format!("\n\n{indent}"), + None, + cap, ); } GeneratedFunctionTarget::InEmptyItemList(item_list) => { - let item_list = edit.make_syntax_mut(item_list.clone()); let insert_after = item_list.children_with_tokens().find_or_first(|child| child.kind() == T!['{']); let position = match insert_after { - Some(child) => ted::Position::after(child), - None => ted::Position::first_child_of(&item_list), + Some(child) => Position::after(child), + None => Position::first_child_of(item_list), }; - let indent = IndentLevel::from_node(&item_list); + let indent = IndentLevel::from_node(item_list); let leading_indent = indent + 1; - let leading_ws = make::tokens::whitespace(&format!("\n{leading_indent}")); - let trailing_ws = make::tokens::whitespace(&format!("\n{indent}")); - func.indent(leading_indent); - - ted::insert_all( + insert_rendered_fn( + &editor, + edit, + function_builder, position, - vec![leading_ws.into(), func.syntax().clone().into(), trailing_ws.into()], + leading_indent, + format!("\n{leading_indent}"), + Some(format!("\n{indent}")), + cap, ); } GeneratedFunctionTarget::InImpl(impl_) => { - let impl_ = edit.make_mut(impl_.clone()); - let leading_indent = impl_.indent_level() + 1; - func.indent(leading_indent); - impl_.get_or_create_assoc_item_list().add_item(func.into()); + if let Some(item_list) = impl_.assoc_item_list() { + let insert_after_item = item_list.assoc_items().last(); + let insert_after = item_list + .assoc_items() + .last() + .map(|it| it.syntax().clone().into()) + .or_else(|| { + item_list + .syntax() + .children_with_tokens() + .find_or_first(|child| child.kind() == T!['{']) + }); + let position = match insert_after { + Some(child) => Position::after(child), + None => Position::first_child_of(item_list.syntax()), + }; + let indent = impl_.indent_level(); + let leading_ws = if insert_after_item.is_some() { + format!("\n\n{leading_indent}") + } else { + format!("\n{leading_indent}") + }; + let trailing_ws = insert_after_item.is_none().then(|| format!("\n{indent}")); + insert_rendered_fn( + &editor, + edit, + function_builder, + position, + leading_indent, + leading_ws, + trailing_ws, + cap, + ); + } else { + let func = function_builder.render(make).indent(leading_indent); + let item_list = make.assoc_item_list([func.into()]); + if let Some(fn_) = item_list.syntax().descendants().find_map(ast::Fn::cast) { + add_generated_fn_annotation(&editor, edit, function_builder, &fn_, cap); + } + editor.insert(Position::last_child_of(impl_.syntax()), item_list.syntax()); + } } } + edit.add_file_edits(file, editor); + } +} + +fn insert_rendered_impl( + editor: &SyntaxEditor, + edit: &mut SourceChangeBuilder, + ctx: &AssistContext<'_, '_>, + function_builder: &FunctionBuilder, + adt: Adt, + position: Position, + impl_indent: IndentLevel, + leading_ws_indent: IndentLevel, + cap: Option<SnippetCap>, +) { + let make = editor.make(); + let leading_ws = make.whitespace(&format!("\n{leading_ws_indent}")); + let name = make.ty_path(make.ident_path(&format!( + "{}", + adt.name(ctx.db()).display(ctx.db(), function_builder.target_edition) + ))); + + // FIXME: adt may have generic params. + let fn_ = function_builder.render(make).indent(IndentLevel(1)); + let impl_ = + make.impl_(None, None, None, name.into(), None, Some(make.assoc_item_list([fn_.into()]))); + let impl_ = impl_.indent(impl_indent); + if let Some(fn_) = impl_.syntax().descendants().find_map(ast::Fn::cast) { + add_generated_fn_annotation(editor, edit, function_builder, &fn_, cap); + } + + editor.insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]); +} + +fn insert_rendered_fn( + editor: &SyntaxEditor, + edit: &mut SourceChangeBuilder, + function_builder: &FunctionBuilder, + position: Position, + indent: IndentLevel, + leading_ws: String, + trailing_ws: Option<String>, + cap: Option<SnippetCap>, +) { + let make = editor.make(); + let leading_ws = make.whitespace(&leading_ws); + let func = function_builder.render(make).indent(indent); + add_generated_fn_annotation(editor, edit, function_builder, &func, cap); + + let mut elements = vec![leading_ws.into(), func.syntax().clone().into()]; + if let Some(trailing_ws) = trailing_ws { + elements.push(make.whitespace(&trailing_ws).into()); + } + editor.insert_all(position, elements); +} + +fn add_generated_fn_annotation( + editor: &SyntaxEditor, + edit: &mut SourceChangeBuilder, + function_builder: &FunctionBuilder, + fn_: &ast::Fn, + cap: Option<SnippetCap>, +) { + let Some(cap) = cap else { return }; + + let annotation = edit.make_placeholder_snippet(cap); + if function_builder.should_focus_return_type + && let Some(ret_type) = fn_.ret_type() + { + editor.add_annotation(ret_type.syntax(), annotation); + } else if let Some(tail_expr) = fn_.body().and_then(|body| body.tail_expr()) { + editor.add_annotation(tail_expr.syntax(), annotation); } } @@ -677,7 +800,8 @@ impl AdtInfo { /// Computes parameter list for the generated function. fn fn_args( - ctx: &AssistContext<'_>, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, target_module: Module, call: ast::CallableExpr, necessary_generic_params: &mut FxHashSet<hir::GenericParam>, @@ -689,14 +813,15 @@ fn fn_args( arg_types.push(fn_arg_type(ctx, target_module, &arg, necessary_generic_params)); } deduplicate_arg_names(&mut arg_names); - let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| { - make::param(make::ext::simple_ident_pat(make::name(&name)).into(), make::ty(&ty)) - }); + let params = arg_names + .into_iter() + .zip(arg_types) + .map(|(name, ty)| make.param(make.simple_ident_pat(make.name(&name)).into(), make.ty(&ty))); - Some(make::param_list( + Some(make.param_list( match call { ast::CallableExpr::Call(_) => None, - ast::CallableExpr::MethodCall(_) => Some(make::self_param()), + ast::CallableExpr::MethodCall(_) => Some(make.self_param()), }, params, )) @@ -711,7 +836,8 @@ fn fn_args( /// currently do anything about it because it's actually easy to resolve it after the assist: just /// use the Rename functionality. fn fn_generic_params( - ctx: &AssistContext<'_>, + make: &SyntaxFactory, + ctx: &AssistContext<'_, '_>, necessary_params: FxHashSet<hir::GenericParam>, target: &GeneratedFunctionTarget, ) -> Option<(Option<ast::GenericParamList>, Option<ast::WhereClause>)> { @@ -737,45 +863,45 @@ fn fn_generic_params( filter_unnecessary_bounds(&mut generic_params, &mut where_preds, necessary_params); filter_bounds_in_scope(&mut generic_params, &mut where_preds, ctx, target); + let source_scope = generic_params.first().and_then(|param| ctx.sema.scope(param.node.syntax())); + let target_scope = source_scope.as_ref().and_then(|_| ctx.sema.scope(&target.parent())); + let generic_params: Vec<ast::GenericParam> = - generic_params.into_iter().map(|it| it.node.clone_for_update()).collect(); - let where_preds: Vec<ast::WherePred> = - where_preds.into_iter().map(|it| it.node.clone_for_update()).collect(); - - let (generic_params, where_preds): (Vec<ast::GenericParam>, Vec<ast::WherePred>) = - if let Some(param) = generic_params.first() - && let source_scope = ctx.sema.scope(param.syntax())? - && let target_scope = ctx.sema.scope(&target.parent())? - && source_scope.module() != target_scope.module() - { - // 4. Rewrite paths - let transform = PathTransform::generic_transformation(&target_scope, &source_scope); - let generic_params = generic_params.iter().map(|it| it.syntax()); - let where_preds = where_preds.iter().map(|it| it.syntax()); - transform - .apply_all(generic_params.chain(where_preds)) - .into_iter() - .filter_map(|it| { - if let Some(it) = ast::GenericParam::cast(it.clone()) { - Some(either::Either::Left(it)) - } else { - ast::WherePred::cast(it).map(either::Either::Right) - } - }) - .partition_map(|it| it) - } else { - (generic_params, where_preds) - }; + generic_params.into_iter().map(|it| it.node).collect(); + let where_preds: Vec<ast::WherePred> = where_preds.into_iter().map(|it| it.node).collect(); + + let (generic_params, where_preds) = if let Some(source_scope) = source_scope + && let Some(target_scope) = target_scope + && source_scope.module() != target_scope.module() + { + // 4. Rewrite paths + let transform = PathTransform::generic_transformation(&target_scope, &source_scope); + let generic_params = generic_params.iter().map(|it| it.syntax()); + let where_preds = where_preds.iter().map(|it| it.syntax()); + transform + .apply_all(generic_params.chain(where_preds)) + .into_iter() + .filter_map(|it| { + if let Some(it) = ast::GenericParam::cast(it.clone()) { + Some(either::Either::Left(it)) + } else { + ast::WherePred::cast(it).map(either::Either::Right) + } + }) + .partition_map(|it| it) + } else { + (generic_params, where_preds) + }; - let generic_param_list = make::generic_param_list(generic_params); + let generic_param_list = make.generic_param_list(generic_params); let where_clause = - if where_preds.is_empty() { None } else { Some(make::where_clause(where_preds)) }; + if where_preds.is_empty() { None } else { Some(make.where_clause(where_preds)) }; Some((Some(generic_param_list), where_clause)) } fn params_and_where_preds_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> (Vec<ast::GenericParam>, Vec<ast::WherePred>) { let Some(body) = containing_body(ctx) else { return Default::default(); @@ -816,7 +942,7 @@ fn params_and_where_preds_in_scope( (generic_params, where_clauses) } -fn containing_body(ctx: &AssistContext<'_>) -> Option<hir::DefWithBody> { +fn containing_body(ctx: &AssistContext<'_, '_>) -> Option<hir::DefWithBody> { let item: ast::Item = ctx.find_node_at_offset()?; let def = match item { ast::Item::Fn(it) => ctx.sema.to_def(&it)?.into(), @@ -828,7 +954,7 @@ fn containing_body(ctx: &AssistContext<'_>) -> Option<hir::DefWithBody> { } fn get_bounds_in_scope<D>( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, def: D, ) -> (impl Iterator<Item = ast::GenericParam>, impl Iterator<Item = ast::WherePred>) where @@ -894,7 +1020,7 @@ struct WherePredWithParams { } fn compute_contained_params_in_generic_param( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, node: ast::GenericParam, ) -> Option<ParamBoundWithParams> { match &node { @@ -923,7 +1049,7 @@ fn compute_contained_params_in_generic_param( } fn compute_contained_params_in_where_pred( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, node: ast::WherePred, ) -> Option<WherePredWithParams> { let self_ty = node.ty()?; @@ -944,7 +1070,10 @@ fn compute_contained_params_in_where_pred( Some(WherePredWithParams { node, self_ty_params, other_params }) } -fn filter_generic_params(ctx: &AssistContext<'_>, node: SyntaxNode) -> Option<hir::GenericParam> { +fn filter_generic_params( + ctx: &AssistContext<'_, '_>, + node: SyntaxNode, +) -> Option<hir::GenericParam> { let path = ast::Path::cast(node)?; match ctx.sema.resolve_path(&path)? { PathResolution::TypeParam(it) => Some(it.into()), @@ -1039,7 +1168,7 @@ fn filter_unnecessary_bounds( fn filter_bounds_in_scope( generic_params: &mut Vec<ParamBoundWithParams>, where_preds: &mut Vec<WherePredWithParams>, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target: &GeneratedFunctionTarget, ) -> Option<()> { let target_impl = target.parent().ancestors().find_map(ast::Impl::cast)?; @@ -1122,13 +1251,13 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri } fn fn_arg_type( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Module, fn_arg: &ast::Expr, generic_params: &mut FxHashSet<hir::GenericParam>, ) -> String { fn maybe_displayed_type( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Module, fn_arg: &ast::Expr, generic_params: &mut FxHashSet<hir::GenericParam>, @@ -1221,7 +1350,7 @@ enum Visibility { fn calculate_necessary_visibility( current_module: Module, target_module: Module, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Visibility { let db = ctx.db(); let current_module = current_module.nearest_non_block_module(db); @@ -3242,4 +3371,82 @@ impl Foo { ", ) } + + #[test] + fn generate_method_skips_trait_impl_for_inherent() { + // regression: rust-lang/rust-analyzer#22123 + check_assist( + generate_function, + r" +struct Bar; + +impl Bar { + fn func1() {} +} + +trait Foo { fn foo(&self); } + +impl Foo for Bar { + fn foo(&self) { + self.func2$0(); + } +} +", + r" +struct Bar; + +impl Bar { + fn func1() {} + + fn func2(&self) ${0:-> _} { + todo!() + } +} + +trait Foo { fn foo(&self); } + +impl Foo for Bar { + fn foo(&self) { + self.func2(); + } +} +", + ) + } + + #[test] + fn generate_method_from_trait_impl_creates_new_inherent_impl() { + // #22123: no inherent impl exists, so the assist must synthesize one + // instead of inserting into the trait impl. + check_assist( + generate_function, + r" +struct Bar; + +trait Foo { fn foo(&self); } + +impl Foo for Bar { + fn foo(&self) { + self.func2$0(); + } +} +", + r" +struct Bar; +impl Bar { + fn func2(&self) ${0:-> _} { + todo!() + } +} + +trait Foo { fn foo(&self); } + +impl Foo for Bar { + fn foo(&self) { + self.func2(); + } +} +", + ) + } } diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index b884581041..7e5d5cec71 100644 --- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -35,7 +35,7 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { // This if condition denotes two modes this assist can work in: // - First is acting upon selection of record fields // - Next is acting upon a single record field @@ -124,7 +124,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt // } // } // ``` -pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { generate_getter_impl(acc, ctx, false) } @@ -149,7 +149,7 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt // } // } // ``` -pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { generate_getter_impl(acc, ctx, true) } @@ -175,7 +175,7 @@ enum AssistType { pub(crate) fn generate_getter_impl( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, mutable: bool, ) -> Option<()> { let (strukt, info_of_record_fields, fn_names) = @@ -215,7 +215,7 @@ pub(crate) fn generate_getter_impl( } fn generate_getter_from_info( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, info: &AssistInfo, record_field_info: &RecordFieldInfo, make: &SyntaxFactory, @@ -329,7 +329,7 @@ fn generate_setter_from_info( } fn extract_and_parse( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, assist_type: AssistType, ) -> Option<(ast::Struct, Vec<RecordFieldInfo>, Vec<String>)> { // This if condition denotes two modes assists can work in: @@ -411,7 +411,7 @@ fn parse_record_field( } fn items( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, info_of_record_fields: Vec<RecordFieldInfo>, assist_info: &AssistInfo, make: &SyntaxFactory, @@ -432,7 +432,7 @@ fn items( fn build_source_change( builder: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, info_of_record_fields: Vec<RecordFieldInfo>, assist_info: AssistInfo, ) { @@ -458,7 +458,7 @@ fn build_source_change( let make = editor.make(); let items = items(ctx, info_of_record_fields, &assist_info, make); let ty_params = assist_info.strukt.generic_param_list(); - let ty_args = ty_params.as_ref().map(|it| it.to_generic_args()); + let ty_args = ty_params.as_ref().map(|it| it.to_generic_args(make)); let impl_def = make.impl_( None, ty_params, diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs index c5a46f6981..ab0eb56fcf 100644 --- a/crates/ide-assists/src/handlers/generate_impl.rs +++ b/crates/ide-assists/src/handlers/generate_impl.rs @@ -7,7 +7,7 @@ use crate::{ AssistContext, AssistId, Assists, utils::{ self, DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl, - generate_impl_with_factory, generate_trait_impl_intransitive, + generate_trait_impl_intransitive, }, }; @@ -45,7 +45,7 @@ fn insert_impl(editor: &SyntaxEditor, impl_: &ast::Impl, nominal: &impl AstNodeE // // impl<T: Clone> Ctx<T> {$0} // ``` -pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let nominal = ctx.find_node_at_offset::<ast::Adt>()?; let name = nominal.name()?; let target = nominal.syntax().text_range(); @@ -61,7 +61,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio |edit| { let editor = edit.make_editor(nominal.syntax()); let make = editor.make(); - let impl_ = generate_impl_with_factory(make, &nominal); + let impl_ = utils::generate_impl(make, &nominal); let impl_ = insert_impl(&editor, &impl_, &nominal); // Add a tabstop after the left curly brace @@ -93,7 +93,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio // // impl<T: Clone> ${1:_} for Ctx<T> {$0} // ``` -pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let nominal = ctx.find_node_at_offset::<ast::Adt>()?; let name = nominal.name()?; let target = nominal.syntax().text_range(); @@ -149,7 +149,7 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> // } // } // ``` -pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let name = ctx.find_node_at_offset::<ast::Name>()?; let trait_ = ast::Trait::cast(name.syntax().parent()?)?; let target_scope = ctx.sema.scope(trait_.syntax())?; diff --git a/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs index f10b21b13e..39f304cac9 100644 --- a/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs +++ b/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs @@ -39,7 +39,10 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_is_empty_from_len( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let fn_node = ctx.find_node_at_offset::<ast::Fn>()?; let fn_name = fn_node.name()?; @@ -86,7 +89,7 @@ pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext< } fn get_impl_method( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, impl_: &ast::Impl, fn_name: &Name, ) -> Option<hir::Function> { diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index acf0819222..fd095dd9b2 100644 --- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -44,7 +44,10 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_mut_trait_impl( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; let indent = impl_def.indent_level(); diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs index 520709adc5..40adfceaae 100644 --- a/crates/ide-assists/src/handlers/generate_new.rs +++ b/crates/ide-assists/src/handlers/generate_new.rs @@ -1,6 +1,6 @@ use ide_db::{ imports::import_assets::item_for_path_search, syntax_helpers::suggest_name::NameGenerator, - use_trivial_constructor::use_trivial_constructor, + use_trivial_constructor::use_trivial_constructor_with_factory, }; use syntax::{ ast::{self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit}, @@ -33,7 +33,7 @@ use crate::{ // } // } // ``` -pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let strukt = ctx.find_node_at_offset::<ast::Struct>()?; let field_list: Vec<(String, ast::Type)> = match strukt.kind() { StructKind::Record(named) => { @@ -90,9 +90,10 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let edition = current_module.krate(ctx.db()).edition(ctx.db()); - let expr = use_trivial_constructor( + let expr = use_trivial_constructor_with_factory( + make, ctx.sema.db, - ide_db::helpers::mod_path_to_ast(&type_path, edition), + ide_db::helpers::mod_path_to_ast_with_factory(make, &type_path, edition), &ty, edition, )?; diff --git a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 10c009a2ea..4348dfa212 100644 --- a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -1,8 +1,9 @@ use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; +use ide_db::use_trivial_constructor::use_trivial_constructor_with_factory; use ide_db::{ - RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast, - imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor, + RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory, + imports::import_assets::item_for_path_search, }; use syntax::syntax_editor::{Position, SyntaxEditor}; use syntax::{ @@ -46,7 +47,7 @@ use crate::{ // ``` pub(crate) fn generate_single_field_struct_from( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let strukt_name = ctx.find_node_at_offset::<ast::Name>()?; let adt = ast::Adt::cast(strukt_name.syntax().parent()?)?; @@ -86,7 +87,7 @@ pub(crate) fn generate_single_field_struct_from( let indent = strukt.indent_level(); let ty_where_clause = strukt.where_clause(); let type_gen_params = strukt.generic_param_list(); - let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args()); + let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args(make)); let trait_gen_args = Some(make.generic_arg_list( [ast::GenericArg::TypeArg(make.type_arg(main_field_ty.clone()))], false, @@ -178,7 +179,7 @@ fn make_adt_constructor( } fn make_constructors( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, module: hir::Module, types: &[ast::Type], ) -> Vec<Option<ast::Expr>> { @@ -197,7 +198,13 @@ fn make_constructors( let ty_path = module.find_path(db, item_for_path_search(db, item_in_ns)?, cfg)?; - use_trivial_constructor(db, mod_path_to_ast(&ty_path, edition), &ty, edition) + use_trivial_constructor_with_factory( + &make, + db, + mod_path_to_ast_with_factory(&make, &ty_path, edition), + &ty, + edition, + ) }) .collect() } diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 049398de8c..2493ba6632 100644 --- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -68,7 +68,10 @@ use syntax::{ // const_maker! {i32, 7} // } // ``` -pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn generate_trait_from_impl( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { // Get AST Node let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?; @@ -131,7 +134,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ ]; if let Some(params) = impl_ast.generic_param_list() { - let gen_args = ¶ms.to_generic_args(); + let gen_args = ¶ms.to_generic_args(make); elements.insert(1, gen_args.syntax().clone().into()); } diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 21f2249a19..af048c6ae0 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -1,6 +1,5 @@ use std::collections::BTreeSet; -use ast::make; use either::Either; use hir::{ FileRange, PathResolution, Semantics, TypeInfo, @@ -8,22 +7,23 @@ use hir::{ sym, }; use ide_db::{ - EditionedFileId, RootDatabase, + EditionedFileId, FileId, FxHashMap, RootDatabase, base_db::Crate, defs::Definition, - imports::insert_use::remove_path_if_in_use_stmt, + imports::insert_use::remove_use_tree_if_simple, path_transform::PathTransform, search::{FileReference, FileReferenceNode, SearchScope}, - source_change::SourceChangeBuilder, syntax_helpers::{node_ext::expr_as_name_ref, prettify_macro_expansion}, }; use itertools::{Itertools, izip}; use syntax::{ - AstNode, NodeOrToken, SyntaxKind, + AstNode, NodeOrToken, SyntaxKind, TextRange, ast::{ - self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::IndentLevel, edit_in_place::Indent, + self, HasArgList, HasGenericArgs, Pat, PathExpr, + edit::{AstNodeEdit, IndentLevel}, + make, }, - ted, + syntax_editor::SyntaxEditor, }; use crate::{ @@ -69,7 +69,7 @@ use crate::{ // }; // } // ``` -pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let def_file = ctx.file_id(); let vfs_def_file = ctx.vfs_file_id(); let name = ctx.find_node_at_offset::<ast::Name>()?; @@ -106,37 +106,59 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> let mut usages = usages.all(); let current_file_usage = usages.references.remove(&def_file); + let mut file_editors: FxHashMap<FileId, SyntaxEditor> = FxHashMap::default(); let mut remove_def = true; let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec<FileReference>| { let file_id = file_id.file_id(ctx.db()); builder.edit_file(file_id); let call_krate = ctx.sema.file_to_module_def(file_id).map(|it| it.krate(ctx.db())); let count = refs.len(); - // The collects are required as we are otherwise iterating while mutating 🙅♀️🙅♂️ - let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some); + let (name_refs, use_trees) = split_refs_and_uses(refs, Some); let call_infos: Vec<_> = name_refs .into_iter() .filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into())) // FIXME: do not handle callsites in macros' parameters, because // directly inlining into macros may cause errors. .filter(|call_info| !ctx.sema.hir_file_for(call_info.node.syntax()).is_macro()) - .map(|call_info| { - let mut_node = builder.make_syntax_mut(call_info.node.syntax().clone()); - (call_info, mut_node) - }) .collect(); - let replaced = call_infos - .into_iter() - .map(|(call_info, mut_node)| { - let replacement = - inline(&ctx.sema, def_file, function, &func_body, ¶ms, &call_info); - ted::replace(mut_node, replacement.syntax()); - }) - .count(); - if replaced + name_refs_use.len() == count { - // we replaced all usages in this file, so we can remove the imports - name_refs_use.iter().for_each(remove_path_if_in_use_stmt); - } else { + + // Skip calls nested inside other calls being inlined to avoid overlapping + // edits. Nested calls are implicitly replaced when the outer call is inlined. + let all_ranges: Vec<TextRange> = + call_infos.iter().map(|ci| ci.node.syntax().text_range()).collect(); + let (call_infos, nested_infos): (Vec<_>, Vec<_>) = + call_infos.into_iter().partition(|ci| { + let r = ci.node.syntax().text_range(); + !all_ranges.iter().any(|&other| other != r && other.contains_range(r)) + }); + let nested_count = nested_infos.len(); + + let anchor = call_infos + .first() + .map(|ci| ci.node.syntax().clone()) + .or_else(|| use_trees.first().map(|ut| ut.syntax().clone())); + if let Some(anchor) = anchor { + let editor = + file_editors.entry(file_id).or_insert_with(|| builder.make_editor(&anchor)); + let replaced = call_infos + .into_iter() + .map(|call_info| { + let replacement = inline( + &ctx.sema, def_file, function, &func_body, ¶ms, &call_info, + editor, + ); + editor.replace(call_info.node.syntax(), replacement.syntax()); + }) + .count(); + if replaced + nested_count + use_trees.len() == count { + // we replaced all usages in this file, so we can remove the imports + for use_tree in &use_trees { + remove_use_tree_if_simple(use_tree, editor); + } + } else { + remove_def = false; + } + } else if use_trees.len() != count { remove_def = false; } }; @@ -148,24 +170,29 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> None => builder.edit_file(vfs_def_file), } if remove_def { - builder.delete(ast_func.syntax().text_range()); + let editor = file_editors + .entry(vfs_def_file) + .or_insert_with(|| builder.make_editor(ast_func.syntax())); + editor.delete(ast_func.syntax()); + } + for (file_id, editor) in file_editors { + builder.add_file_edits(file_id, editor); } }, ) } pub(super) fn split_refs_and_uses<T: ast::AstNode>( - builder: &mut SourceChangeBuilder, iter: impl IntoIterator<Item = FileReference>, mut map_ref: impl FnMut(ast::NameRef) -> Option<T>, -) -> (Vec<T>, Vec<ast::Path>) { +) -> (Vec<T>, Vec<ast::UseTree>) { iter.into_iter() .filter_map(|file_ref| match file_ref.name { FileReferenceNode::NameRef(name_ref) => Some(name_ref), _ => None, }) .filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) { - Some(use_tree) => builder.make_mut(use_tree).path().map(Either::Right), + Some(use_tree) => Some(Either::Right(use_tree)), None => map_ref(name_ref).map(Either::Left), }) .partition_map(|either| either) @@ -192,7 +219,7 @@ pub(super) fn split_refs_and_uses<T: ast::AstNode>( // }; // } // ``` -pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let name_ref: ast::NameRef = ctx.find_node_at_offset()?; let call_info = CallInfo::from_name_ref( name_ref.clone(), @@ -235,14 +262,11 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< let syntax = call_info.node.syntax().clone(); acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| { - let replacement = inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info); - builder.replace_ast( - match call_info.node { - ast::CallableExpr::Call(it) => ast::Expr::CallExpr(it), - ast::CallableExpr::MethodCall(it) => ast::Expr::MethodCallExpr(it), - }, - replacement, - ); + let editor = builder.make_editor(call_info.node.syntax()); + let replacement = + inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info, &editor); + editor.replace(call_info.node.syntax(), replacement.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }) } @@ -318,21 +342,25 @@ fn inline( fn_body: &ast::BlockExpr, params: &[(ast::Pat, Option<ast::Type>, hir::Param<'_>)], CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo, + file_editor: &SyntaxEditor, ) -> ast::Expr { + let make = file_editor.make(); let file_id = sema.hir_file_for(fn_body.syntax()); - let mut body = if let Some(macro_file) = file_id.macro_file() { + let body_to_clone = if let Some(macro_file) = file_id.macro_file() { cov_mark::hit!(inline_call_defined_in_macro); let span_map = sema.db.expansion_span_map(macro_file); let body_prettified = - prettify_macro_expansion(sema.db, fn_body.syntax().clone(), &span_map, *krate); - if let Some(body) = ast::BlockExpr::cast(body_prettified) { - body - } else { - fn_body.clone_for_update() - } + prettify_macro_expansion(sema.db, fn_body.syntax().clone(), span_map, *krate); + if let Some(body) = ast::BlockExpr::cast(body_prettified) { body } else { fn_body.clone() } } else { - fn_body.clone_for_update() + fn_body.clone() }; + + // Capture before `with_ast_node` re-roots and loses the source-relative position. + let mut original_body_indent = IndentLevel::from_node(body_to_clone.syntax()); + let body_offset = body_to_clone.syntax().text_range().start(); + let (editor, body) = SyntaxEditor::with_ast_node(&body_to_clone); + let usages_for_locals = |local| { Definition::Local(local) .usages(sema) @@ -355,7 +383,7 @@ fn inline( .map(|FileReference { name, range, .. }| match name { FileReferenceNode::NameRef(_) => body .syntax() - .covering_element(range) + .covering_element(range - body_offset) .ancestors() .nth(3) .and_then(ast::PathExpr::cast), @@ -368,48 +396,60 @@ fn inline( }) .collect(); - if function.self_param(sema.db).is_some() { - let this = || { - make::name_ref("this") - .syntax() - .clone_for_update() - .first_token() - .expect("NameRef should have had a token.") - }; - if let Some(self_local) = params[0].2.as_local(sema.db) { - usages_for_locals(self_local) - .filter_map(|FileReference { name, range, .. }| match name { - FileReferenceNode::NameRef(_) => Some(body.syntax().covering_element(range)), - _ => None, - }) - .for_each(|usage| { - ted::replace(usage, this()); - }); - } - } + let has_self_param = function.self_param(sema.db).is_some(); + + // Collect all self token usages upfront (needed when a let binding is emitted). + // When self can be directly inlined, the parameter loop handles those PathExprs; + // when a let stmt is needed, we rename all self tokens to `this` here. + let self_token_usages: Vec<_> = if has_self_param { + params[0] + .2 + .as_local(sema.db) + .map(|self_local| { + usages_for_locals(self_local) + .filter_map(|FileReference { name, range, .. }| match name { + FileReferenceNode::NameRef(_) => { + Some(body.syntax().covering_element(range - body_offset)) + } + _ => None, + }) + .collect() + }) + .unwrap_or_default() + } else { + Vec::new() + }; - // We should place the following code after last usage of `usages_for_locals` - // because `ted::replace` will change the offset in syntax tree, which makes - // `FileReference` incorrect + // Replace `Self` type keywords with the actual impl type if let Some(imp) = sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast) && !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) && let Some(t) = imp.self_ty() { - while let Some(self_tok) = body + let self_tokens: Vec<_> = body .syntax() .descendants_with_tokens() .filter_map(NodeOrToken::into_token) - .find(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) - { - let replace_with = t.clone_subtree().syntax().clone_for_update(); - if !is_in_type_path(&self_tok) - && let Some(ty) = ast::Type::cast(replace_with.clone()) - && let Some(generic_arg_list) = ty.generic_arg_list() + .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) + .collect(); + for self_tok in self_tokens { + let replace_with = if !is_in_type_path(&self_tok) + && let Some(generic_arg_list) = t.generic_arg_list() { - ted::remove(generic_arg_list.syntax()); - } - ted::replace(self_tok, replace_with); + // Strip the outer generic arg list and reparse, since turbofish-less + // generics aren't valid in expression position. The outermost + // `GenericArgList` text is unique within `t`'s text (any inner generics + // are nested inside it), so `replacen(.., 1)` is safe. + let stripped = t.syntax().text().to_string().replacen( + &generic_arg_list.syntax().text().to_string(), + "", + 1, + ); + editor.make().ty(&stripped).syntax().clone() + } else { + t.syntax().clone() + }; + editor.replace(self_tok, replace_with); } } @@ -428,7 +468,19 @@ fn inline( } } - let mut let_stmts = Vec::new(); + let mut let_stmts: Vec<ast::Stmt> = Vec::new(); + + let this_token = editor + .make() + .name_ref("this") + .syntax() + .first_token() + .expect("NameRef should have had a token."); + let rewrite_self_to_this = |editor: &SyntaxEditor| { + for usage in &self_token_usages { + editor.replace(usage.clone(), this_token.clone()); + } + }; // Inline parameter expressions or generate `let` statements depending on whether inlining works or not. for ((pat, param_ty, param), usages, expr) in izip!(params, param_use_nodes, arguments) { @@ -444,7 +496,7 @@ fn inline( let param_ty_prettified = prettify_macro_expansion( sema.db, param_ty.syntax().clone(), - &span_map, + span_map, *krate, ); ast::Type::cast(param_ty_prettified).unwrap_or(param_ty) @@ -458,7 +510,7 @@ fn inline( let is_self = param.name(sema.db).is_some_and(|name| name == sym::self_); if is_self { - let mut this_pat = make::ident_pat(false, false, make::name("this")); + let mut this_pat = make.ident_pat(false, false, make.name("this")); let mut expr = expr.clone(); if let Pat::IdentPat(pat) = pat { match (pat.ref_token(), pat.mut_token()) { @@ -466,11 +518,11 @@ fn inline( (None, None) => {} // mut self => let mut this = obj (None, Some(_)) => { - this_pat = make::ident_pat(false, true, make::name("this")); + this_pat = make.ident_pat(false, true, make.name("this")); } // &self => let this = &obj (Some(_), None) => { - expr = make::expr_ref(expr, false); + expr = make.expr_ref(expr, false); } // let foo = &mut X; &mut self => let this = &mut obj // let mut foo = X; &mut self => let this = &mut *obj (reborrow) @@ -479,35 +531,38 @@ fn inline( .type_of_expr(&expr) .map(|ty| ty.original.is_mutable_reference()); expr = if let Some(true) = should_reborrow { - make::expr_reborrow(expr) + make.expr_reborrow(expr) } else { - make::expr_ref(expr, true) + make.expr_ref(expr, true) }; } } }; - let_stmts - .push(make::let_stmt(this_pat.into(), ty, Some(expr)).clone_for_update().into()) + let_stmts.push(make.let_stmt(this_pat.into(), ty, Some(expr)).into()) } else { - let_stmts.push( - make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(), - ); + let_stmts.push(make.let_stmt(pat.clone(), ty, Some(expr.clone())).into()); } }; + let is_self_param = + has_self_param && param.name(sema.db).is_some_and(|name| name == sym::self_); + // check if there is a local var in the function that conflicts with parameter // if it does then emit a let statement and continue if func_let_vars.contains(&expr.syntax().text().to_string()) { + if is_self_param { + rewrite_self_to_this(&editor); + } insert_let_stmt(); continue; } - let inline_direct = |usage, replacement: &ast::Expr| { + let inline_direct = |editor: &SyntaxEditor, usage: &PathExpr, replacement: &ast::Expr| { if let Some(field) = path_expr_as_record_field(usage) { cov_mark::hit!(inline_call_inline_direct_field); - field.replace_expr(replacement.clone_for_update()); + field.replace_expr(editor, replacement.clone()); } else { - ted::replace(usage.syntax(), replacement.syntax().clone_for_update()); + editor.replace(usage.syntax(), replacement.syntax()); } }; @@ -518,66 +573,74 @@ fn inline( && usage.syntax().parent().and_then(ast::Expr::cast).is_some() => { cov_mark::hit!(inline_call_inline_closure); - let expr = make::expr_paren(expr.clone()).into(); - inline_direct(usage, &expr); + let expr = editor.make().expr_paren(expr.clone()).into(); + inline_direct(&editor, usage, &expr); } // inline single use literals [usage] if matches!(expr, ast::Expr::Literal(_)) => { cov_mark::hit!(inline_call_inline_literal); - inline_direct(usage, expr); + inline_direct(&editor, usage, expr); } // inline direct local arguments [_, ..] if expr_as_name_ref(expr).is_some() => { cov_mark::hit!(inline_call_inline_locals); - usages.iter().for_each(|usage| inline_direct(usage, expr)); + usages.iter().for_each(|usage| inline_direct(&editor, usage, expr)); } // can't inline, emit a let statement _ => { + if is_self_param { + // Rename all `self` tokens to `this` so the let binding matches. + rewrite_self_to_this(&editor); + } insert_let_stmt(); } } } + // Apply all edits to get the transformed body + let edit = editor.finish(); + let mut body = ast::BlockExpr::cast(edit.new_root().clone()) + .expect("editor root should still be a BlockExpr"); + + // Apply generic substitution (needs immutable tree) if let Some(generic_arg_list) = generic_arg_list.clone() && let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) - { - body.reindent_to(IndentLevel(0)); - if let Some(new_body) = ast::BlockExpr::cast( + && let Some(new_body) = ast::BlockExpr::cast( PathTransform::function_call(target, source, function, generic_arg_list) .apply(body.syntax()), - ) { - body = new_body; - } + ) + { + body = new_body; } let is_async_fn = function.is_async(sema.db); if is_async_fn { cov_mark::hit!(inline_call_async_fn); - body = make::async_move_block_expr(body.statements(), body.tail_expr()).clone_for_update(); + body = make.async_move_block_expr(body.statements(), body.tail_expr()); // Arguments should be evaluated outside the async block, and then moved into it. if !let_stmts.is_empty() { cov_mark::hit!(inline_call_async_fn_with_let_stmts); - body.indent(IndentLevel(1)); - body = make::block_expr(let_stmts, Some(body.into())).clone_for_update(); + body = body.indent(IndentLevel(1)); + body = make.block_expr(let_stmts, Some(body.into())); } - } else if let Some(stmt_list) = body.stmt_list() { - let position = stmt_list.l_curly_token().expect("L_CURLY for StatementList is missing."); - let_stmts.into_iter().rev().for_each(|let_stmt| { - ted::insert(ted::Position::after(position.clone()), let_stmt.syntax().clone()); - }); + } else if !let_stmts.is_empty() { + // Prepend let statements to the body's existing statements + let stmts: Vec<ast::Stmt> = let_stmts.into_iter().chain(body.statements()).collect(); + body = make.block_expr(stmts, body.tail_expr()); + original_body_indent = IndentLevel(0); } let original_indentation = match node { ast::CallableExpr::Call(it) => it.indent_level(), ast::CallableExpr::MethodCall(it) => it.indent_level(), }; - body.reindent_to(original_indentation); + body = body.dedent(original_body_indent).indent(original_indentation); let no_stmts = body.statements().next().is_none(); match body.tail_expr() { Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => { - make::expr_paren(expr).clone_for_update().into() + make.expr_paren(expr).into() } Some(expr) if !is_async_fn && no_stmts => expr, _ => match node @@ -587,7 +650,7 @@ fn inline( .and_then(|bin_expr| bin_expr.lhs()) { Some(lhs) if lhs.syntax() == node.syntax() => { - make::expr_paren(ast::Expr::BlockExpr(body)).clone_for_update().into() + make.expr_paren(ast::Expr::BlockExpr(body)).into() } _ => ast::Expr::BlockExpr(body), }, @@ -1503,8 +1566,11 @@ async fn foo(arg: u32) -> u32 { } fn spawn<T>(_: T) {} fn main() { - spawn(async move { - bar(42).await * 2 + spawn({ + let arg = 42; + async move { + bar(arg).await * 2 + } }); } "#, @@ -1535,9 +1601,12 @@ async fn foo(arg: u32) -> u32 { } fn spawn<T>(_: T) {} fn main() { - spawn(async move { - bar(42).await; - 42 + spawn({ + let arg = 42; + async move { + bar(arg).await; + 42 + } }); } "#, @@ -1572,10 +1641,11 @@ fn spawn<T>(_: T) {} fn main() { let var = 42; spawn({ + let x = var; let y = var + 1; let z: &u32 = &var; async move { - bar(var).await; + bar(x).await; y + y + *z } }); @@ -1866,6 +1936,28 @@ fn _hash2(self_: &u64, state: &mut u64) { } #[test] + fn inline_callers_nested_calls() { + check_assist( + inline_into_callers, + r#" +fn id$0(x: u32) -> u32 { x } +fn main() { + let x = id(id(1)); +} +"#, + r#" + +fn main() { + let x = { + let x = id(1); + x + }; +} +"#, + ); + } + + #[test] fn inline_into_callers_in_macros_not_applicable() { check_assist_not_applicable( inline_into_callers, diff --git a/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/crates/ide-assists/src/handlers/inline_const_as_literal.rs index b11d3792bc..46cedea967 100644 --- a/crates/ide-assists/src/handlers/inline_const_as_literal.rs +++ b/crates/ide-assists/src/handlers/inline_const_as_literal.rs @@ -22,7 +22,10 @@ use crate::{AssistContext, AssistId, Assists}; // "Hello, World!" // } // ``` -pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_const_as_literal( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let variable = ctx.find_node_at_offset::<ast::PathExpr>()?; if let hir::PathResolution::Def(hir::ModuleDef::Const(konst)) = @@ -57,7 +60,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_> } fn validate_type_recursively( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ty_hir: Option<&hir::Type<'_>>, refed: bool, fuel: i32, diff --git a/crates/ide-assists/src/handlers/inline_local_variable.rs b/crates/ide-assists/src/handlers/inline_local_variable.rs index 2af074f1fc..531adf62ba 100644 --- a/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -32,7 +32,7 @@ use crate::{ // (1 + 2) * 4; // } // ``` -pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let file_id = ctx.file_id(); let range = ctx.selection_trimmed(); let InlineData { let_stmt, delete_let, references, target } = @@ -724,6 +724,7 @@ fn foo() { check_assist( inline_local_variable, r" +//- minicore: iterator fn foo() { let a$0 = vec![10, 20]; for i in a {} diff --git a/crates/ide-assists/src/handlers/inline_macro.rs b/crates/ide-assists/src/handlers/inline_macro.rs index 280bd7f2ca..002791b88d 100644 --- a/crates/ide-assists/src/handlers/inline_macro.rs +++ b/crates/ide-assists/src/handlers/inline_macro.rs @@ -1,6 +1,6 @@ use hir::db::ExpandDatabase; use ide_db::syntax_helpers::prettify_macro_expansion; -use syntax::ast::{self, AstNode}; +use syntax::ast::{self, AstNode, edit::AstNodeEdit}; use crate::{AssistContext, AssistId, Assists}; @@ -35,7 +35,7 @@ use crate::{AssistContext, AssistId, Assists}; // println!("{number}"); // } // ``` -pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?; let macro_call = ctx.sema.to_def(&unexpanded)?; let target_crate_id = ctx.sema.file_to_module_def(ctx.vfs_file_id())?.krate(ctx.db()).into(); @@ -46,12 +46,15 @@ pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option "Inline macro".to_owned(), text_range, |builder| { + let editor = builder.make_editor(unexpanded.syntax()); let expanded = ctx.sema.parse_or_expand(macro_call.into()); let span_map = ctx.sema.db.expansion_span_map(macro_call); // Don't call `prettify_macro_expansion()` outside the actual assist action; it does some heavy rowan tree manipulation, // which can be very costly for big macros when it is done *even without the assist being invoked*. - let expanded = prettify_macro_expansion(ctx.db(), expanded, &span_map, target_crate_id); - builder.replace(text_range, expanded.to_string()) + let expanded = prettify_macro_expansion(ctx.db(), expanded, span_map, target_crate_id); + let expanded = ast::edit::indent(&expanded, unexpanded.indent_level()); + editor.replace(unexpanded.syntax(), expanded); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -207,15 +210,15 @@ macro_rules! num { inline_macro, r#" macro_rules! foo { - () => {foo!()} + ($t:tt) => {foo!(1)} } -fn f() { let result = foo$0!(); } +fn f() { let result = foo$0!(0); } "#, r#" macro_rules! foo { - () => {foo!()} + ($t:tt) => {foo!(1)} } -fn f() { let result = foo!(); } +fn f() { let result = foo!(1); } "#, ); } @@ -258,7 +261,7 @@ macro_rules! whitespace { if true {} }; } -fn f() { if true{}; } +fn f() { if true {}; } "#, ) } @@ -297,12 +300,12 @@ macro_rules! foo { } fn main() { cfg_if!{ - if #[cfg(test)]{ - 1; - }else { - 1; - } -}; + if #[cfg(test)]{ + 1; + }else { + 1; + } + }; } "#, ); diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs index 6d8750afdc..e4a1314f5b 100644 --- a/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -5,8 +5,7 @@ use hir::{HasSource, PathResolution}; use ide_db::FxHashMap; use ide_db::{ - defs::Definition, imports::insert_use::ast_to_remove_for_path_in_use_stmt, - search::FileReference, + defs::Definition, imports::insert_use::remove_use_tree_if_simple, search::FileReference, }; use itertools::Itertools; use syntax::ast::syntax_factory::SyntaxFactory; @@ -46,7 +45,7 @@ use super::inline_call::split_refs_and_uses; // let _: i32 = 3; // } // ``` -pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let name = ctx.find_node_at_offset::<ast::Name>()?; let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?; @@ -72,14 +71,12 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) let source = ctx.sema.parse(file_id); let editor = builder.make_editor(source.syntax()); - let (path_types, path_type_uses) = - split_refs_and_uses(builder, refs, |path_type| { - path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast) - }); + let (path_types, path_type_uses) = split_refs_and_uses(refs, |path_type| { + path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast) + }); path_type_uses .iter() - .flat_map(ast_to_remove_for_path_in_use_stmt) - .for_each(|x| editor.delete(x.syntax())); + .for_each(|use_tree| remove_use_tree_if_simple(use_tree, &editor)); for (target, replacement) in path_types.into_iter().filter_map(|path_type| { let replacement = @@ -128,7 +125,7 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) // let a: Vec<u32>; // } // ``` -pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let alias_instance = ctx.find_node_at_offset::<ast::PathType>()?; let concrete_type; let replacement; @@ -415,7 +412,7 @@ fn create_replacement( editor.finish().new_root().clone() } -fn get_type_alias(ctx: &AssistContext<'_>, path: &ast::PathType) -> Option<ast::TypeAlias> { +fn get_type_alias(ctx: &AssistContext<'_, '_>, path: &ast::PathType) -> Option<ast::TypeAlias> { let resolved_path = ctx.sema.resolve_path(&path.path()?)?; // We need the generics in the correct order to be able to map any provided @@ -1094,7 +1091,6 @@ fn f() -> Vec<&str> { } //- /foo.rs - fn foo() { let _: Vec<i8> = Vec::new(); } @@ -1123,7 +1119,6 @@ mod foo; //- /foo.rs - fn foo() { let _: i32 = 0; } diff --git a/crates/ide-assists/src/handlers/into_to_qualified_from.rs b/crates/ide-assists/src/handlers/into_to_qualified_from.rs index 47b273535a..9f8b08f4f8 100644 --- a/crates/ide-assists/src/handlers/into_to_qualified_from.rs +++ b/crates/ide-assists/src/handlers/into_to_qualified_from.rs @@ -36,7 +36,7 @@ use crate::assist_context::{AssistContext, Assists}; // let b: B = B::from(a); // } // ``` -pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; let nameref = method_call.name_ref()?; let receiver = method_call.receiver()?; diff --git a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index 2cbeae1d19..986989952f 100644 --- a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -34,7 +34,10 @@ static ASSIST_LABEL: &str = "Introduce named lifetime"; // } // } // ``` -pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn introduce_named_lifetime( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { // FIXME: How can we handle renaming any one of multiple anonymous lifetimes? // FIXME: should also add support for the case fun(f: &Foo) -> &$0Foo let lifetime = diff --git a/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs b/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs index 95f223420b..427fbbeaa0 100644 --- a/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs +++ b/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs @@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn introduce_named_type_parameter( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?; let param = impl_trait_type.syntax().ancestors().find_map(ast::Param::cast)?; @@ -48,7 +48,8 @@ pub(crate) fn introduce_named_type_parameter( ) .for_impl_trait_as_generic(&impl_trait_type); - let type_param = make.type_param(make.name(&type_param_name), Some(type_bound_list)); + let type_bound_list = non_default_bounds(&type_bound_list).then_some(type_bound_list); + let type_param = make.type_param(make.name(&type_param_name), type_bound_list); let new_ty = make.ty(&type_param_name); editor.replace(impl_trait_type.syntax(), new_ty.syntax()); @@ -63,6 +64,10 @@ pub(crate) fn introduce_named_type_parameter( ) } +fn non_default_bounds(bounds: &ast::TypeBoundList) -> bool { + bounds.bounds().collect_array().is_none_or(|[bound]| bound.syntax().text() != "Sized") +} + #[cfg(test)] mod tests { use super::*; @@ -169,6 +174,24 @@ fn foo< } #[test] + fn replace_impl_default_bounds() { + check_assist( + introduce_named_type_parameter, + r#"fn foo(bar: $0impl Sized) {}"#, + r#"fn foo<$0S>(bar: S) {}"#, + ); + } + + #[test] + fn replace_impl_question_bounds() { + check_assist( + introduce_named_type_parameter, + r#"fn foo(bar: &$0impl ?Sized) {}"#, + r#"fn foo<$0S: ?Sized>(bar: &S) {}"#, + ); + } + + #[test] fn replace_impl_with_mut() { check_assist( introduce_named_type_parameter, diff --git a/crates/ide-assists/src/handlers/invert_if.rs b/crates/ide-assists/src/handlers/invert_if.rs index c8cb7bb60f..9dda4bbb96 100644 --- a/crates/ide-assists/src/handlers/invert_if.rs +++ b/crates/ide-assists/src/handlers/invert_if.rs @@ -26,7 +26,7 @@ use crate::{ // if y { B } else { A } // } // ``` -pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let if_keyword = ctx .find_token_syntax_at_offset(T![if]) .or_else(|| ctx.find_token_syntax_at_offset(T![else]))?; diff --git a/crates/ide-assists/src/handlers/merge_imports.rs b/crates/ide-assists/src/handlers/merge_imports.rs index 1dd0833fad..dc40a6a640 100644 --- a/crates/ide-assists/src/handlers/merge_imports.rs +++ b/crates/ide-assists/src/handlers/merge_imports.rs @@ -27,7 +27,7 @@ use Edit::*; // ``` // use std::{fmt::Formatter, io}; // ``` -pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (target, edits) = if ctx.has_empty_selection() { // Merge a neighbor cov_mark::hit!(merge_with_use_item_neighbors); diff --git a/crates/ide-assists/src/handlers/merge_match_arms.rs b/crates/ide-assists/src/handlers/merge_match_arms.rs index 6e84af5f91..f41769150c 100644 --- a/crates/ide-assists/src/handlers/merge_match_arms.rs +++ b/crates/ide-assists/src/handlers/merge_match_arms.rs @@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists, TextRange}; // } // } // ``` -pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let current_arm = ctx.find_node_at_trimmed_offset::<ast::MatchArm>()?; // Don't try to handle arms with guards for now - can add support for this later if current_arm.guard().is_some() { @@ -107,7 +107,7 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool { fn are_same_types( current_arm_types: &FxHashMap<String, Option<Type<'_>>>, arm: &ast::MatchArm, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> bool { let arm_types = get_arm_types(ctx, arm); for (other_arm_type_name, other_arm_type) in arm_types { @@ -122,14 +122,14 @@ fn are_same_types( } fn get_arm_types<'db>( - context: &AssistContext<'db>, + context: &AssistContext<'_, 'db>, arm: &ast::MatchArm, ) -> FxHashMap<String, Option<Type<'db>>> { let mut mapping: FxHashMap<String, Option<Type<'db>>> = FxHashMap::default(); fn recurse<'db>( map: &mut FxHashMap<String, Option<Type<'db>>>, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, pat: &Option<ast::Pat>, ) { if let Some(local_pat) = pat { diff --git a/crates/ide-assists/src/handlers/merge_nested_if.rs b/crates/ide-assists/src/handlers/merge_nested_if.rs index e491c043e1..bb3725a133 100644 --- a/crates/ide-assists/src/handlers/merge_nested_if.rs +++ b/crates/ide-assists/src/handlers/merge_nested_if.rs @@ -23,7 +23,7 @@ use crate::{ // if x == 3 && y == 4 { 1 } // } // ``` -pub(crate) fn merge_nested_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn merge_nested_if(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let if_keyword = ctx.find_token_syntax_at_offset(T![if])?; let expr = ast::IfExpr::cast(if_keyword.parent()?)?; let if_range = if_keyword.text_range(); diff --git a/crates/ide-assists/src/handlers/move_bounds.rs b/crates/ide-assists/src/handlers/move_bounds.rs index e044068ff7..3efc847141 100644 --- a/crates/ide-assists/src/handlers/move_bounds.rs +++ b/crates/ide-assists/src/handlers/move_bounds.rs @@ -24,7 +24,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn move_bounds_to_where_clause( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let type_param_list = ctx.find_node_at_offset::<ast::GenericParamList>()?; diff --git a/crates/ide-assists/src/handlers/move_const_to_impl.rs b/crates/ide-assists/src/handlers/move_const_to_impl.rs index 86bdf3f8b4..4071f16a9f 100644 --- a/crates/ide-assists/src/handlers/move_const_to_impl.rs +++ b/crates/ide-assists/src/handlers/move_const_to_impl.rs @@ -42,7 +42,7 @@ use crate::assist_context::{AssistContext, Assists}; // } // } // ``` -pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let db = ctx.db(); let const_: ast::Const = ctx.find_node_at_offset()?; // Don't show the assist when the cursor is at the const's body. diff --git a/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/crates/ide-assists/src/handlers/move_from_mod_rs.rs index a36d3136a1..21a8c53e8c 100644 --- a/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -20,7 +20,7 @@ use crate::{ // ``` // fn t() {} // ``` -pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let source_file = ctx.find_node_at_offset::<ast::SourceFile>()?; let module = ctx.sema.file_to_module_def(ctx.vfs_file_id())?; // Enable this assist if the user select all "meaningful" content in the source file diff --git a/crates/ide-assists/src/handlers/move_guard.rs b/crates/ide-assists/src/handlers/move_guard.rs index 7309cc6d06..7cd0d01238 100644 --- a/crates/ide-assists/src/handlers/move_guard.rs +++ b/crates/ide-assists/src/handlers/move_guard.rs @@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let match_arm = ctx.find_node_at_offset::<MatchArm>()?; let guard = match_arm.guard()?; if ctx.offset() > guard.syntax().text_range().end() { @@ -122,7 +122,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) // ``` pub(crate) fn move_arm_cond_to_match_guard( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; let match_pat = match_arm.pat()?; diff --git a/crates/ide-assists/src/handlers/move_module_to_file.rs b/crates/ide-assists/src/handlers/move_module_to_file.rs index 503003bc6b..3fec102275 100644 --- a/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -24,7 +24,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // mod foo; // ``` -pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let module_ast = ctx.find_node_at_offset::<ast::Module>()?; let module_items = module_ast.item_list()?; diff --git a/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/crates/ide-assists/src/handlers/move_to_mod_rs.rs index 5e95b264fc..7407bd1a0c 100644 --- a/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -20,7 +20,7 @@ use crate::{ // ``` // fn t() {} // ``` -pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let source_file = ctx.find_node_at_offset::<ast::SourceFile>()?; let module = ctx.sema.file_to_module_def(ctx.vfs_file_id())?; // Enable this assist if the user select all "meaningful" content in the source file diff --git a/crates/ide-assists/src/handlers/normalize_import.rs b/crates/ide-assists/src/handlers/normalize_import.rs index 36da1d1788..f97a3e583f 100644 --- a/crates/ide-assists/src/handlers/normalize_import.rs +++ b/crates/ide-assists/src/handlers/normalize_import.rs @@ -17,7 +17,7 @@ use crate::{ // ``` // use std::{fmt::Formatter, io}; // ``` -pub(crate) fn normalize_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn normalize_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let use_item = if ctx.has_empty_selection() { ctx.find_node_at_offset()? } else { diff --git a/crates/ide-assists/src/handlers/number_representation.rs b/crates/ide-assists/src/handlers/number_representation.rs index fac81aefe0..a50f443d5d 100644 --- a/crates/ide-assists/src/handlers/number_representation.rs +++ b/crates/ide-assists/src/handlers/number_representation.rs @@ -15,7 +15,10 @@ const MIN_NUMBER_OF_DIGITS_TO_FORMAT: usize = 5; // ``` // const _: i32 = 1_012_345; // ``` -pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn reformat_number_literal( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let literal = ctx.find_node_at_offset::<ast::Literal>()?; let literal = match literal.kind() { ast::LiteralKind::IntNumber(it) => it, diff --git a/crates/ide-assists/src/handlers/promote_local_to_const.rs b/crates/ide-assists/src/handlers/promote_local_to_const.rs index ed61d32eb6..23c88e65d6 100644 --- a/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -39,7 +39,7 @@ use crate::{ // } // } // ``` -pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let pat = ctx.find_node_at_offset::<ast::IdentPat>()?; let name = pat.name()?; if !pat.is_simple_ident() { diff --git a/crates/ide-assists/src/handlers/pull_assignment_up.rs b/crates/ide-assists/src/handlers/pull_assignment_up.rs index 082052c9d4..4024d51ffc 100644 --- a/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -33,7 +33,7 @@ use crate::{ // }; // } // ``` -pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?; let op_kind = assign_expr.op_kind()?; @@ -115,13 +115,13 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> ) } -struct AssignmentsCollector<'a> { - sema: &'a hir::Semantics<'a, ide_db::RootDatabase>, +struct AssignmentsCollector<'a, 'db> { + sema: &'a hir::Semantics<'db, ide_db::RootDatabase>, common_lhs: ast::Expr, assignments: Vec<(ast::BinExpr, ast::Expr)>, } -impl AssignmentsCollector<'_> { +impl AssignmentsCollector<'_, '_> { fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> { for arm in match_expr.match_arm_list()?.arms() { match arm.expr()? { diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs index d7885d5065..edf0a855c4 100644 --- a/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -32,7 +32,7 @@ use crate::{ // Foo::foo(&foo); // } // ``` -pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let name: ast::NameRef = ctx.find_node_at_offset()?; let call = name.syntax().parent().and_then(ast::MethodCallExpr::cast)?; diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs index e3dd77360c..cb48554083 100644 --- a/crates/ide-assists/src/handlers/qualify_path.rs +++ b/crates/ide-assists/src/handlers/qualify_path.rs @@ -4,7 +4,7 @@ use std::iter; use hir::AsAssocItem; use ide_db::RootDatabase; use ide_db::{ - helpers::mod_path_to_ast, + helpers::mod_path_to_ast_with_factory, imports::import_assets::{ImportCandidate, LocatedImport}, }; use syntax::Edition; @@ -34,7 +34,7 @@ use crate::{ // } // # pub mod std { pub mod collections { pub struct HashMap { } } } // ``` -pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (import_assets, syntax_under_caret, expected) = find_importable_node(ctx)?; let cfg = ctx.config.import_path_config(); @@ -129,7 +129,7 @@ impl QualifyCandidate<'_> { item: hir::ItemInNs, edition: Edition, ) { - let import = mod_path_to_ast(import, edition); + let import = mod_path_to_ast_with_factory(editor.make(), import, edition); match self { QualifyCandidate::QualifierStart(segment, generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); diff --git a/crates/ide-assists/src/handlers/raw_string.rs b/crates/ide-assists/src/handlers/raw_string.rs index 8234a0374e..0a74515739 100644 --- a/crates/ide-assists/src/handlers/raw_string.rs +++ b/crates/ide-assists/src/handlers/raw_string.rs @@ -24,7 +24,7 @@ use crate::{ // r#"Hello, World!"#; // } // ``` -pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let token = ctx.find_token_at_offset::<ast::AnyString>()?; if token.is_raw() { return None; @@ -60,7 +60,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt // "Hello, \"World!\""; // } // ``` -pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let token = ctx.find_token_at_offset::<ast::AnyString>()?; if !token.is_raw() { return None; @@ -97,7 +97,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O // r##"Hello, World!"##; // } // ``` -pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let token = ctx.find_token_at_offset::<ast::AnyString>()?; if !token.is_raw() { return None; @@ -128,7 +128,7 @@ pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> // r"Hello, World!"; // } // ``` -pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let token = ctx.find_token_at_offset::<ast::AnyString>()?; if !token.is_raw() { return None; @@ -160,7 +160,7 @@ fn replace_literal( token: &impl AstToken, new: &str, builder: &mut SourceChangeBuilder, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) { let old_token = token.syntax(); let parent = old_token.parent().expect("no parent token"); diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index 778533be5a..91977c2b7f 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs @@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists}; // let x = 42 * (4 + 2); // } // ``` -pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); let make = editor.make(); let macro_calls = if ctx.has_empty_selection() { diff --git a/crates/ide-assists/src/handlers/remove_else_branches.rs b/crates/ide-assists/src/handlers/remove_else_branches.rs index 0c03856417..a40d691e4c 100644 --- a/crates/ide-assists/src/handlers/remove_else_branches.rs +++ b/crates/ide-assists/src/handlers/remove_else_branches.rs @@ -35,7 +35,7 @@ use crate::{AssistContext, AssistId, Assists}; // let _x = 2; // } // ``` -pub(crate) fn remove_else_branches(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_else_branches(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let else_token = ctx.find_token_syntax_at_offset(T![else])?; let else_branches = ctx .find_node_at_range::<ast::IfExpr>() diff --git a/crates/ide-assists/src/handlers/remove_mut.rs b/crates/ide-assists/src/handlers/remove_mut.rs index 2a6024339f..db379809c5 100644 --- a/crates/ide-assists/src/handlers/remove_mut.rs +++ b/crates/ide-assists/src/handlers/remove_mut.rs @@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists}; // fn feed(&self, amount: u32) {} // } // ``` -pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let mut_token = ctx.find_token_syntax_at_offset(T![mut])?; let target = mut_token.text_range(); diff --git a/crates/ide-assists/src/handlers/remove_parentheses.rs b/crates/ide-assists/src/handlers/remove_parentheses.rs index af249c97b9..d6606d181e 100644 --- a/crates/ide-assists/src/handlers/remove_parentheses.rs +++ b/crates/ide-assists/src/handlers/remove_parentheses.rs @@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists}; // _ = 2 + 2; // } // ``` -pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let parens = ctx.find_node_at_offset::<ast::ParenExpr>()?; let cursor_in_range = diff --git a/crates/ide-assists/src/handlers/remove_underscore.rs b/crates/ide-assists/src/handlers/remove_underscore.rs index 1de1c15cf7..efe5c945be 100644 --- a/crates/ide-assists/src/handlers/remove_underscore.rs +++ b/crates/ide-assists/src/handlers/remove_underscore.rs @@ -24,7 +24,7 @@ use crate::{AssistContext, Assists}; // foo = 2; // } // ``` -pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (text, text_range, def) = if let Some(name_ref) = ctx.find_node_at_offset::<ast::Name>() { let text = name_ref.text(); if !text.starts_with('_') { diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs index c38bdfdccf..2958acc478 100644 --- a/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists}; // mod foo { // } // ``` -pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { // First, grab the uses that intersect with the current selection. let selected_el = match ctx.covering_element() { syntax::NodeOrToken::Node(n) => n, @@ -111,19 +111,24 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) is_path_per_ns_unused_in_scope(ctx, &u, scope, &res).then_some(u) } }) - .peekable(); + .collect::<Vec<_>>(); - // Peek so we terminate early if an unused use is found. Only do the rest of the work if the user selects the assist. - if unused.peek().is_some() { + // Terminate early unless an unused use is found. Only do the rest of the work if the user selects the assist. + if !unused.is_empty() { acc.add( AssistId::quick_fix("remove_unused_imports"), "Remove all unused imports", selected_el.text_range(), |builder| { - let unused: Vec<ast::UseTree> = unused.map(|x| builder.make_mut(x)).collect(); - for node in unused { - node.remove_recursive(); + let editor = builder.make_editor(&selected_el); + unused.sort_by_key(|use_tree| use_tree.syntax().text_range().start()); + for node in &unused { + editor.delete(node.syntax()); } + for node in unused.iter().cloned() { + node.remove_recursive(&editor); + } + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } else { @@ -132,7 +137,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) } fn is_path_per_ns_unused_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, u: &ast::UseTree, scope: &mut Vec<SearchScope>, path: &PathResolutionPerNs, @@ -151,7 +156,7 @@ fn is_path_per_ns_unused_in_scope( } fn is_path_unused_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, u: &ast::UseTree, scope: &mut Vec<SearchScope>, path: &[Option<PathResolution>], @@ -167,7 +172,7 @@ fn is_path_unused_in_scope( } fn is_trait_unused_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, u: &ast::UseTree, scope: &mut Vec<SearchScope>, t: &hir::Trait, @@ -178,7 +183,7 @@ fn is_trait_unused_in_scope( } fn used_once_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, def: Definition, rename: Option<Rename>, scopes: &Vec<SearchScope>, diff --git a/crates/ide-assists/src/handlers/remove_unused_param.rs b/crates/ide-assists/src/handlers/remove_unused_param.rs index b91d678c93..d7b0b5ea01 100644 --- a/crates/ide-assists/src/handlers/remove_unused_param.rs +++ b/crates/ide-assists/src/handlers/remove_unused_param.rs @@ -31,7 +31,7 @@ use crate::{ // frobnicate(); // } // ``` -pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let param: ast::Param = ctx.find_node_at_offset()?; let ident_pat = match param.pat()? { ast::Pat::IdentPat(it) => it, @@ -94,7 +94,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> } fn process_usages( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, editioned_file_id: EditionedFileId, references: Vec<FileReference>, diff --git a/crates/ide-assists/src/handlers/reorder_fields.rs b/crates/ide-assists/src/handlers/reorder_fields.rs index facbab8019..9c9224b997 100644 --- a/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/crates/ide-assists/src/handlers/reorder_fields.rs @@ -19,7 +19,7 @@ use crate::{AssistContext, AssistId, Assists}; // struct Foo {foo: i32, bar: i32}; // const test: Foo = Foo {foo: 1, bar: 0} // ``` -pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let path = ctx.find_node_at_offset::<ast::Path>()?; let record = path.syntax().parent().and_then(<Either<ast::RecordExpr, ast::RecordPat>>::cast)?; @@ -97,7 +97,7 @@ fn replace<T: AstNode + PartialEq>( fn compute_fields_ranks( path: &ast::Path, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<FxHashMap<String, usize>> { let strukt = match ctx.sema.resolve_path(path) { Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(it)))) => it, diff --git a/crates/ide-assists/src/handlers/reorder_impl_items.rs b/crates/ide-assists/src/handlers/reorder_impl_items.rs index df5281895a..658947abe1 100644 --- a/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -42,7 +42,7 @@ use crate::{AssistContext, AssistId, Assists}; // fn c() {} // } // ``` -pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?; let items = impl_ast.assoc_item_list()?; @@ -113,7 +113,7 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> fn compute_item_ranks( path: &ast::Path, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<FxHashMap<String, usize>> { let td = trait_definition(path, &ctx.sema)?; diff --git a/crates/ide-assists/src/handlers/replace_arith_op.rs b/crates/ide-assists/src/handlers/replace_arith_op.rs index 5ad5efac05..9fadf1333f 100644 --- a/crates/ide-assists/src/handlers/replace_arith_op.rs +++ b/crates/ide-assists/src/handlers/replace_arith_op.rs @@ -4,7 +4,10 @@ use syntax::{ ast::{self, ArithOp, BinaryOp}, }; -use crate::assist_context::{AssistContext, Assists}; +use crate::{ + assist_context::{AssistContext, Assists}, + utils::wrap_paren, +}; // Assist: replace_arith_with_checked // @@ -21,7 +24,10 @@ use crate::assist_context::{AssistContext, Assists}; // let x = 1.checked_add(2); // } // ``` -pub(crate) fn replace_arith_with_checked(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_arith_with_checked( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { replace_arith(acc, ctx, ArithKind::Checked) } @@ -42,7 +48,7 @@ pub(crate) fn replace_arith_with_checked(acc: &mut Assists, ctx: &AssistContext< // ``` pub(crate) fn replace_arith_with_saturating( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { replace_arith(acc, ctx, ArithKind::Saturating) } @@ -64,13 +70,13 @@ pub(crate) fn replace_arith_with_saturating( // ``` pub(crate) fn replace_arith_with_wrapping( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { replace_arith(acc, ctx, ArithKind::Wrapping) } -fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> Option<()> { - let (lhs, op, rhs) = parse_binary_op(ctx)?; +fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_, '_>, kind: ArithKind) -> Option<()> { + let (lhs, op, is_assign, rhs) = parse_binary_op(ctx)?; let op_expr = lhs.syntax().parent()?; if !is_primitive_int(ctx, &lhs) || !is_primitive_int(ctx, &rhs) { @@ -87,18 +93,20 @@ fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> let make = editor.make(); let method_name = kind.method_name(op); - let needs_parentheses = - lhs.precedence().needs_parentheses_in(ast::prec::ExprPrecedence::Postfix); - let receiver = if needs_parentheses { make.expr_paren(lhs).into() } else { lhs }; - let arith_expr = - make.expr_method_call(receiver, make.name_ref(&method_name), make.arg_list([rhs])); + let receiver = wrap_paren(lhs.clone(), make, ast::prec::ExprPrecedence::Postfix); + let mut arith_expr = make + .expr_method_call(receiver, make.name_ref(&method_name), make.arg_list([rhs])) + .into(); + if is_assign { + arith_expr = make.expr_assignment(lhs, arith_expr).into(); + } editor.replace(op_expr, arith_expr.syntax()); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } -fn is_primitive_int(ctx: &AssistContext<'_>, expr: &ast::Expr) -> bool { +fn is_primitive_int(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> bool { match ctx.sema.type_of_expr(expr) { Some(ty) => ty.adjusted().is_int_or_uint(), _ => false, @@ -106,24 +114,25 @@ fn is_primitive_int(ctx: &AssistContext<'_>, expr: &ast::Expr) -> bool { } /// Extract the operands of an arithmetic expression (e.g. `1 + 2` or `1.checked_add(2)`) -fn parse_binary_op(ctx: &AssistContext<'_>) -> Option<(ast::Expr, ArithOp, ast::Expr)> { +fn parse_binary_op(ctx: &AssistContext<'_, '_>) -> Option<(ast::Expr, ArithOp, bool, ast::Expr)> { if !ctx.has_empty_selection() { return None; } let expr = ctx.find_node_at_offset::<ast::BinExpr>()?; - let op = match expr.op_kind() { - Some(BinaryOp::ArithOp(ArithOp::Add)) => ArithOp::Add, - Some(BinaryOp::ArithOp(ArithOp::Sub)) => ArithOp::Sub, - Some(BinaryOp::ArithOp(ArithOp::Mul)) => ArithOp::Mul, - Some(BinaryOp::ArithOp(ArithOp::Div)) => ArithOp::Div, + let (op, is_assign) = match expr.op_kind()? { + BinaryOp::ArithOp(arith_op) => (arith_op, false), + BinaryOp::Assignment { op: Some(op) } => (op, true), _ => return None, }; + if !matches!(op, ArithOp::Add | ArithOp::Sub | ArithOp::Mul | ArithOp::Div) { + return None; + } let lhs = expr.lhs()?; let rhs = expr.rhs()?; - Some((lhs, op, rhs)) + Some((lhs, op, is_assign, rhs)) } pub(crate) enum ArithKind { @@ -250,6 +259,25 @@ fn main() { } #[test] + fn replace_arith_with_wrapping_add_assign() { + check_assist( + replace_arith_with_wrapping, + r#" +fn main() { + let mut x = 1; + x $0+= 2; +} +"#, + r#" +fn main() { + let mut x = 1; + x = x.wrapping_add(2); +} +"#, + ) + } + + #[test] fn replace_arith_not_applicable_with_non_empty_selection() { check_assist_not_applicable( replace_arith_with_checked, diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 751cd42f6e..4e85b30b58 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1,10 +1,13 @@ use hir::{InFile, ModuleDef}; -use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator}; +use ide_db::{ + helpers::mod_path_to_ast_with_factory, imports::import_assets::NameToImport, items_locator, +}; use itertools::Itertools; use syntax::{ + Edition, SyntaxKind::WHITESPACE, T, - ast::{self, AstNode, HasName}, + ast::{self, AstNode, HasName, syntax_factory::SyntaxFactory}, syntax_editor::{Position, SyntaxEditor}, }; @@ -41,7 +44,7 @@ use crate::{ // ``` pub(crate) fn replace_derive_with_manual_impl( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let attr = ctx.find_node_at_offset_with_descend::<ast::Attr>()?; let path = attr.path()?; @@ -87,13 +90,12 @@ pub(crate) fn replace_derive_with_manual_impl( .flat_map(|trait_| { current_module .find_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), cfg) - .as_ref() - .map(|path| mod_path_to_ast(path, current_edition)) - .zip(Some(trait_)) + .map(|path| (path, trait_)) }); - let mut no_traits_found = true; - for (replace_trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { + let found_traits = found_traits.collect::<Vec<_>>(); + let no_traits_found = found_traits.is_empty(); + for (replace_trait_mod_path, trait_) in found_traits { add_assist( acc, ctx, @@ -101,35 +103,58 @@ pub(crate) fn replace_derive_with_manual_impl( ¤t_derives, &args, &path, - &replace_trait_path, + Some(replace_trait_mod_path), Some(trait_), &adt, + current_edition, )?; } if no_traits_found { - add_assist(acc, ctx, &attr, ¤t_derives, &args, &path, &path, None, &adt)?; + add_assist( + acc, + ctx, + &attr, + ¤t_derives, + &args, + &path, + None, + None, + &adt, + current_edition, + )?; } Some(()) } fn add_assist( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, attr: &ast::Attr, old_derives: &[ast::Path], old_tree: &ast::TokenTree, old_trait_path: &ast::Path, - replace_trait_path: &ast::Path, + replace_trait_mod_path: Option<hir::ModPath>, trait_: Option<hir::Trait>, adt: &ast::Adt, + current_edition: Edition, ) -> Option<()> { let target = attr.syntax().text_range(); let annotated_name = adt.name()?; - let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`"); + let label_trait_path = match replace_trait_mod_path.as_ref() { + Some(path) => { + mod_path_to_ast_with_factory(&SyntaxFactory::without_mappings(), path, current_edition) + } + None => old_trait_path.clone(), + }; + let label = format!("Convert to manual `impl {label_trait_path} for {annotated_name}`"); acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| { let editor = builder.make_editor(attr.syntax()); let make = editor.make(); + let replace_trait_path = match replace_trait_mod_path.as_ref() { + Some(path) => mod_path_to_ast_with_factory(make, path, current_edition), + None => old_trait_path.clone(), + }; let insert_after = Position::after(adt.syntax()); let impl_is_unsafe = trait_.map(|s| s.is_unsafe(ctx.db())).unwrap_or(false); let impl_def = impl_def_from_trait( @@ -139,7 +164,7 @@ fn add_assist( adt, &annotated_name, trait_, - replace_trait_path, + &replace_trait_path, impl_is_unsafe, ); update_attribute(&editor, old_derives, old_tree, old_trait_path, attr); diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 0badad7d0c..aa3f917f12 100644 --- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -1,3 +1,5 @@ +use hir::db::ExpandDatabase; +use itertools::Itertools; use std::iter::successors; use ide_db::{RootDatabase, defs::NameClass, ty_filter::TryEnum}; @@ -45,7 +47,10 @@ use crate::{ // } // } // ``` -pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_if_let_with_match( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; let available_range = TextRange::new( if_expr.syntax().text_range().start(), @@ -66,8 +71,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' } }); let scrutinee_to_be_expr = if_expr.condition()?; - let scrutinee_to_be_expr = match let_and_guard(&scrutinee_to_be_expr) { - (Some(let_expr), _) => let_expr.expr()?, + let scrutinee_to_be_expr = match let_and_guard(&scrutinee_to_be_expr, ctx)? { + (Some((_, expr)), _) => expr, (None, cond) => cond?, }; @@ -75,11 +80,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' let mut cond_bodies = Vec::new(); for if_expr in if_exprs { let cond = if_expr.condition()?; - let (cond, guard) = match let_and_guard(&cond) { + let (cond, guard) = match let_and_guard(&cond, ctx)? { (None, guard) => (None, Some(guard?)), - (Some(let_), guard) => { - let pat = let_.pat()?; - let expr = let_.expr()?; + (Some((pat, expr)), guard) => { if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() { // Only if all condition expressions are equal we can merge them into a match return None; @@ -120,6 +123,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' // Dedent from original position, then indent for match arm let body = body.dedent(indent); let body = unwrap_trivial_block(body); + let pat = pretty_pat_inside_macro(pat, &ctx.sema); match (pat, guard.map(|it| make.match_guard(it))) { (Some(pat), guard) => make.match_arm(pat, guard, body), (None, _) if !pat_seen => { @@ -159,7 +163,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<' } fn make_else_arm( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, else_expr: Option<ast::Expr>, conditionals: &[(Option<ast::Pat>, Option<ast::Expr>, ast::BlockExpr)], @@ -222,7 +226,10 @@ fn make_else_arm( // } // } // ``` -pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_match_with_if_let( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?; let match_arm_list = match_expr.match_arm_list()?; let available_range = TextRange::new( @@ -394,13 +401,18 @@ fn is_sad_pat(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool { .is_some_and(|it| does_pat_match_variant(pat, &it.sad_pattern())) } -fn let_and_guard(cond: &ast::Expr) -> (Option<ast::LetExpr>, Option<ast::Expr>) { +fn let_and_guard( + cond: &ast::Expr, + ctx: &AssistContext<'_, '_>, +) -> Option<(Option<(ast::Pat, ast::Expr)>, Option<ast::Expr>)> { if let ast::Expr::ParenExpr(expr) = cond && let Some(sub_expr) = expr.expr() { - let_and_guard(&sub_expr) + let_and_guard(&sub_expr, ctx)? } else if let ast::Expr::LetExpr(let_expr) = cond { - (Some(let_expr.clone()), None) + (Some((let_expr.pat()?, let_expr.expr()?)), None) + } else if let Some((pat, expr, guard)) = parse_matches_macro(cond, ctx) { + (Some((pat, expr)), guard) } else if let ast::Expr::BinExpr(bin_expr) = cond && let Some(ast::Expr::LetExpr(let_expr)) = and_bin_expr_left(bin_expr).lhs() { @@ -418,10 +430,11 @@ fn let_and_guard(cond: &ast::Expr) -> (Option<ast::LetExpr>, Option<ast::Expr>) } let new_expr = editor.finish().new_root().clone(); - (Some(let_expr), ast::Expr::cast(new_expr)) + (Some((let_expr.pat()?, let_expr.expr()?)), ast::Expr::cast(new_expr)) } else { (None, Some(cond.clone())) } + .into() } fn match_scrutinee_needs_paren(expr: &ast::Expr) -> bool { @@ -445,6 +458,66 @@ fn and_bin_expr_left(expr: &ast::BinExpr) -> ast::BinExpr { } } +fn parse_matches_macro( + expr: &ast::Expr, + ctx: &AssistContext<'_, '_>, +) -> Option<(ast::Pat, ast::Expr, Option<ast::Expr>)> { + let ast::Expr::MacroExpr(macro_expr) = expr else { return None }; + let macro_call = macro_expr.macro_call()?; + let tt = macro_call.token_tree()?; + let r_delim = syntax::NodeOrToken::Token(tt.right_delimiter_token()?); + + if macro_call.path()?.segment()?.name_ref()?.text() != "matches" { + return None; + } + + let parse = |src: String| syntax::hacks::parse_expr_from_str(&src, ctx.edition()); + let input = tt.syntax().children_with_tokens().skip(1).take_while(|it| *it != r_delim); + // Only supports single top-level comma case + let [comma] = input.clone().filter(|it| it.kind() == T![,]).collect_array()?; + let input_rest = input.clone().skip_while(|it| *it != comma).skip(1); + let if_kwd = input_rest.clone().find(|it| it.kind() == T![if]); + let input_expr = input.clone().take_while(|it| *it != comma).join(""); + let input_pat = input_rest.clone().take_while(|it| Some(it) != if_kwd.as_ref()); + let input_guard = + if_kwd.as_ref().map(|if_kwd| { input_rest }.skip_while(|it| it != if_kwd).skip(1).join("")); + + let pat_token = match { input_pat }.find(|it| !it.kind().is_trivia())? { + syntax::NodeOrToken::Node(node) => node.first_token()?, + syntax::NodeOrToken::Token(t) => t, + }; + // XXX: Use descend pat for sema analysis + let descend_pat = ctx.sema.descend_into_macros(pat_token).into_iter().find_map(|token| { + token + .parent_ancestors() + .take_while(|it| !matches!(it.kind(), SyntaxKind::MATCH_ARM | SyntaxKind::ITEM_LIST)) + .filter_map(ast::Pat::cast) + .last() + })?; + + Some((descend_pat, parse(input_expr)?, input_guard.and_then(parse))) +} + +fn pretty_pat_inside_macro( + pat: Option<ast::Pat>, + sema: &hir::Semantics<'_, RootDatabase>, +) -> Option<ast::Pat> { + let pretty = |pat| { + let db = sema.db; + let scope = sema.scope(&pat)?; + let file_id = scope.file_id().macro_file()?; + // Don't call `prettify_macro_expansion()` outside the actual assist action; see inline_macro assist + let pretty_node = hir::prettify_macro_expansion( + db, + pat, + db.expansion_span_map(file_id), + scope.module().krate(db).into(), + ); + ast::Pat::cast(pretty_node) + }; + pat.map(|pat| pretty(pat.syntax().clone()).unwrap_or(pat)) +} + #[cfg(test)] mod tests { use super::*; @@ -1816,6 +1889,53 @@ fn foo(x: Result<i32, ()>) { } #[test] + fn test_if_matches_with_match() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result, matches +fn foo(x: Result<i32, ()>) { + $0if matches!(x, Ok(a @ 1..2)) { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<i32, ()>) { + match x { + Ok([email protected]) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result, matches +fn foo(x: Result<i32, ()>) { + $0if matches!(x, Ok(ref a)) { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result<i32, ()>) { + match x { + Ok(ref a) => (), + Err(_) => (), + } +} +"#, + ); + } + + #[test] fn test_replace_match_with_if_let_unwraps_simple_expressions() { check_assist( replace_match_with_if_let, diff --git a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 802d5f72b9..f50614a66f 100644 --- a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -29,7 +29,7 @@ use crate::{ // ``` pub(crate) fn replace_is_method_with_if_let_method( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let has_cond = ctx.find_node_at_offset::<Either<ast::IfExpr, ast::WhileExpr>>()?; diff --git a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index 85e72130e0..29688d9b97 100644 --- a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -34,7 +34,10 @@ use crate::{AssistContext, AssistId, Assists}; // // fn compute() -> Option<i32> { None } // ``` -pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_let_with_if_let( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let let_kw = ctx.find_token_syntax_at_offset(T![let])?; let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?; let init = let_stmt.initializer()?; diff --git a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 6e4dd8cb73..7aa9a82109 100644 --- a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -25,7 +25,10 @@ use crate::{AssistContext, Assists, utils::wrap_paren_in_call}; // a.unwrap_or_else(|| 2); // } // ``` -pub(crate) fn replace_with_lazy_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_with_lazy_method( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; let scope = ctx.sema.scope(call.syntax())?; @@ -113,7 +116,10 @@ fn into_closure(param: &Expr, name_lazy: &str) -> Expr { // a.unwrap_or(2); // } // ``` -pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_with_eager_method( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let call: ast::MethodCallExpr = ctx.find_node_at_offset()?; let scope = ctx.sema.scope(call.syntax())?; diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 17ef7727ec..979f832978 100644 --- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -26,7 +26,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn replace_named_generic_with_impl( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { // finds `<P: AsRef<Path>>` let type_param = ctx.find_node_at_offset::<ast::TypeParam>()?; @@ -34,7 +34,7 @@ pub(crate) fn replace_named_generic_with_impl( let type_param_name = type_param.name()?; // The list of type bounds / traits: `AsRef<Path>` - let type_bound_list = type_param.type_bound_list()?; + let type_bound_list = type_param.type_bound_list(); let fn_ = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; let param_list_text_range = fn_.param_list()?.syntax().text_range(); @@ -89,6 +89,8 @@ pub(crate) fn replace_named_generic_with_impl( } } + let type_bound_list = type_bound_list + .unwrap_or_else(|| make.type_bound_list([make.type_bound_text("Sized")]).unwrap()); let new_bounds = make.impl_trait_type(type_bound_list); for path_type in path_types_to_replace.iter().rev() { editor.replace(path_type.syntax(), new_bounds.syntax()); @@ -313,6 +315,15 @@ mod tests { } #[test] + fn replace_generic_without_bounds() { + check_assist( + replace_named_generic_with_impl, + r#"fn foo<T$0>(input: T) {}"#, + r#"fn foo(input: impl Sized) {}"#, + ); + } + + #[test] fn replace_generic_with_multiple_trait_bounds() { check_assist( replace_named_generic_with_impl, diff --git a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index eebe93f005..0bd1ec12d0 100644 --- a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -1,11 +1,11 @@ use hir::{AsAssocItem, ModuleDef, PathResolution}; use ide_db::{ - helpers::mod_path_to_ast, - imports::insert_use::{ImportScope, insert_use}, + helpers::mod_path_to_ast_with_factory, + imports::insert_use::{ImportScope, insert_use_with_editor}, }; use syntax::{ AstNode, Edition, SyntaxNode, - ast::{self, HasGenericArgs, make}, + ast::{self, HasGenericArgs}, match_ast, syntax_editor::SyntaxEditor, }; @@ -29,7 +29,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn replace_qualified_name_with_use( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let original_path: ast::Path = ctx.find_node_at_offset()?; // We don't want to mess with use statements @@ -75,7 +75,7 @@ pub(crate) fn replace_qualified_name_with_use( let scope_node = scope.as_syntax_node(); let editor = builder.make_editor(scope_node); shorten_paths(&editor, scope_node, &original_path); - builder.add_file_edits(ctx.vfs_file_id(), editor); + let make = editor.make(); let path = drop_generic_args(&original_path); let edition = ctx .sema @@ -83,18 +83,19 @@ pub(crate) fn replace_qualified_name_with_use( .map(|semantics_scope| semantics_scope.krate().edition(ctx.db())) .unwrap_or(Edition::CURRENT); // stick the found import in front of the to be replaced path - let path = - match path_to_qualifier.and_then(|it| mod_path_to_ast(&it, edition).qualifier()) { - Some(qualifier) => make::path_concat(qualifier, path), - None => path, - }; - let scope = builder.make_import_scope_mut(scope); - insert_use(&scope, path, &ctx.config.insert_use); + let path = match path_to_qualifier + .and_then(|it| mod_path_to_ast_with_factory(make, &it, edition).qualifier()) + { + Some(qualifier) => make.path_concat(qualifier, path), + None => path, + }; + insert_use_with_editor(&scope, path, &ctx.config.insert_use, &editor); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } -fn target_path(ctx: &AssistContext<'_>, mut original_path: ast::Path) -> Option<ast::Path> { +fn target_path(ctx: &AssistContext<'_, '_>, mut original_path: ast::Path) -> Option<ast::Path> { let on_first = original_path.qualifier().is_none(); if on_first { diff --git a/crates/ide-assists/src/handlers/replace_string_with_char.rs b/crates/ide-assists/src/handlers/replace_string_with_char.rs index fb5b234d55..df22c472a4 100644 --- a/crates/ide-assists/src/handlers/replace_string_with_char.rs +++ b/crates/ide-assists/src/handlers/replace_string_with_char.rs @@ -22,7 +22,10 @@ use crate::{AssistContext, AssistId, Assists, utils::string_suffix}; // find('{'); // } // ``` -pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_string_with_char( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let token = ctx.find_token_syntax_at_offset(STRING).and_then(ast::String::cast)?; let value = token.value().ok()?; let target = token.syntax().text_range(); @@ -64,7 +67,10 @@ pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_ // find("{"); // } // ``` -pub(crate) fn replace_char_with_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn replace_char_with_string( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let token = ctx.find_token_syntax_at_offset(CHAR)?; let target = token.text_range(); diff --git a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs index a692259410..5e336523b4 100644 --- a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs +++ b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs @@ -28,7 +28,7 @@ use crate::{ // ``` pub(crate) fn replace_turbofish_with_explicit_type( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let let_stmt = ctx.find_node_at_offset::<LetStmt>()?; diff --git a/crates/ide-assists/src/handlers/sort_items.rs b/crates/ide-assists/src/handlers/sort_items.rs index 911fa9d14b..49b3cfb908 100644 --- a/crates/ide-assists/src/handlers/sort_items.rs +++ b/crates/ide-assists/src/handlers/sort_items.rs @@ -81,7 +81,7 @@ use crate::{AssistContext, AssistId, Assists, utils::get_methods}; // Cat { name: String, weight: f64 }, // } // ``` -pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { if ctx.has_empty_selection() { cov_mark::hit!(not_applicable_if_no_selection); return None; @@ -150,7 +150,7 @@ fn add_sort_field_list_assist(acc: &mut Assists, field_list: Option<ast::FieldLi fn add_sort_methods_assist( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, item_list: ast::AssocItemList, ) -> Option<()> { let selection = ctx.selection_trimmed(); diff --git a/crates/ide-assists/src/handlers/split_import.rs b/crates/ide-assists/src/handlers/split_import.rs index 1729a0667c..ec5ed44b56 100644 --- a/crates/ide-assists/src/handlers/split_import.rs +++ b/crates/ide-assists/src/handlers/split_import.rs @@ -13,7 +13,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // use std::{collections::HashMap}; // ``` -pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let colon_colon = ctx.find_token_syntax_at_offset(T![::])?; let path = ast::Path::cast(colon_colon.parent()?)?.qualifier()?; @@ -30,9 +30,9 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let target = colon_colon.text_range(); acc.add(AssistId::refactor_rewrite("split_import"), "Split import", target, |edit| { - let use_tree = edit.make_mut(use_tree.clone()); - let path = edit.make_mut(path); - use_tree.split_prefix(&path); + let editor = edit.make_editor(use_tree.syntax()); + use_tree.split_prefix_with_editor(&editor, &path); + edit.add_file_edits(ctx.vfs_file_id(), editor); }) } diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs index 849dfa49dd..5642f10a6d 100644 --- a/crates/ide-assists/src/handlers/term_search.rs +++ b/crates/ide-assists/src/handlers/term_search.rs @@ -10,7 +10,7 @@ use syntax::{AstNode, ast}; use crate::assist_context::{AssistContext, Assists}; -pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?; let syntax = unexpanded.syntax(); let goal_range = syntax.text_range(); diff --git a/crates/ide-assists/src/handlers/toggle_async_sugar.rs b/crates/ide-assists/src/handlers/toggle_async_sugar.rs index fa6ccb9a5f..99be89ece1 100644 --- a/crates/ide-assists/src/handlers/toggle_async_sugar.rs +++ b/crates/ide-assists/src/handlers/toggle_async_sugar.rs @@ -27,7 +27,7 @@ use crate::{AssistContext, Assists}; // ``` pub(crate) fn sugar_impl_future_into_async( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let ret_type: ast::RetType = ctx.find_node_at_offset()?; let function = ret_type.syntax().parent().and_then(ast::Fn::cast)?; @@ -117,7 +117,7 @@ pub(crate) fn sugar_impl_future_into_async( // ``` pub(crate) fn desugar_async_into_impl_future( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option<()> { let async_token = ctx.find_token_syntax_at_offset(SyntaxKind::ASYNC_KW)?; let function = async_token.parent().and_then(ast::Fn::cast)?; diff --git a/crates/ide-assists/src/handlers/toggle_ignore.rs b/crates/ide-assists/src/handlers/toggle_ignore.rs index a088fb178d..35c304b387 100644 --- a/crates/ide-assists/src/handlers/toggle_ignore.rs +++ b/crates/ide-assists/src/handlers/toggle_ignore.rs @@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists, utils::test_related_attribute_syn} // assert_eq!(2 + 2, 5); // } // ``` -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 func = attr.syntax().parent().and_then(ast::Fn::cast)?; let attr = test_related_attribute_syn(&func)?; diff --git a/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index 4d375080f5..e1ac9ea135 100644 --- a/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -26,7 +26,7 @@ use crate::{AssistContext, Assists}; // // sth!{ } // ``` -pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { #[derive(Debug)] enum MacroDelims { LPar, @@ -45,7 +45,8 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) let ltoken = token_tree.left_delimiter_token()?; let rtoken = token_tree.right_delimiter_token()?; - if !is_macro_call(&token_tree)? { + if is_macro_call(&token_tree) != Some(true) { + cov_mark::hit!(toggle_macro_delimiter_is_not_macro_call); return None; } @@ -111,7 +112,6 @@ fn is_macro_call(token_tree: &ast::TokenTree) -> Option<bool> { return Some(true); } - let token_tree = ast::TokenTree::cast(parent)?; let prev = previous_non_trivia_token(token_tree.syntax().clone())?; let prev_prev = previous_non_trivia_token(prev.clone())?; Some(prev.kind() == T![!] && prev_prev.kind() == SyntaxKind::IDENT) @@ -374,6 +374,7 @@ mod abc { #[test] fn test_unrelated_par() { + cov_mark::check!(toggle_macro_delimiter_is_not_macro_call); check_assist_not_applicable( toggle_macro_delimiter, r#" @@ -383,8 +384,7 @@ macro_rules! prt { }}; } -prt!(($03 + 5)); - +prt!((3 + 5$0)); "#, ) } diff --git a/crates/ide-assists/src/handlers/unmerge_imports.rs b/crates/ide-assists/src/handlers/unmerge_imports.rs index ab6317ad44..ec5c0929b4 100644 --- a/crates/ide-assists/src/handlers/unmerge_imports.rs +++ b/crates/ide-assists/src/handlers/unmerge_imports.rs @@ -21,7 +21,7 @@ use crate::{ // use std::fmt::{Debug}; // use std::fmt::Display; // ``` -pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); let make = editor.make(); let tree = ctx.find_node_at_offset::<ast::UseTree>()?; diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs index 65300ccefd..555d3d2ee9 100644 --- a/crates/ide-assists/src/handlers/unmerge_match_arm.rs +++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs @@ -30,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // } // ``` -pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let pipe_token = ctx.find_token_syntax_at_offset(T![|])?; let or_pat = ast::OrPat::cast(pipe_token.parent()?)?; if or_pat.leading_pipe().is_some_and(|it| it == pipe_token) { diff --git a/crates/ide-assists/src/handlers/unnecessary_async.rs b/crates/ide-assists/src/handlers/unnecessary_async.rs index b9385775b4..5542180362 100644 --- a/crates/ide-assists/src/handlers/unnecessary_async.rs +++ b/crates/ide-assists/src/handlers/unnecessary_async.rs @@ -28,7 +28,7 @@ use crate::{AssistContext, Assists}; // pub fn foo() {} // pub async fn bar() { foo() } // ``` -pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let function: ast::Fn = ctx.find_node_at_offset()?; // Do nothing if the cursor isn't on the async token. @@ -91,7 +91,7 @@ pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> O } fn find_all_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, def: &Definition, ) -> impl Iterator<Item = (EditionedFileId, FileReference)> { def.usages(&ctx.sema).all().into_iter().flat_map(|(file_id, references)| { @@ -101,7 +101,7 @@ fn find_all_references( /// Finds the await expression for the given `NameRef`. /// If no await expression is found, returns None. -fn find_await_expression(ctx: &AssistContext<'_>, nameref: &NameRef) -> Option<ast::AwaitExpr> { +fn find_await_expression(ctx: &AssistContext<'_, '_>, nameref: &NameRef) -> Option<ast::AwaitExpr> { // From the nameref, walk up the tree to the await expression. let await_expr = if let Some(path) = full_path_of_name_ref(nameref) { // Function calls. diff --git a/crates/ide-assists/src/handlers/unqualify_method_call.rs b/crates/ide-assists/src/handlers/unqualify_method_call.rs index 045a272952..242ebc4063 100644 --- a/crates/ide-assists/src/handlers/unqualify_method_call.rs +++ b/crates/ide-assists/src/handlers/unqualify_method_call.rs @@ -22,7 +22,7 @@ use crate::{AssistContext, AssistId, Assists}; // } // # mod std { pub mod ops { pub trait Add { fn add(self, _: Self) {} } impl Add for i32 {} } } // ``` -pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let call = ctx.find_node_at_offset::<ast::CallExpr>()?; let ast::Expr::PathExpr(path_expr) = call.expr()? else { return None }; let path = path_expr.path()?; @@ -77,7 +77,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) fn add_import( qualifier: ast::Path, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, editor: &syntax::syntax_editor::SyntaxEditor, ) { if let Some(path_segment) = qualifier.segment() { diff --git a/crates/ide-assists/src/handlers/unwrap_block.rs b/crates/ide-assists/src/handlers/unwrap_branch.rs index 77941bcfb2..a582af4e2c 100644 --- a/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/crates/ide-assists/src/handlers/unwrap_branch.rs @@ -1,8 +1,10 @@ +use either::Either; use syntax::{ AstNode, SyntaxElement, SyntaxKind, SyntaxNode, T, ast::{ self, edit::{AstNodeEdit, IndentLevel}, + syntax_factory::SyntaxFactory, }, match_ast, syntax_editor::{Element, Position, SyntaxEditor}, @@ -10,7 +12,7 @@ use syntax::{ use crate::{AssistContext, AssistId, Assists}; -// Assist: unwrap_block +// Assist: unwrap_branch // // This assist removes if...else, for, while and loop control statements to just keep the body. // @@ -27,15 +29,16 @@ use crate::{AssistContext, AssistId, Assists}; // println!("foo"); // } // ``` -pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; - let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; - let target = block.syntax().text_range(); - let mut container = block.syntax().clone(); +pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { + let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone()); + let place = unwrap_branch_place(ctx)?; + let target = place.syntax().text_range(); + let block = wrap_block_raw(&place, editor.make()); + let mut container = place.syntax().clone(); let mut replacement = block.clone(); let mut prefer_container = None; - let from_indent = block.indent_level(); + let from_indent = place.indent_level(); let into_indent = loop { let parent = container.parent()?; container = match_ast! { @@ -67,21 +70,84 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option _ => return None, } }; + if ast::MatchArm::cast(container.parent()?).is_some() { + replacement = editor.make().tail_only_block_expr(replacement.into()); + prefer_container = Some(container.clone()); + break IndentLevel::from_node(&container); + } }; + let is_branch = + !block.is_standalone() || place.syntax().parent().and_then(ast::MatchArm::cast).is_some(); + let label = if is_branch { "Unwrap branch" } else { "Unwrap block" }; let replacement = replacement.stmt_list()?; - acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| { - let editor = builder.make_editor(block.syntax()); + acc.add(AssistId::refactor_rewrite("unwrap_branch"), label, target, |builder| { let replacement = replacement.dedent(from_indent).indent(into_indent); + let mut replacement = extract_statements(replacement); let container = prefer_container.unwrap_or(container); - editor.replace_with_many(&container, extract_statements(replacement)); + if ast::ExprStmt::can_cast(container.kind()) + && block.tail_expr().is_some_and(|it| !it.is_block_like()) + { + replacement.push(editor.make().token(T![;]).into()); + } + + editor.replace_with_many(&container, replacement); delete_else_before(container, &editor); builder.add_file_edits(ctx.vfs_file_id(), editor); }) } +// Assist: unwrap_block +// +// This assist removes braces and unwrap single expressions block. +// +// ``` +// fn foo() { +// match () { +// _ => {$0 +// bar() +// } +// } +// } +// ``` +// -> +// ``` +// fn foo() { +// match () { +// _ => bar(), +// } +// } +// ``` +pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { + let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; + let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; + let target = block.syntax().text_range(); + let tail_expr = block.tail_expr()?; + let stmt_list = block.stmt_list()?; + let container = Either::<ast::MatchArm, ast::ClosureExpr>::cast(block.syntax().parent()?)?; + + if stmt_list.statements().next().is_some() { + return None; + } + + acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| { + let editor = builder.make_editor(block.syntax()); + let replacement = stmt_list.dedent(tail_expr.indent_level()).indent(block.indent_level()); + let mut replacement = extract_statements(replacement); + + if container.left().is_some_and(|it| it.comma_token().is_none()) + && !tail_expr.is_block_like() + { + replacement.push(editor.make().token(T![,]).into()); + } + + editor.replace_with_many(block.syntax(), replacement); + builder.add_file_edits(ctx.vfs_file_id(), editor); + }) +} + fn delete_else_before(container: SyntaxNode, editor: &SyntaxEditor) { let make = editor.make(); let Some(else_token) = container @@ -121,6 +187,18 @@ fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExp try_wrap_assign().unwrap_or(replacement) } +fn unwrap_branch_place(ctx: &AssistContext<'_, '_>) -> Option<ast::Expr> { + if let Some(l_curly_token) = ctx.find_token_syntax_at_offset(T!['{']) { + let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?; + Some(block.into()) + } else if let Some(fat_arrow_token) = ctx.find_token_syntax_at_offset(T![=>]) { + let match_arm = fat_arrow_token.parent().and_then(ast::MatchArm::cast)?; + match_arm.expr() + } else { + None + } +} + fn extract_statements(stmt_list: ast::StmtList) -> Vec<SyntaxElement> { let mut elements = stmt_list .syntax() @@ -132,16 +210,27 @@ fn extract_statements(stmt_list: ast::StmtList) -> Vec<SyntaxElement> { elements } +fn wrap_block_raw(expr: &ast::Expr, make: &SyntaxFactory) -> ast::BlockExpr { + if let ast::Expr::BlockExpr(block) = expr { + block.clone() + } else { + make.tail_only_block_expr(expr.indent(1.into())) + } +} + #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; + use crate::tests::{ + check_assist, check_assist_by_label, check_assist_not_applicable, + check_assist_not_applicable_by_label, check_assist_with_label, + }; use super::*; #[test] fn unwrap_tail_expr_block() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { $0{ @@ -160,7 +249,7 @@ fn main() { #[test] fn unwrap_stmt_expr_block() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { $0{ @@ -176,9 +265,8 @@ fn main() { } "#, ); - // Pedantically, we should add an `;` here... check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { $0{ @@ -189,7 +277,7 @@ fn main() { "#, r#" fn main() { - 92 + 92; () } "#, @@ -199,7 +287,7 @@ fn main() { #[test] fn simple_if() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { bar(); @@ -228,7 +316,7 @@ fn main() { #[test] fn simple_if_else() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { bar(); @@ -260,7 +348,7 @@ fn main() { #[test] fn simple_if_else_if() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { // bar(); @@ -294,7 +382,7 @@ fn main() { #[test] fn simple_if_else_if_nested() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { // bar(); @@ -330,7 +418,7 @@ fn main() { #[test] fn simple_if_else_if_nested_else() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { // bar(); @@ -370,7 +458,7 @@ fn main() { #[test] fn simple_if_else_if_nested_middle() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { // bar(); @@ -408,7 +496,7 @@ fn main() { #[test] fn simple_if_bad_cursor_position() { check_assist_not_applicable( - unwrap_block, + unwrap_branch, r#" fn main() { bar();$0 @@ -428,7 +516,7 @@ fn main() { #[test] fn simple_for() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { for i in 0..5 {$0 @@ -461,7 +549,7 @@ fn main() { #[test] fn simple_if_in_for() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { for i in 0..5 { @@ -490,9 +578,91 @@ fn main() { } #[test] + fn simple_if_in_match_arm() { + check_assist( + unwrap_branch, + r#" +fn main() { + match 1 { + 1 => if true {$0 + foo(); + } + _ => (), + } +} +"#, + r#" +fn main() { + match 1 { + 1 => { + foo(); + } + _ => (), + } +} +"#, + ); + + check_assist( + unwrap_branch, + r#" +fn main() { + match 1 { + 1 => if true { + foo(); + } else {$0 + bar(); + } + _ => (), + } +} +"#, + r#" +fn main() { + match 1 { + 1 => { + bar(); + } + _ => (), + } +} +"#, + ); + } + + #[test] + fn simple_match_in_match_arm() { + check_assist( + unwrap_branch, + r#" +fn main() { + match 1 { + 1 => match () { + _ => {$0 + foo(); + } + } + _ => (), + } +} +"#, + r#" +fn main() { + match 1 { + 1 => { + foo(); + } + _ => (), + } +} +"#, + ); + } + + #[test] fn simple_loop() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { loop {$0 @@ -525,7 +695,7 @@ fn main() { #[test] fn simple_while() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { while true {$0 @@ -558,7 +728,7 @@ fn main() { #[test] fn simple_let_else() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { let Some(2) = None else {$0 @@ -573,7 +743,7 @@ fn main() { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { let Some(2) = None else {$0 @@ -592,7 +762,7 @@ fn main() { #[test] fn unwrap_match_arm() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { match rel_path { @@ -616,7 +786,7 @@ fn main() { #[test] fn unwrap_match_arm_in_let() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { let value = match rel_path { @@ -638,9 +808,33 @@ fn main() { } #[test] + fn unwrap_match_arm_without_block() { + check_assist( + unwrap_branch, + r#" +fn main() { + match rel_path { + Ok(rel_path) $0=> Foo { + rel_path, + }, + Err(_) => None, + } +} +"#, + r#" +fn main() { + Foo { + rel_path, + } +} +"#, + ); + } + + #[test] fn simple_if_in_while_bad_cursor_position() { check_assist_not_applicable( - unwrap_block, + unwrap_branch, r#" fn main() { while true { @@ -661,7 +855,7 @@ fn main() { #[test] fn simple_single_line() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { {$0 0 } @@ -678,7 +872,7 @@ fn main() { #[test] fn simple_nested_block() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { $0{ @@ -701,7 +895,7 @@ fn main() { #[test] fn nested_single_line() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { {$0 { println!("foo"); } } @@ -715,7 +909,7 @@ fn main() { ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { {$0 { 0 } } @@ -732,7 +926,7 @@ fn main() { #[test] fn simple_if_single_line() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { if true {$0 /* foo */ foo() } else { bar() /* bar */} @@ -749,7 +943,7 @@ fn main() { #[test] fn if_single_statement() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { if true {$0 @@ -768,7 +962,7 @@ fn main() { #[test] fn multiple_statements() { check_assist( - unwrap_block, + unwrap_branch, r#" fn main() -> i32 { if 2 > 1 {$0 @@ -792,7 +986,7 @@ fn main() -> i32 { fn unwrap_block_in_let_initializers() { // https://github.com/rust-lang/rust-analyzer/issues/13679 check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { let x = {$0 @@ -807,7 +1001,7 @@ fn main() { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() -> i32 { let _ = {$01; 2}; @@ -820,7 +1014,7 @@ fn main() -> i32 { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() -> i32 { let mut a = {$01; 2}; @@ -833,7 +1027,7 @@ fn main() -> i32 { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() -> i32 { let mut a = {$0 @@ -857,7 +1051,7 @@ fn main() -> i32 { fn unwrap_if_in_let_initializers() { // https://github.com/rust-lang/rust-analyzer/issues/13679 check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { let a = 1; @@ -881,7 +1075,7 @@ fn main() { fn unwrap_block_with_modifiers() { // https://github.com/rust-lang/rust-analyzer/issues/17964 check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { unsafe $0{ @@ -896,7 +1090,7 @@ fn main() { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { async move $0{ @@ -911,7 +1105,7 @@ fn main() { "#, ); check_assist( - unwrap_block, + unwrap_branch, r#" fn main() { try $0{ @@ -926,4 +1120,177 @@ fn main() { "#, ); } + + #[test] + fn unwrap_block_labels() { + check_assist_with_label( + unwrap_branch, + r#" +fn main() { + $0{ + bar; + } +} +"#, + "Unwrap block", + ); + check_assist_with_label( + unwrap_branch, + r#" +fn main() { + let x = $0{ + bar() + }; +} +"#, + "Unwrap block", + ); + check_assist_with_label( + unwrap_branch, + r#" +fn main() { + let x = if true $0{ + bar() + }; +} +"#, + "Unwrap branch", + ); + check_assist_with_label( + unwrap_branch, + r#" +fn main() { + let x = match () { + () => $0{ + bar(), + } + }; +} +"#, + "Unwrap branch", + ); + check_assist_with_label( + unwrap_branch, + r#" +fn main() { + match () { + () => $0{ + bar(), + } + } +} +"#, + "Unwrap branch", + ); + } + + #[test] + fn unwrap_block_in_branch() { + check_assist_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => {$0 + if true { + foo() + } + } + Err(_) => None, + } +} +"#, + r#" +fn main() { + match rel_path { + Ok(rel_path) => if true { + foo() + } + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + + check_assist_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => {$0 + 1 + 2 + } + Err(_) => None, + } +} +"#, + r#" +fn main() { + match rel_path { + Ok(rel_path) => 1 + 2, + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + } + + #[test] + fn unwrap_block_in_branch_non_standalone() { + check_assist_not_applicable_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => { + if true {$0 + foo() + } + } + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + } + + #[test] + fn unwrap_block_in_branch_non_tail_expr_only() { + check_assist_not_applicable_by_label( + unwrap_block, + r#" +fn main() { + match rel_path { + Ok(rel_path) => {$0 + x; + y + } + Err(_) => None, + } +} +"#, + "Unwrap block", + ); + } + + #[test] + fn unwrap_block_in_closure() { + check_assist_by_label( + unwrap_block, + r#" +fn main() { + let f = || {$0 foo() }; +} +"#, + r#" +fn main() { + let f = || foo(); +} +"#, + "Unwrap block", + ); + } } diff --git a/crates/ide-assists/src/handlers/unwrap_return_type.rs b/crates/ide-assists/src/handlers/unwrap_return_type.rs index 1fe9ea4eb8..608c68ea85 100644 --- a/crates/ide-assists/src/handlers/unwrap_return_type.rs +++ b/crates/ide-assists/src/handlers/unwrap_return_type.rs @@ -37,7 +37,7 @@ use crate::{AssistContext, AssistId, Assists}; // fn foo() -> i32 { 42i32 } // ``` -pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; let parent = ret_type.syntax().parent()?; let body_expr = match_ast! { diff --git a/crates/ide-assists/src/handlers/unwrap_tuple.rs b/crates/ide-assists/src/handlers/unwrap_tuple.rs index e03274bbb3..7d0205afe3 100644 --- a/crates/ide-assists/src/handlers/unwrap_tuple.rs +++ b/crates/ide-assists/src/handlers/unwrap_tuple.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; // let bar = "Bar"; // } // ``` -pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let let_kw = ctx.find_token_syntax_at_offset(T![let])?; let let_stmt = let_kw.parent().and_then(Either::<ast::LetStmt, ast::LetExpr>::cast)?; let mut indent_level = let_stmt.indent_level(); diff --git a/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs b/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs index 935ae18905..0c8e40bca1 100644 --- a/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs +++ b/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs @@ -21,7 +21,10 @@ use crate::{AssistContext, Assists}; // todo!() // } // ``` -pub(crate) fn unwrap_type_to_generic_arg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn unwrap_type_to_generic_arg( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, +) -> Option<()> { let path_type = ctx.find_node_at_offset::<ast::PathType>()?; let path = path_type.path()?; let segment = path.segment()?; diff --git a/crates/ide-assists/src/handlers/wrap_return_type.rs b/crates/ide-assists/src/handlers/wrap_return_type.rs index ddc0af31c3..f9c103aab8 100644 --- a/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -40,7 +40,7 @@ use crate::{AssistContext, AssistId, Assists}; // fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } // ``` -pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; let parent = ret_type.syntax().parent()?; let body_expr = match_ast! { @@ -212,7 +212,7 @@ impl WrapperKind { // Try to find an wrapper type alias in the current scope (shadowing the default). fn wrapper_alias<'db>( - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, make: &SyntaxFactory, core_wrapper: hir::Enum, ast_ret_type: &ast::Type, diff --git a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 635fab857d..94bd29049d 100644 --- a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -106,7 +106,7 @@ fn attempt_get_derive(attr: ast::Attr, ident: SyntaxToken) -> WrapUnwrapOption { attempt_attr().unwrap_or_else(|| WrapUnwrapOption::WrapAttr(vec![attr])) } } -pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { +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>(); @@ -161,7 +161,7 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) - fn wrap_derive( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, attr: ast::Attr, derive_element: TextRange, ) -> Option<()> { @@ -232,7 +232,11 @@ fn wrap_derive( Some(()) } -fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec<ast::Attr>) -> Option<()> { +fn wrap_cfg_attrs( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, + attrs: Vec<ast::Attr>, +) -> Option<()> { let (first_attr, last_attr) = (attrs.first()?, attrs.last()?); let range = first_attr.syntax().text_range().cover(last_attr.syntax().text_range()); let handle_source_change = |edit: &mut SourceChangeBuilder| { @@ -268,7 +272,7 @@ fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec<ast::At fn unwrap_cfg_attr( acc: &mut Assists, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, meta: ast::CfgAttrMeta, ) -> Option<()> { let top_attr = ast::Meta::from(meta.clone()).parent_attr()?; diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 80f05caf4e..eabcb8093e 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -102,7 +102,7 @@ pub fn assists( mod handlers { use crate::{AssistContext, Assists}; - pub(crate) type Handler = fn(&mut Assists, &AssistContext<'_>) -> Option<()>; + pub(crate) type Handler = fn(&mut Assists, &AssistContext<'_, '_>) -> Option<()>; mod add_braces; mod add_explicit_dot_deref; @@ -232,7 +232,7 @@ mod handlers { mod unmerge_match_arm; mod unnecessary_async; mod unqualify_method_call; - mod unwrap_block; + mod unwrap_branch; mod unwrap_return_type; mod unwrap_tuple; mod unwrap_type_to_generic_arg; @@ -380,7 +380,8 @@ mod handlers { unmerge_imports::unmerge_imports, unnecessary_async::unnecessary_async, unqualify_method_call::unqualify_method_call, - unwrap_block::unwrap_block, + unwrap_branch::unwrap_block, + unwrap_branch::unwrap_branch, unwrap_return_type::unwrap_return_type, unwrap_tuple::unwrap_tuple, unwrap_type_to_generic_arg::unwrap_type_to_generic_arg, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 048f3d7ce8..d3ee35aa86 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -3736,6 +3736,29 @@ fn doctest_unwrap_block() { "unwrap_block", r#####" fn foo() { + match () { + _ => {$0 + bar() + } + } +} +"#####, + r#####" +fn foo() { + match () { + _ => bar(), + } +} +"#####, + ) +} + +#[test] +fn doctest_unwrap_branch() { + check_doc_test( + "unwrap_branch", + r#####" +fn foo() { if true {$0 println!("foo"); } diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index bf1062d207..096f6678a5 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -21,8 +21,7 @@ use syntax::{ SyntaxNode, SyntaxToken, T, TextRange, TextSize, WalkEvent, ast::{ self, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, - edit::{AstNodeEdit, IndentLevel}, - edit_in_place::AttrsOwnerEdit, + edit::{AstNodeEdit, AttrsOwnerEdit, IndentLevel}, make, prec::ExprPrecedence, syntax_factory::SyntaxFactory, @@ -183,7 +182,11 @@ pub fn filter_assoc_items( (default_methods, def.body()), (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None) ), - _ => default_methods == DefaultMethods::No, + ast::AssocItem::TypeAlias(def) => matches!( + (default_methods, def.ty()), + (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None) + ), + ast::AssocItem::MacroCall(_) => unreachable!(), }) .collect(); @@ -222,7 +225,7 @@ pub fn add_trait_assoc_items_to_impl( let item_prettified = prettify_macro_expansion( sema.db, original_item.syntax().clone(), - &span_map, + span_map, target_scope.krate().into(), ); if let Some(formatted) = ast::AssocItem::cast(item_prettified) { @@ -242,8 +245,9 @@ pub fn add_trait_assoc_items_to_impl( PathTransform::trait_impl(target_scope, &source_scope, trait_, impl_.clone()); cloned_item = ast::AssocItem::cast(transform.apply(cloned_item.syntax())).unwrap(); } - cloned_item.remove_attrs_and_docs(); - cloned_item + let (editor, cloned_item) = SyntaxEditor::with_ast_node(&cloned_item); + cloned_item.remove_attrs_and_docs(&editor); + ast::AssocItem::cast(editor.finish().new_root().clone()).unwrap() }) .filter_map(|item| match item { ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { @@ -382,18 +386,21 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool { pat_head == var_head } -pub(crate) fn does_pat_variant_nested_or_literal(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool { +pub(crate) fn does_pat_variant_nested_or_literal( + ctx: &AssistContext<'_, '_>, + pat: &ast::Pat, +) -> bool { check_pat_variant_nested_or_literal_with_depth(ctx, pat, 0) } -fn check_pat_variant_from_enum(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool { +fn check_pat_variant_from_enum(ctx: &AssistContext<'_, '_>, pat: &ast::Pat) -> bool { ctx.sema.type_of_pat(pat).is_none_or(|ty: hir::TypeInfo<'_>| { ty.adjusted().as_adt().is_some_and(|adt| matches!(adt, hir::Adt::Enum(_))) }) } fn check_pat_variant_nested_or_literal_with_depth( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, pat: &ast::Pat, depth_after_refutable: usize, ) -> bool { @@ -409,6 +416,7 @@ fn check_pat_variant_nested_or_literal_with_depth( | ast::Pat::MacroPat(_) | ast::Pat::PathPat(_) | ast::Pat::BoxPat(_) + | ast::Pat::DerefPat(_) | ast::Pat::ConstBlockPat(_) => true, ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| { @@ -480,7 +488,7 @@ pub(crate) fn expr_fill_default(config: &AssistConfig) -> ast::Expr { /// - `Some(None)`: no impl exists. /// - `Some(Some(_))`: an impl exists, with no matching function names. pub(crate) fn find_struct_impl( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, adt: &ast::Adt, names: &[String], ) -> Option<Option<ast::Impl>> { @@ -536,15 +544,11 @@ pub(crate) fn generate_impl_with_item( adt: &ast::Adt, body: Option<ast::AssocItemList>, ) -> ast::Impl { - generate_impl_inner_with_factory(make, false, adt, None, true, body) + generate_impl_inner(make, false, adt, None, true, body) } -pub(crate) fn generate_impl_with_factory(make: &SyntaxFactory, adt: &ast::Adt) -> ast::Impl { - generate_impl_inner_with_factory(make, false, adt, None, true, None) -} - -pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl { - generate_impl_inner(false, adt, None, true, None) +pub(crate) fn generate_impl(make: &SyntaxFactory, adt: &ast::Adt) -> ast::Impl { + generate_impl_inner(make, false, adt, None, true, None) } /// Generates the corresponding `impl <trait> for Type {}` including type @@ -557,7 +561,7 @@ pub(crate) fn generate_trait_impl( adt: &ast::Adt, trait_: ast::Type, ) -> ast::Impl { - generate_impl_inner_with_factory(make, is_unsafe, adt, Some(trait_), true, None) + generate_impl_inner(make, is_unsafe, adt, Some(trait_), true, None) } /// Generates the corresponding `impl <trait> for Type {}` including type @@ -569,7 +573,7 @@ pub(crate) fn generate_trait_impl_intransitive( adt: &ast::Adt, trait_: ast::Type, ) -> ast::Impl { - generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, None) + generate_impl_inner(make, false, adt, Some(trait_), false, None) } pub(crate) fn generate_trait_impl_intransitive_with_item( @@ -578,7 +582,7 @@ pub(crate) fn generate_trait_impl_intransitive_with_item( trait_: ast::Type, body: ast::AssocItemList, ) -> ast::Impl { - generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, Some(body)) + generate_impl_inner(make, false, adt, Some(trait_), false, Some(body)) } pub(crate) fn generate_trait_impl_with_item( @@ -588,79 +592,10 @@ pub(crate) fn generate_trait_impl_with_item( trait_: ast::Type, body: ast::AssocItemList, ) -> ast::Impl { - generate_impl_inner_with_factory(make, is_unsafe, adt, Some(trait_), true, Some(body)) + generate_impl_inner(make, is_unsafe, adt, Some(trait_), true, Some(body)) } fn generate_impl_inner( - is_unsafe: bool, - adt: &ast::Adt, - trait_: Option<ast::Type>, - trait_is_transitive: bool, - body: Option<ast::AssocItemList>, -) -> ast::Impl { - // Ensure lifetime params are before type & const params - let generic_params = adt.generic_param_list().map(|generic_params| { - let lifetime_params = - generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam); - let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| { - let param = match param { - ast::TypeOrConstParam::Type(param) => { - // remove defaults since they can't be specified in impls - let mut bounds = - param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect()); - if let Some(trait_) = &trait_ { - // Add the current trait to `bounds` if the trait is transitive, - // meaning `impl<T> Trait for U<T>` requires `T: Trait`. - if trait_is_transitive { - bounds.push(make::type_bound(trait_.clone())); - } - }; - // `{ty_param}: {bounds}` - let param = make::type_param(param.name()?, make::type_bound_list(bounds)); - ast::GenericParam::TypeParam(param) - } - ast::TypeOrConstParam::Const(param) => { - // remove defaults since they can't be specified in impls - let param = make::const_param(param.name()?, param.ty()?); - ast::GenericParam::ConstParam(param) - } - }; - Some(param) - }); - - make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params)) - }); - let generic_args = - generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); - let adt_assoc_bounds = trait_ - .as_ref() - .zip(generic_params.as_ref()) - .and_then(|(trait_, params)| generic_param_associated_bounds(adt, trait_, params)); - - let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text())); - - let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); - match trait_ { - Some(trait_) => make::impl_trait( - cfg_attrs, - is_unsafe, - None, - None, - generic_params, - generic_args, - false, - trait_, - ty, - adt_assoc_bounds, - adt.where_clause(), - body, - ), - None => make::impl_(cfg_attrs, generic_params, generic_args, ty, adt.where_clause(), body), - } - .clone_for_update() -} - -fn generate_impl_inner_with_factory( make: &SyntaxFactory, is_unsafe: bool, adt: &ast::Adt, @@ -700,12 +635,11 @@ fn generate_impl_inner_with_factory( make.generic_param_list(itertools::chain(lifetime_params, ty_or_const_params)) }); - let generic_args = - generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); - let adt_assoc_bounds = - trait_.as_ref().zip(generic_params.as_ref()).and_then(|(trait_, params)| { - generic_param_associated_bounds_with_factory(make, adt, trait_, params) - }); + let generic_args = generic_params.as_ref().map(|params| params.to_generic_args(make)); + let adt_assoc_bounds = trait_ + .as_ref() + .zip(generic_params.as_ref()) + .and_then(|(trait_, params)| generic_param_associated_bounds(make, adt, trait_, params)); let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into(); @@ -730,51 +664,6 @@ fn generate_impl_inner_with_factory( } fn generic_param_associated_bounds( - adt: &ast::Adt, - trait_: &ast::Type, - generic_params: &ast::GenericParamList, -) -> Option<ast::WhereClause> { - let in_type_params = |name: &ast::NameRef| { - generic_params - .generic_params() - .filter_map(|param| match param { - ast::GenericParam::TypeParam(type_param) => type_param.name(), - _ => None, - }) - .any(|param| param.text() == name.text()) - }; - let adt_body = match adt { - ast::Adt::Enum(e) => e.variant_list().map(|it| it.syntax().clone()), - ast::Adt::Struct(s) => s.field_list().map(|it| it.syntax().clone()), - ast::Adt::Union(u) => u.record_field_list().map(|it| it.syntax().clone()), - }; - let mut trait_where_clause = adt_body - .into_iter() - .flat_map(|it| it.descendants()) - .filter_map(ast::Path::cast) - .filter_map(|path| { - let qualifier = path.qualifier()?.as_single_segment()?; - let qualifier = qualifier - .name_ref() - .or_else(|| match qualifier.type_anchor()?.ty()? { - ast::Type::PathType(path_type) => path_type.path()?.as_single_name_ref(), - _ => None, - }) - .filter(in_type_params)?; - Some((qualifier, path.segment()?.name_ref()?)) - }) - .map(|(qualifier, assoc_name)| { - let segments = [qualifier, assoc_name].map(make::path_segment); - let path = make::path_from_segments(segments, false); - let bounds = Some(make::type_bound(trait_.clone())); - make::where_pred(either::Either::Right(make::ty_path(path)), bounds) - }) - .unique_by(|it| it.syntax().to_string()) - .peekable(); - trait_where_clause.peek().is_some().then(|| make::where_clause(trait_where_clause)) -} - -fn generic_param_associated_bounds_with_factory( make: &SyntaxFactory, adt: &ast::Adt, trait_: &ast::Type, @@ -1147,7 +1036,7 @@ pub(crate) fn add_group_separators(s: &str, group_size: usize) -> String { /// Replaces the record expression, handling field shorthands including inside macros. pub(crate) fn replace_record_field_expr( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, record_field: ast::RecordExprField, initializer: ast::Expr, diff --git a/crates/ide-assists/src/utils/ref_field_expr.rs b/crates/ide-assists/src/utils/ref_field_expr.rs index fc9bf210e4..d2b02b3373 100644 --- a/crates/ide-assists/src/utils/ref_field_expr.rs +++ b/crates/ide-assists/src/utils/ref_field_expr.rs @@ -13,7 +13,7 @@ use crate::AssistContext; /// Decides whether the new path expression needs to be dereferenced and/or wrapped in parens. /// Returns the relevant parent expression to replace and the [RefData]. pub(crate) fn determine_ref_and_parens( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, field_expr: &FieldExpr, ) -> (ast::Expr, RefData) { let s = field_expr.syntax(); @@ -62,8 +62,8 @@ pub(crate) fn determine_ref_and_parens( // other combinations (`&value` -> `value`, `&&value` -> `&value`, `&value` -> `&&value`) might or might not be able to auto-ref/deref, // but there might be trait implementations an added `&` might resolve to // -> ONLY handle auto-ref from `value` to `&value` - fn is_auto_ref(ctx: &AssistContext<'_>, call_expr: &MethodCallExpr) -> bool { - fn impl_(ctx: &AssistContext<'_>, call_expr: &MethodCallExpr) -> Option<bool> { + fn is_auto_ref(ctx: &AssistContext<'_, '_>, call_expr: &MethodCallExpr) -> bool { + fn impl_(ctx: &AssistContext<'_, '_>, call_expr: &MethodCallExpr) -> Option<bool> { let rec = call_expr.receiver()?; let rec_ty = ctx.sema.type_of_expr(&rec)?.original(); // input must be actual value diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 2ed582598b..f3190bbbc8 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -34,7 +34,8 @@ use crate::{ CompletionContext, CompletionItem, CompletionItemKind, context::{ DotAccess, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind, - PathCompletionCtx, PathKind, PatternContext, TypeAscriptionTarget, TypeLocation, Visible, + PathCompletionCtx, PathKind, PatternContext, Qualified, TypeAscriptionTarget, TypeLocation, + Visible, }, item::Builder, render::{ @@ -86,7 +87,7 @@ impl Completions { } } - pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_>, keyword: &'static str) { + pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_, '_>, keyword: &'static str) { let item = CompletionItem::new( CompletionItemKind::Keyword, ctx.source_range(), @@ -96,7 +97,7 @@ impl Completions { item.add_to(self, ctx.db); } - pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_>) { + pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_, '_>) { ["self::", "crate::"].into_iter().for_each(|kw| self.add_keyword(ctx, kw)); if ctx.depth_from_crate_root > 0 { @@ -106,7 +107,7 @@ impl Completions { pub(crate) fn add_nameref_keywords_with_type_like( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ) { let mut add_keyword = |kw| { @@ -119,7 +120,7 @@ impl Completions { } } - pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext<'_>) { + pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext<'_, '_>) { ["self", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw)); if ctx.depth_from_crate_root > 0 { @@ -129,7 +130,7 @@ impl Completions { pub(crate) fn add_type_keywords( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ) { let mut add_keyword = |kw, snippet| { @@ -144,7 +145,7 @@ impl Completions { pub(crate) fn add_super_keyword( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, super_chain_len: Option<usize>, ) { if let Some(len) = super_chain_len @@ -157,7 +158,7 @@ impl Completions { pub(crate) fn add_keyword_snippet_expr( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, incomplete_let: bool, kw: &str, snippet: &str, @@ -184,7 +185,7 @@ impl Completions { pub(crate) fn add_keyword_snippet( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, kw: &str, snippet: &str, ) { @@ -200,7 +201,7 @@ impl Completions { pub(crate) fn add_expr( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, expr: &hir::term_search::Expr<'_>, ) { if let Some(item) = render_expr(ctx, expr) { @@ -210,7 +211,7 @@ impl Completions { pub(crate) fn add_crate_roots( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ) { ctx.process_all_names(&mut |name, res, doc_aliases| match res { @@ -223,7 +224,7 @@ impl Completions { pub(crate) fn add_path_resolution( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, resolution: hir::ScopeDef, @@ -245,7 +246,7 @@ impl Completions { pub(crate) fn add_pattern_resolution( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, local_name: hir::Name, resolution: hir::ScopeDef, @@ -266,7 +267,7 @@ impl Completions { pub(crate) fn add_enum_variants( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, e: hir::Enum, ) { @@ -277,7 +278,7 @@ impl Completions { pub(crate) fn add_module( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, module: hir::Module, local_name: hir::Name, @@ -294,7 +295,7 @@ impl Completions { pub(crate) fn add_macro( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, mac: hir::Macro, local_name: hir::Name, @@ -315,7 +316,7 @@ impl Completions { pub(crate) fn add_function( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, func: hir::Function, local_name: Option<hir::Name>, @@ -337,7 +338,7 @@ impl Completions { pub(crate) fn add_method( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, func: hir::Function, receiver: Option<SmolStr>, @@ -361,7 +362,7 @@ impl Completions { pub(crate) fn add_method_with_import( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, func: hir::Function, import: LocatedImport, @@ -385,7 +386,7 @@ impl Completions { .add_to(self, ctx.db); } - pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) { + pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_, '_>, konst: hir::Const) { let is_private_editable = match ctx.is_visible(&konst) { Visible::Yes => false, Visible::Editable => true, @@ -399,7 +400,7 @@ impl Completions { pub(crate) fn add_type_alias( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, type_alias: hir::TypeAlias, ) { let is_private_editable = match ctx.is_visible(&type_alias) { @@ -415,7 +416,7 @@ impl Completions { pub(crate) fn add_type_alias_with_eq( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, type_alias: hir::TypeAlias, ) { if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) { @@ -426,7 +427,7 @@ impl Completions { pub(crate) fn add_qualified_enum_variant( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, variant: hir::EnumVariant, path: hir::ModPath, @@ -443,7 +444,7 @@ impl Completions { pub(crate) fn add_enum_variant( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, variant: hir::EnumVariant, local_name: Option<hir::Name>, @@ -466,7 +467,7 @@ impl Completions { pub(crate) fn add_field( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, receiver: Option<SmolStr>, field: hir::Field, @@ -490,7 +491,7 @@ impl Completions { pub(crate) fn add_struct_literal( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, strukt: hir::Struct, path: Option<hir::ModPath>, @@ -514,7 +515,7 @@ impl Completions { pub(crate) fn add_union_literal( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, un: hir::Union, path: Option<hir::ModPath>, local_name: Option<hir::Name>, @@ -535,7 +536,7 @@ impl Completions { pub(crate) fn add_tuple_field( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, receiver: Option<SmolStr>, field: usize, ty: &hir::Type<'_>, @@ -546,7 +547,7 @@ impl Completions { self.add(item); } - pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) { + pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_, '_>, name: hir::Name) { CompletionItem::new( SymbolKind::LifetimeParam, ctx.source_range(), @@ -556,7 +557,7 @@ impl Completions { .add_to(self, ctx.db) } - pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) { + pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_, '_>, name: hir::Name) { CompletionItem::new( SymbolKind::Label, ctx.source_range(), @@ -568,7 +569,7 @@ impl Completions { pub(crate) fn add_variant_pat( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, path_ctx: Option<&PathCompletionCtx<'_>>, variant: hir::EnumVariant, @@ -589,7 +590,7 @@ impl Completions { pub(crate) fn add_qualified_variant_pat( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, variant: hir::EnumVariant, path: hir::ModPath, @@ -610,7 +611,7 @@ impl Completions { pub(crate) fn add_struct_pat( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, strukt: hir::Struct, local_name: Option<hir::Name>, @@ -628,7 +629,7 @@ impl Completions { )); } - pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) { + pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_, '_>, name: &str) { let item = CompletionItem::new( CompletionItemKind::Binding, ctx.source_range(), @@ -643,10 +644,10 @@ impl Completions { /// Skips variants that are visible with single segment paths. fn enum_variants_with_paths( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, enum_: hir::Enum, impl_: Option<&ast::Impl>, - cb: impl Fn(&mut Completions, &CompletionContext<'_>, hir::EnumVariant, hir::ModPath), + cb: impl Fn(&mut Completions, &CompletionContext<'_, '_>, hir::EnumVariant, hir::ModPath), ) { let mut process_variant = |variant: EnumVariant| { let self_path = hir::ModPath::from_segments( @@ -682,7 +683,7 @@ fn enum_variants_with_paths( pub(super) fn complete_name( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, NameContext { name, kind }: &NameContext, ) { match kind { @@ -723,10 +724,10 @@ pub(super) fn complete_name( } } -pub(super) fn complete_name_ref( +pub(super) fn complete_name_ref<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, - NameRefContext { nameref, kind }: &NameRefContext<'_>, + ctx: &CompletionContext<'_, 'db>, + NameRefContext { nameref, kind }: &NameRefContext<'db>, ) { match kind { NameRefKind::Path(path_ctx) => { @@ -752,6 +753,7 @@ pub(super) fn complete_name_ref( if let TypeAscriptionTarget::RetType { item: Some(item), .. } = ascription && path_ctx.required_thin_arrow().is_some() + && matches!(path_ctx.qualified, Qualified::No) { keyword::complete_for_and_where(acc, ctx, &item.clone().into()); } @@ -811,7 +813,7 @@ pub(super) fn complete_name_ref( fn complete_patterns( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, ) { flyimport::import_on_the_fly_pat(acc, ctx, pattern_ctx); diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index da1e664f96..df513dd8fd 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs @@ -4,13 +4,7 @@ use std::sync::LazyLock; -use ide_db::{ - FxHashMap, SymbolKind, - generated::lints::{ - CLIPPY_LINT_GROUPS, CLIPPY_LINTS, DEFAULT_LINTS, FEATURES, Lint, RUSTDOC_LINTS, - }, - syntax_helpers::node_ext::parse_tt_as_comma_sep_paths, -}; +use ide_db::{FxHashMap, SymbolKind, syntax_helpers::node_ext::parse_tt_as_comma_sep_paths}; use itertools::Itertools; use syntax::{ AstNode, Edition, SyntaxKind, T, @@ -26,6 +20,7 @@ use crate::{ mod cfg; mod derive; mod diagnostic; +mod feature; mod lint; mod macro_use; mod repr; @@ -36,8 +31,8 @@ pub(crate) use self::derive::complete_derive_path; /// Complete inputs to known builtin attributes as well as derive attributes pub(crate) fn complete_known_attribute_input( acc: &mut Completions, - ctx: &CompletionContext<'_>, - &colon_prefix: &bool, + ctx: &CompletionContext<'_, '_>, + colon_prefix: bool, fake_attribute_under_caret: &ast::TokenTreeMeta, extern_crate: Option<&ast::ExternCrate>, ) -> Option<()> { @@ -49,35 +44,25 @@ pub(crate) fn complete_known_attribute_input( let tt = attribute.token_tree()?; match segments.as_slice() { - ["repr"] => repr::complete_repr(acc, ctx, tt), - ["feature"] => lint::complete_lint( + ["repr"] => repr::complete_repr(acc, ctx, &parse_comma_sep_expr(tt)?), + ["feature"] => { + feature::complete_feature(acc, ctx, &parse_tt_as_comma_sep_paths(tt, ctx.edition)?) + } + ["allow" | "expect" | "deny" | "forbid" | "warn"] => lint::complete_lint( acc, ctx, colon_prefix, &parse_tt_as_comma_sep_paths(tt, ctx.edition)?, - FEATURES, ), - ["allow"] | ["expect"] | ["deny"] | ["forbid"] | ["warn"] => { - let existing_lints = parse_tt_as_comma_sep_paths(tt, ctx.edition)?; - - let lints: Vec<Lint> = CLIPPY_LINT_GROUPS - .iter() - .map(|g| &g.lint) - .chain(DEFAULT_LINTS) - .chain(CLIPPY_LINTS) - .chain(RUSTDOC_LINTS) - .cloned() - .collect(); - - lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints); - } ["macro_use"] => macro_use::complete_macro_use( acc, ctx, extern_crate, &parse_tt_as_comma_sep_paths(tt, ctx.edition)?, ), - ["diagnostic", "on_unimplemented"] => diagnostic::complete_on_unimplemented(acc, ctx, tt), + ["diagnostic", "on_unimplemented"] => { + diagnostic::complete_on_unimplemented(acc, ctx, &parse_comma_sep_expr(tt)?) + } _ => (), } Some(()) @@ -85,7 +70,7 @@ pub(crate) fn complete_known_attribute_input( pub(crate) fn complete_attribute_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, &AttrCtx { kind, annotated_item_kind, ref derive_helpers }: &AttrCtx, ) { @@ -190,7 +175,7 @@ pub(crate) fn complete_attribute_path( match attributes { Some(applicable) => applicable .iter() - .flat_map(|name| ATTRIBUTES.binary_search_by(|attr| attr.key().cmp(name)).ok()) + .flat_map(|name| ATTRIBUTES.binary_search_by_key(name, |attr| attr.key()).ok()) .flat_map(|idx| ATTRIBUTES.get(idx)) .for_each(add_completion), None if is_inner => ATTRIBUTES.iter().for_each(add_completion), diff --git a/crates/ide-completion/src/completions/attribute/cfg.rs b/crates/ide-completion/src/completions/attribute/cfg.rs index 0d36fb7a40..1672e8e793 100644 --- a/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/crates/ide-completion/src/completions/attribute/cfg.rs @@ -6,12 +6,12 @@ use syntax::{AstToken, Direction, NodeOrToken, SmolStr, SyntaxKind, algo, ast::I use crate::{CompletionItem, completions::Completions, context::CompletionContext}; -pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { +pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) { let add_completion = |item: &str| { let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item, ctx.edition); completion.insert_text(format!(r#""{item}""#)); - acc.add(completion.build(ctx.db)); + completion.add_to(acc, ctx.db); }; // FIXME: Move this into context/analysis.rs @@ -49,8 +49,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { ctx.edition, ); item.insert_text(insert_text); - - acc.add(item.build(ctx.db)); + item.add_to(acc, ctx.db); }), }, None => ctx @@ -82,7 +81,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { { item.insert_snippet(cap, snippet); } - acc.add(item.build(ctx.db)); + item.add_to(acc, ctx.db); }), } } diff --git a/crates/ide-completion/src/completions/attribute/derive.rs b/crates/ide-completion/src/completions/attribute/derive.rs index 57ea609a40..9e7dabbd01 100644 --- a/crates/ide-completion/src/completions/attribute/derive.rs +++ b/crates/ide-completion/src/completions/attribute/derive.rs @@ -12,7 +12,7 @@ use crate::{ pub(crate) fn complete_derive_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, existing_derives: &ExistingDerives, ) { diff --git a/crates/ide-completion/src/completions/attribute/diagnostic.rs b/crates/ide-completion/src/completions/attribute/diagnostic.rs index 8adc974239..1a2e5652e1 100644 --- a/crates/ide-completion/src/completions/attribute/diagnostic.rs +++ b/crates/ide-completion/src/completions/attribute/diagnostic.rs @@ -9,47 +9,45 @@ use super::AttrCompletion; pub(super) fn complete_on_unimplemented( acc: &mut Completions, - ctx: &CompletionContext<'_>, - input: ast::TokenTree, + ctx: &CompletionContext<'_, '_>, + existing_keys: &[ast::Expr], ) { - if let Some(existing_keys) = super::parse_comma_sep_expr(input) { - for attr in ATTRIBUTE_ARGS { - let already_annotated = existing_keys - .iter() - .filter_map(|expr| match expr { - ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), - ast::Expr::BinExpr(bin) - if bin.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) => - { - match bin.lhs()? { - ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), - _ => None, - } + for attr in ATTRIBUTE_ARGS { + let already_annotated = existing_keys + .iter() + .filter_map(|expr| match expr { + ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), + ast::Expr::BinExpr(bin) + if bin.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) => + { + match bin.lhs()? { + ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), + _ => None, } - _ => None, - }) - .any(|it| { - let text = it.text(); - attr.key() == text && text != "note" - }); - if already_annotated { - continue; - } + } + _ => None, + }) + .any(|it| { + let text = it.text(); + attr.key() == text && text != "note" + }); + if already_annotated { + continue; + } - let mut item = CompletionItem::new( - SymbolKind::BuiltinAttr, - ctx.source_range(), - attr.label, - ctx.edition, - ); - if let Some(lookup) = attr.lookup { - item.lookup_by(lookup); - } - if let Some((snippet, cap)) = attr.snippet.zip(ctx.config.snippet_cap) { - item.insert_snippet(cap, snippet); - } - item.add_to(acc, ctx.db); + let mut item = CompletionItem::new( + SymbolKind::BuiltinAttr, + ctx.source_range(), + attr.label, + ctx.edition, + ); + if let Some(lookup) = attr.lookup { + item.lookup_by(lookup); + } + if let Some((snippet, cap)) = attr.snippet.zip(ctx.config.snippet_cap) { + item.insert_snippet(cap, snippet); } + item.add_to(acc, ctx.db); } } diff --git a/crates/ide-completion/src/completions/attribute/feature.rs b/crates/ide-completion/src/completions/attribute/feature.rs new file mode 100644 index 0000000000..1bcf05faa8 --- /dev/null +++ b/crates/ide-completion/src/completions/attribute/feature.rs @@ -0,0 +1,30 @@ +//! Completion for features +use ide_db::{ + SymbolKind, + documentation::Documentation, + generated::lints::{FEATURES, Lint}, +}; +use syntax::ast; + +use crate::{Completions, context::CompletionContext, item::CompletionItem}; + +pub(super) fn complete_feature( + acc: &mut Completions, + ctx: &CompletionContext<'_, '_>, + existing_features: &[ast::Path], +) { + for &Lint { label, description, .. } in FEATURES { + let feature_already_annotated = existing_features + .iter() + .filter_map(|p| p.as_single_name_ref()) + .any(|n| n.text() == label); + if feature_already_annotated { + continue; + } + + let mut item = + CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition); + item.documentation(Documentation::new_borrowed(description)); + item.add_to(acc, ctx.db) + } +} diff --git a/crates/ide-completion/src/completions/attribute/lint.rs b/crates/ide-completion/src/completions/attribute/lint.rs index df577b8ed0..2f957b5a55 100644 --- a/crates/ide-completion/src/completions/attribute/lint.rs +++ b/crates/ide-completion/src/completions/attribute/lint.rs @@ -1,29 +1,29 @@ //! Completion for lints -use ide_db::{SymbolKind, documentation::Documentation, generated::lints::Lint}; +use ide_db::{ + SymbolKind, + documentation::Documentation, + generated::lints::{CLIPPY_LINT_GROUPS, CLIPPY_LINTS, DEFAULT_LINTS, Lint, RUSTDOC_LINTS}, +}; use syntax::ast; use crate::{Completions, context::CompletionContext, item::CompletionItem}; pub(super) fn complete_lint( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, is_qualified: bool, existing_lints: &[ast::Path], - lints_completions: &[Lint], ) { - for &Lint { label, description, .. } in lints_completions { - let (qual, name) = { - // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead? - let mut parts = label.split("::"); - let ns_or_label = match parts.next() { - Some(it) => it, - None => continue, - }; - let label = parts.next(); - match label { - Some(label) => (Some(ns_or_label), label), - None => (None, ns_or_label), - } + let lints = (CLIPPY_LINT_GROUPS.iter().map(|g| &g.lint)) + .chain(DEFAULT_LINTS) + .chain(CLIPPY_LINTS) + .chain(RUSTDOC_LINTS); + + for &Lint { label, description, .. } in lints { + // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead? + let (qual, name) = match label.split_once("::") { + Some((qual, name)) => (Some(qual), name), + None => (None, label), }; if qual.is_none() && is_qualified { // qualified completion requested, but this lint is unqualified @@ -56,7 +56,7 @@ pub(super) fn complete_lint( }; let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition); - item.documentation(Documentation::new_owned(description.to_owned())); + item.documentation(Documentation::new_borrowed(description)); item.add_to(acc, ctx.db) } } diff --git a/crates/ide-completion/src/completions/attribute/macro_use.rs b/crates/ide-completion/src/completions/attribute/macro_use.rs index 136315c61f..618cf6fd01 100644 --- a/crates/ide-completion/src/completions/attribute/macro_use.rs +++ b/crates/ide-completion/src/completions/attribute/macro_use.rs @@ -7,7 +7,7 @@ use crate::{Completions, context::CompletionContext, item::CompletionItem}; pub(super) fn complete_macro_use( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, extern_crate: Option<&ast::ExternCrate>, existing_imports: &[ast::Path], ) { @@ -20,11 +20,11 @@ pub(super) fn complete_macro_use( let mac_name = mac.name(ctx.db); let mac_name = mac_name.as_str(); - let existing_import = existing_imports + let already_imported = existing_imports .iter() .filter_map(|p| p.as_single_name_ref()) - .find(|n| n.text() == mac_name); - if existing_import.is_some() { + .any(|n| n.text() == mac_name); + if already_imported { continue; } diff --git a/crates/ide-completion/src/completions/attribute/repr.rs b/crates/ide-completion/src/completions/attribute/repr.rs index cb7ccf7373..63cddb365e 100644 --- a/crates/ide-completion/src/completions/attribute/repr.rs +++ b/crates/ide-completion/src/completions/attribute/repr.rs @@ -7,43 +7,37 @@ use crate::{Completions, context::CompletionContext, item::CompletionItem}; pub(super) fn complete_repr( acc: &mut Completions, - ctx: &CompletionContext<'_>, - input: ast::TokenTree, + ctx: &CompletionContext<'_, '_>, + existing_reprs: &[ast::Expr], ) { - if let Some(existing_reprs) = super::parse_comma_sep_expr(input) { - for &ReprCompletion { label, snippet, lookup, collides } in REPR_COMPLETIONS { - let repr_already_annotated = existing_reprs - .iter() - .filter_map(|expr| match expr { + for &ReprCompletion { label, snippet, lookup, collides } in REPR_COMPLETIONS { + let repr_already_annotated = existing_reprs + .iter() + .filter_map(|expr| match expr { + ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), + ast::Expr::CallExpr(call) => match call.expr()? { ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), - ast::Expr::CallExpr(call) => match call.expr()? { - ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(), - _ => None, - }, _ => None, - }) - .any(|it| { - let text = it.text(); - lookup.unwrap_or(label) == text || collides.contains(&text.as_str()) - }); - if repr_already_annotated { - continue; - } + }, + _ => None, + }) + .any(|it| { + let text = it.text(); + lookup.unwrap_or(label) == text || collides.contains(&text.as_str()) + }); + if repr_already_annotated { + continue; + } - let mut item = CompletionItem::new( - SymbolKind::BuiltinAttr, - ctx.source_range(), - label, - ctx.edition, - ); - if let Some(lookup) = lookup { - item.lookup_by(lookup); - } - if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) { - item.insert_snippet(cap, snippet); - } - item.add_to(acc, ctx.db); + let mut item = + CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), label, ctx.edition); + if let Some(lookup) = lookup { + item.lookup_by(lookup); + } + if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) { + item.insert_snippet(cap, snippet); } + item.add_to(acc, ctx.db); } } diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 18c1992afa..2cc2200df9 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -18,7 +18,7 @@ use crate::{ /// Complete dot accesses, i.e. fields or methods. pub(crate) fn complete_dot( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, ) { let receiver_ty = match dot_access { @@ -126,7 +126,7 @@ pub(crate) fn complete_dot( pub(crate) fn complete_undotted_self( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, expr_ctx: &PathExprCtx<'_>, ) { @@ -198,7 +198,7 @@ pub(crate) fn complete_undotted_self( fn complete_fields( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, receiver: &hir::Type<'_>, mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type<'_>), mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type<'_>), @@ -227,13 +227,13 @@ fn complete_fields( } fn complete_methods( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, receiver: &hir::Type<'_>, traits_in_scope: &FxHashSet<hir::TraitId>, f: impl FnMut(hir::Function), ) { - struct Callback<'a, F> { - ctx: &'a CompletionContext<'a>, + struct Callback<'a, 'db, F> { + ctx: &'a CompletionContext<'a, 'db>, f: F, // We deliberately deduplicate by function ID and not name, because while inherent methods cannot be // duplicated, trait methods can. And it is still useful to show all of them (even when there @@ -241,7 +241,7 @@ fn complete_methods( seen_methods: FxHashSet<Function>, } - impl<F> MethodCandidateCallback for Callback<'_, F> + impl<F> MethodCandidateCallback for Callback<'_, '_, F> where F: FnMut(hir::Function), { @@ -1093,7 +1093,7 @@ impl Foo { fn foo(&mut self) { let _: fn(&mut Self) = |this| { $0 } } }"#, me this.foo() fn(&mut self) lc self &mut Foo lc this &mut Foo - md core + md core:: sp Self Foo st Foo Foo tt Fn @@ -1117,7 +1117,7 @@ impl Foo { fn foo(&self) { let _: fn(&Self) = |foo| { $0 } } }"#, me self.foo() fn(&self) lc foo &Foo lc self &Foo - md core + md core:: sp Self Foo st Foo Foo tt Fn @@ -1137,7 +1137,7 @@ impl Foo { fn foo(&self) { let _: fn(&Self) = || { $0 } } }"#, fd self.field i32 me self.foo() fn(&self) lc self &Foo - md core + md core:: sp Self Foo st Foo Foo tt Fn @@ -1159,7 +1159,7 @@ impl Foo { fn foo(&self) { let _: fn(&Self, &Self) = |foo, other| { $0 } } }"#, lc foo &Foo lc other &Foo lc self &Foo - md core + md core:: sp Self Foo st Foo Foo tt Fn diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 885d1a3075..30662877d6 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -47,7 +47,7 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ pub(crate) fn complete_cargo_env_vars( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, original: &ast::String, expanded: &ast::String, ) -> Option<()> { diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index c15c67173e..a2a4cbac21 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -12,14 +12,14 @@ use crate::{ context::{PathCompletionCtx, PathExprCtx, Qualified}, }; -struct PathCallback<'a, F> { - ctx: &'a CompletionContext<'a>, +struct PathCallback<'a, 'db, F> { + ctx: &'a CompletionContext<'a, 'db>, acc: &'a mut Completions, add_assoc_item: F, seen: FxHashSet<hir::AssocItem>, } -impl<F> PathCandidateCallback for PathCallback<'_, F> +impl<F> PathCandidateCallback for PathCallback<'_, '_, F> where F: FnMut(&mut Completions, hir::AssocItem), { @@ -46,7 +46,7 @@ where pub(crate) fn complete_expr_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, expr_ctx: &PathExprCtx<'_>, ) { @@ -458,7 +458,7 @@ pub(crate) fn complete_expr_path( pub(crate) fn complete_expr( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, ) { let _p = tracing::info_span!("complete_expr").entered(); diff --git a/crates/ide-completion/src/completions/extern_abi.rs b/crates/ide-completion/src/completions/extern_abi.rs index 570d1a0a2d..b00b2e1907 100644 --- a/crates/ide-completion/src/completions/extern_abi.rs +++ b/crates/ide-completion/src/completions/extern_abi.rs @@ -43,7 +43,7 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[ pub(crate) fn complete_extern_abi( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, expanded: &ast::String, ) -> Option<()> { if !expanded.syntax().parent().is_some_and(|it| ast::Abi::can_cast(it.kind())) { diff --git a/crates/ide-completion/src/completions/extern_crate.rs b/crates/ide-completion/src/completions/extern_crate.rs index 91202e8b32..819db549d4 100644 --- a/crates/ide-completion/src/completions/extern_crate.rs +++ b/crates/ide-completion/src/completions/extern_crate.rs @@ -8,7 +8,7 @@ use crate::{CompletionItem, CompletionItemKind, context::CompletionContext}; use super::Completions; -pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_>) { +pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) { let imported_extern_crates: Vec<Name> = ctx.scope.extern_crate_decls().collect(); for (name, module) in ctx.scope.extern_crates() { diff --git a/crates/ide-completion/src/completions/field.rs b/crates/ide-completion/src/completions/field.rs index 26afa9c8ad..b4f7e1839d 100644 --- a/crates/ide-completion/src/completions/field.rs +++ b/crates/ide-completion/src/completions/field.rs @@ -7,7 +7,7 @@ use crate::{ pub(crate) fn complete_field_list_tuple_variant( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ) { if ctx.qualifier_ctx.vis_node.is_some() { @@ -28,7 +28,7 @@ pub(crate) fn complete_field_list_tuple_variant( pub(crate) fn complete_field_list_record_variant( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, ) { if ctx.qualifier_ctx.vis_node.is_none() { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 2cf87baf33..b350647b9a 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -108,10 +108,10 @@ use crate::{ // The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag. // Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding // capability enabled. -pub(crate) fn import_on_the_fly_path( +pub(crate) fn import_on_the_fly_path<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, - path_ctx: &PathCompletionCtx<'_>, + ctx: &CompletionContext<'_, 'db>, + path_ctx: &PathCompletionCtx<'db>, ) -> Option<()> { if !ctx.config.enable_imports_on_the_fly { return None; @@ -155,7 +155,7 @@ pub(crate) fn import_on_the_fly_path( pub(crate) fn import_on_the_fly_pat( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, ) -> Option<()> { if !ctx.config.enable_imports_on_the_fly { @@ -178,10 +178,10 @@ pub(crate) fn import_on_the_fly_pat( ) } -pub(crate) fn import_on_the_fly_dot( +pub(crate) fn import_on_the_fly_dot<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, - dot_access: &DotAccess<'_>, + ctx: &CompletionContext<'_, 'db>, + dot_access: &DotAccess<'db>, ) -> Option<()> { if !ctx.config.enable_imports_on_the_fly { return None; @@ -206,11 +206,11 @@ pub(crate) fn import_on_the_fly_dot( ) } -fn import_on_the_fly( +fn import_on_the_fly<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, - path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx<'_>, - import_assets: ImportAssets<'_>, + ctx: &CompletionContext<'_, 'db>, + path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx<'db>, + import_assets: ImportAssets<'db>, position: SyntaxNode, potential_import_name: String, ) -> Option<()> { @@ -292,11 +292,11 @@ fn import_on_the_fly( Some(()) } -fn import_on_the_fly_pat_( +fn import_on_the_fly_pat_<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, 'db>, pattern_ctx: &PatternContext, - import_assets: ImportAssets<'_>, + import_assets: ImportAssets<'db>, position: SyntaxNode, potential_import_name: String, ) -> Option<()> { @@ -338,11 +338,11 @@ fn import_on_the_fly_pat_( Some(()) } -fn import_on_the_fly_method( +fn import_on_the_fly_method<'db>( acc: &mut Completions, - ctx: &CompletionContext<'_>, - dot_access: &DotAccess<'_>, - import_assets: ImportAssets<'_>, + ctx: &CompletionContext<'_, 'db>, + dot_access: &DotAccess<'db>, + import_assets: ImportAssets<'db>, position: SyntaxNode, potential_import_name: String, ) -> Option<()> { @@ -378,7 +378,7 @@ fn import_on_the_fly_method( Some(()) } -fn filter_excluded_flyimport(ctx: &CompletionContext<'_>, import: &LocatedImport) -> bool { +fn filter_excluded_flyimport(ctx: &CompletionContext<'_, '_>, import: &LocatedImport) -> bool { let def = import.item_to_import.into_module_def(); let is_exclude_flyimport = ctx.exclude_flyimport.get(&def).copied(); @@ -400,14 +400,14 @@ fn filter_excluded_flyimport(ctx: &CompletionContext<'_>, import: &LocatedImport true } -fn import_name(ctx: &CompletionContext<'_>) -> String { +fn import_name(ctx: &CompletionContext<'_, '_>) -> String { let token_kind = ctx.token.kind(); if token_kind.is_any_identifier() { ctx.token.to_string() } else { String::new() } } fn import_assets_for_path<'db>( - ctx: &CompletionContext<'db>, + ctx: &CompletionContext<'_, 'db>, path: Option<&ast::Path>, potential_import_name: &str, qualifier: Option<ast::Path>, diff --git a/crates/ide-completion/src/completions/fn_param.rs b/crates/ide-completion/src/completions/fn_param.rs index bd0b69215c..f1e8e5f39e 100644 --- a/crates/ide-completion/src/completions/fn_param.rs +++ b/crates/ide-completion/src/completions/fn_param.rs @@ -22,7 +22,7 @@ use crate::{ /// Also complete parameters for closure or local functions from the surrounding defined locals. pub(crate) fn complete_fn_param( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, ) -> Option<()> { let (ParamContext { param_list, kind, param, .. }, impl_or_trait) = match pattern_ctx { @@ -78,7 +78,7 @@ pub(crate) fn complete_fn_param( } fn fill_fn_params( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, function: &ast::Fn, param_list: &ast::ParamList, current_param: &ast::Param, @@ -139,7 +139,7 @@ fn fill_fn_params( } fn params_from_stmt_list_scope( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, stmt_list: ast::StmtList, mut cb: impl FnMut(hir::Name, String), ) { @@ -196,7 +196,7 @@ fn should_add_self_completions( } } -fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> SmolStr, TextRange)> { +fn comma_wrapper(ctx: &CompletionContext<'_, '_>) -> Option<(impl Fn(&str) -> SmolStr, TextRange)> { let param = ctx.original_token.parent_ancestors().find(|node| node.kind() == SyntaxKind::PARAM)?; diff --git a/crates/ide-completion/src/completions/format_string.rs b/crates/ide-completion/src/completions/format_string.rs index eaacd8d1a8..1e52402185 100644 --- a/crates/ide-completion/src/completions/format_string.rs +++ b/crates/ide-completion/src/completions/format_string.rs @@ -10,7 +10,7 @@ use crate::{CompletionItem, CompletionItemKind, Completions, context::Completion /// Complete identifiers in format strings. pub(crate) fn format_string( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, original: &ast::String, expanded: &ast::String, ) { diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs index 39048e4400..1b26cab263 100644 --- a/crates/ide-completion/src/completions/item_list.rs +++ b/crates/ide-completion/src/completions/item_list.rs @@ -9,7 +9,7 @@ pub(crate) mod trait_impl; pub(crate) fn complete_item_list_in_expr( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, expr_ctx: &PathExprCtx<'_>, ) { @@ -24,7 +24,7 @@ pub(crate) fn complete_item_list_in_expr( pub(crate) fn complete_item_list( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, kind: &ItemListKind, ) { @@ -72,7 +72,11 @@ pub(crate) fn complete_item_list( } } -fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option<&ItemListKind>) { +fn add_keywords( + acc: &mut Completions, + ctx: &CompletionContext<'_, '_>, + kind: Option<&ItemListKind>, +) { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None); diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index 4072f05a41..c165a32082 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -38,10 +38,14 @@ use ide_db::{ syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items, }; use syntax::ast::HasGenericParams; +use syntax::syntax_editor::{Position, SyntaxEditor}; use syntax::{ AstNode, SmolStr, SyntaxElement, SyntaxKind, T, TextRange, ToSmolStr, - ast::{self, HasGenericArgs, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make}, - format_smolstr, ted, + ast::{ + self, HasGenericArgs, HasTypeBounds, + edit::{AstNodeEdit, AttrsOwnerEdit}, + }, + format_smolstr, }; use crate::{ @@ -59,7 +63,7 @@ enum ImplCompletionKind { pub(crate) fn complete_trait_impl_const( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, name: &Option<ast::Name>, ) -> Option<()> { complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::Const) @@ -67,7 +71,7 @@ pub(crate) fn complete_trait_impl_const( pub(crate) fn complete_trait_impl_type_alias( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, name: &Option<ast::Name>, ) -> Option<()> { complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::TypeAlias) @@ -75,7 +79,7 @@ pub(crate) fn complete_trait_impl_type_alias( pub(crate) fn complete_trait_impl_fn( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, name: &Option<ast::Name>, ) -> Option<()> { complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::Fn) @@ -83,7 +87,7 @@ pub(crate) fn complete_trait_impl_fn( fn complete_trait_impl_name( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, name: &Option<ast::Name>, kind: ImplCompletionKind, ) -> Option<()> { @@ -122,7 +126,7 @@ fn complete_trait_impl_name( pub(crate) fn complete_trait_impl_item_by_name( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, name_ref: &Option<ast::NameRef>, impl_: &Option<ast::Impl>, @@ -149,7 +153,7 @@ pub(crate) fn complete_trait_impl_item_by_name( fn complete_trait_impl( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, kind: ImplCompletionKind, replacement_range: TextRange, impl_def: &ast::Impl, @@ -178,7 +182,7 @@ fn complete_trait_impl( fn add_function_impl( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, replacement_range: TextRange, func: hir::Function, impl_def: hir::Impl, @@ -198,7 +202,7 @@ fn add_function_impl( fn add_function_impl_( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, replacement_range: TextRange, func: hir::Function, impl_def: hir::Impl, @@ -233,13 +237,14 @@ fn add_function_impl_( get_transformed_fn(ctx, source.value, impl_def, async_sugaring) { let function_decl = function_declaration(ctx, &transformed_fn, source.file_id.macro_file()); + let ws = if function_decl.contains('\n') { "\n" } else { " " }; match ctx.config.snippet_cap { Some(cap) => { - let snippet = format!("{function_decl} {{\n $0\n}}"); + let snippet = format!("{function_decl}{ws}{{\n $0\n}}"); item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet)); } None => { - let header = format!("{function_decl} {{"); + let header = format!("{function_decl}{ws}{{"); item.text_edit(TextEdit::replace(replacement_range, header)); } }; @@ -257,70 +262,65 @@ enum AsyncSugaring { /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. fn get_transformed_assoc_item( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, assoc_item: ast::AssocItem, impl_def: hir::Impl, ) -> Option<ast::AssocItem> { let trait_ = impl_def.trait_(ctx.db)?; let source_scope = &ctx.sema.scope(assoc_item.syntax())?; - let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?; - let transform = PathTransform::trait_impl( - target_scope, - source_scope, - trait_, - ctx.sema.source(impl_def)?.value, - ); + let impl_source = ctx.sema.source(impl_def)?; + let target_scope = &ctx.sema.scope(impl_source.syntax().value)?; + let transform = + PathTransform::trait_impl(target_scope, source_scope, trait_, impl_source.value); - let assoc_item = assoc_item.clone_for_update(); // FIXME: Paths in nested macros are not handled well. See // `macro_generated_assoc_item2` test. let assoc_item = ast::AssocItem::cast(transform.apply(assoc_item.syntax()))?; - assoc_item.remove_attrs_and_docs(); - Some(assoc_item) + let (editor, assoc_item) = SyntaxEditor::with_ast_node(&assoc_item); + assoc_item.remove_attrs_and_docs(&editor); + ast::AssocItem::cast(editor.finish().new_root().clone()) } /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. fn get_transformed_fn( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, fn_: ast::Fn, impl_def: hir::Impl, async_: AsyncSugaring, ) -> Option<ast::Fn> { let trait_ = impl_def.trait_(ctx.db)?; let source_scope = &ctx.sema.scope(fn_.syntax())?; - let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?; - let transform = PathTransform::trait_impl( - target_scope, - source_scope, - trait_, - ctx.sema.source(impl_def)?.value, - ); + let impl_source = ctx.sema.source(impl_def)?; + let target_scope = &ctx.sema.scope(impl_source.syntax().value)?; + let transform = + PathTransform::trait_impl(target_scope, source_scope, trait_, impl_source.value); - let fn_ = fn_.clone_for_update(); + let fn_ = fn_.reset_indent(); // FIXME: Paths in nested macros are not handled well. See // `macro_generated_assoc_item2` test. let fn_ = ast::Fn::cast(transform.apply(fn_.syntax()))?; - fn_.remove_attrs_and_docs(); + let (editor, fn_) = SyntaxEditor::with_ast_node(&fn_); + let factory = editor.make(); + fn_.remove_attrs_and_docs(&editor); match async_ { AsyncSugaring::Desugar => { match fn_.ret_type() { Some(ret_ty) => { let ty = ret_ty.ty()?; - ted::replace( + editor.replace( ty.syntax(), - make::ty(&format!("impl Future<Output = {ty}>")) - .syntax() - .clone_for_update(), + factory.ty(&format!("impl Future<Output = {ty}>")).syntax(), + ); + } + None => { + let ret_type = factory.ret_type(factory.ty("impl Future<Output = ()>")); + editor.insert_with_whitespace( + Position::after(fn_.param_list()?.syntax()), + ret_type.syntax(), ); } - None => ted::append_child( - fn_.param_list()?.syntax(), - make::ret_type(make::ty("impl Future<Output = ()>")) - .syntax() - .clone_for_update(), - ), } - fn_.async_token().unwrap().detach(); + editor.delete(fn_.async_token()?); } AsyncSugaring::Resugar => { let ty = fn_.ret_type()?.ty()?; @@ -347,23 +347,26 @@ fn get_transformed_fn( if let ast::Type::TupleType(ty) = &output && ty.fields().next().is_none() { - ted::remove(fn_.ret_type()?.syntax()); + editor.delete(fn_.ret_type()?.syntax()); } else { - ted::replace(ty.syntax(), output.syntax()); + editor.replace(ty.syntax(), output.syntax()); } } _ => (), } - ted::prepend_child(fn_.syntax(), make::token(T![async])); + editor.insert_with_whitespace( + Position::first_child_of(fn_.syntax()), + factory.token(T![async]), + ); } AsyncSugaring::Async | AsyncSugaring::Plain => (), } - Some(fn_) + ast::Fn::cast(editor.finish().new_root().clone()) } fn add_type_alias_impl( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, replacement_range: TextRange, type_alias: hir::TypeAlias, impl_def: hir::Impl, @@ -444,7 +447,7 @@ fn add_type_alias_impl( fn add_const_impl( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, replacement_range: TextRange, const_: hir::Const, impl_def: hir::Impl, @@ -486,13 +489,13 @@ fn add_const_impl( } fn make_const_compl_syntax( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, const_: &ast::Const, macro_file: Option<MacroCallId>, ) -> SmolStr { let const_ = if let Some(macro_file) = macro_file { let span_map = ctx.db.expansion_span_map(macro_file); - prettify_macro_expansion(ctx.db, const_.syntax().clone(), &span_map, ctx.krate.into()) + prettify_macro_expansion(ctx.db, const_.syntax().clone(), span_map, ctx.krate.into()) } else { const_.syntax().clone() }; @@ -514,13 +517,13 @@ fn make_const_compl_syntax( } fn function_declaration( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, node: &ast::Fn, macro_file: Option<MacroCallId>, ) -> String { let node = if let Some(macro_file) = macro_file { let span_map = ctx.db.expansion_span_map(macro_file); - prettify_macro_expansion(ctx.db, node.syntax().clone(), &span_map, ctx.krate.into()) + prettify_macro_expansion(ctx.db, node.syntax().clone(), span_map, ctx.krate.into()) } else { node.syntax().clone() }; @@ -1256,7 +1259,7 @@ trait SomeTrait<T> {} trait Foo<T> { fn function() - where Self: SomeTrait<T>; + where Self: SomeTrait<T>; } struct Bar; @@ -1269,13 +1272,14 @@ trait SomeTrait<T> {} trait Foo<T> { fn function() - where Self: SomeTrait<T>; + where Self: SomeTrait<T>; } struct Bar; impl Foo<u32> for Bar { fn function() - where Self: SomeTrait<u32> { +where Self: SomeTrait<u32> +{ $0 } } @@ -1356,7 +1360,7 @@ noop! { struct Test; impl Foo for Test { - fn foo(&mut self,bar:i64,baz: &mut u32) -> Result<(),u32> { + fn foo(&mut self,bar: i64,baz: &mut u32) -> Result<(),u32> { $0 } } @@ -1740,7 +1744,7 @@ impl Trait for () { me fn bar(..) me fn baz(..) me fn foo(..) - md proc_macros + md proc_macros:: kw crate:: kw self:: "#]], diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index fbb3cde968..f7dd1589ae 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -6,7 +6,7 @@ use crate::{CompletionContext, Completions}; pub(crate) fn complete_for_and_where( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, keyword_item: &ast::Item, ) { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs index 8902cd09ce..6291b42a03 100644 --- a/crates/ide-completion/src/completions/lifetime.rs +++ b/crates/ide-completion/src/completions/lifetime.rs @@ -17,7 +17,7 @@ use crate::{ /// Completes lifetimes. pub(crate) fn complete_lifetime( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, lifetime_ctx: &LifetimeContext, ) { let &LifetimeContext { kind: LifetimeKind::Lifetime { in_lifetime_param_bound, def }, .. } = @@ -44,7 +44,7 @@ pub(crate) fn complete_lifetime( /// Completes labels. pub(crate) fn complete_label( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, lifetime_ctx: &LifetimeContext, ) { if !matches!(lifetime_ctx, LifetimeContext { kind: LifetimeKind::LabelRef, .. }) { diff --git a/crates/ide-completion/src/completions/macro_def.rs b/crates/ide-completion/src/completions/macro_def.rs index 2c8e7a2e62..884b4dd966 100644 --- a/crates/ide-completion/src/completions/macro_def.rs +++ b/crates/ide-completion/src/completions/macro_def.rs @@ -4,7 +4,7 @@ use ide_db::SymbolKind; use crate::{CompletionItem, Completions, context::CompletionContext}; -pub(crate) fn complete_macro_segment(acc: &mut Completions, ctx: &CompletionContext<'_>) { +pub(crate) fn complete_macro_segment(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) { for &label in MACRO_SEGMENTS { let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), label, ctx.edition); diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs index 3333300045..a8c6c82aac 100644 --- a/crates/ide-completion/src/completions/mod_.rs +++ b/crates/ide-completion/src/completions/mod_.rs @@ -14,7 +14,7 @@ use crate::{CompletionItem, Completions, context::CompletionContext}; /// Complete mod declaration, i.e. `mod $0;` pub(crate) fn complete_mod( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, mod_under_caret: &ast::Module, ) -> Option<()> { if mod_under_caret.item_list().is_some() { diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index e7597bf95c..7b887fb7d7 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -12,7 +12,7 @@ use crate::{ /// Completes constants and paths in unqualified patterns. pub(crate) fn complete_pattern( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, ) { let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet); @@ -128,7 +128,7 @@ pub(crate) fn complete_pattern( pub(crate) fn complete_pattern_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, ) { match qualified { diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 82baf885dd..0cb39dd108 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -12,7 +12,7 @@ use ide_db::{ text_edit::TextEdit, ty_filter::TryEnum, }; -use itertools::Itertools; +use itertools::{Either, Itertools}; use stdx::never; use syntax::{ SmolStr, @@ -31,7 +31,7 @@ use crate::{ pub(crate) fn complete_postfix( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, ) { if !ctx.config.enable_postfix_completions { @@ -84,15 +84,15 @@ pub(crate) fn complete_postfix( let mut item = postfix_snippet( "drop", "fn drop(&mut self)", - &format!("{path}($0{receiver_text})", path = path.display(ctx.db, ctx.edition)), + format!("{path}($0{receiver_text})", path = path.display(ctx.db, ctx.edition)), ); item.set_documentation(drop_fn.docs(ctx.db)); item.add_to(acc, ctx.db); } - postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db); - postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db); - postfix_snippet("deref", "*expr", &format!("*{receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("ref", "&expr", format!("&{receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("refm", "&mut expr", format!("&mut {receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("deref", "*expr", format!("*{receiver_text}")).add_to(acc, ctx.db); // The rest of the postfix completions create an expression that moves an argument, // so it's better to consider references now to avoid breaking the compilation @@ -110,15 +110,47 @@ pub(crate) fn complete_postfix( add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text); } - postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")) + postfix_snippet("box", "Box::new(expr)", format!("Box::new({receiver_text})")) .add_to(acc, ctx.db); - postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme - postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db); - postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")) + postfix_snippet("dbg", "dbg!(expr)", format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme + postfix_snippet("dbgr", "dbg!(&expr)", format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db); + postfix_snippet("call", "function(expr)", format!("${{1}}({receiver_text})")) .add_to(acc, ctx.db); + if let Some(expected_ty) = ctx.expected_type.as_ref() + && let Some(adt) = expected_ty.as_adt() + { + let is_valid_new = expected_ty + .iterate_assoc_items(ctx.db, |item| { + if let hir::AssocItem::Function(func) = item + && func.name(ctx.db) == hir::sym::new + && !func.has_self_param(ctx.db) + { + let params = func.params_without_self(ctx.db); + if params.len() == 1 { + return Some(()); + } + } + None + }) + .is_some(); + + let adt = hir::ModuleDef::from(adt); + if is_valid_new && let Some(path) = ctx.module.find_path(ctx.db, adt, cfg) { + let ty_name = path.display(ctx.db, ctx.display_target.edition).to_smolstr(); + + postfix_snippet( + "new", + &format_smolstr!("{}::new(expr)", ty_name), + format!("{}::new({}$0)", ty_name, receiver_text), + ) + .add_to(acc, ctx.db); + } + } + let try_enum = TryEnum::from_ty(&ctx.sema, receiver_ty); let is_in_cond = is_in_condition(&dot_receiver_including_refs); + let is_in_value = is_in_value(&dot_receiver_including_refs); if let Some(parent) = dot_receiver_including_refs.syntax().parent() { let placeholder = suggest_receiver_name(dot_receiver, "0", &ctx.sema); match &try_enum { @@ -127,13 +159,13 @@ pub(crate) fn complete_postfix( postfix_snippet( "let", "let Ok(_)", - &format!("let Ok({placeholder}) = {receiver_text}"), + format!("let Ok({placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); postfix_snippet( "letm", "let Ok(mut _)", - &format!("let Ok(mut {placeholder}) = {receiver_text}"), + format!("let Ok(mut {placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); } @@ -141,38 +173,38 @@ pub(crate) fn complete_postfix( postfix_snippet( "let", "let Some(_)", - &format!("let Some({placeholder}) = {receiver_text}"), + format!("let Some({placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); postfix_snippet( "letm", "let Some(mut _)", - &format!("let Some(mut {placeholder}) = {receiver_text}"), + format!("let Some(mut {placeholder}) = {receiver_text}"), ) .add_to(acc, ctx.db); } }, _ if is_in_cond => { - postfix_snippet("let", "let", &format!("let $1 = {receiver_text}")) + postfix_snippet("let", "let", format!("let $1 = {receiver_text}")) .add_to(acc, ctx.db); } _ if matches!(parent.kind(), STMT_LIST | EXPR_STMT) => { - postfix_snippet("let", "let", &format!("let $0 = {receiver_text}{semi}")) + postfix_snippet("let", "let", format!("let $0 = {receiver_text}{semi}")) .add_to(acc, ctx.db); - postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text}{semi}")) + postfix_snippet("letm", "let mut", format!("let mut $0 = {receiver_text}{semi}")) .add_to(acc, ctx.db); } _ if matches!(parent.kind(), MATCH_ARM | CLOSURE_EXPR) => { postfix_snippet( "let", "let", - &format!("{{\n let $1 = {receiver_text};\n $0\n}}"), + format!("{{\n let $1 = {receiver_text};\n $0\n}}"), ) .add_to(acc, ctx.db); postfix_snippet( "letm", "let mut", - &format!("{{\n let mut $1 = {receiver_text};\n $0\n}}"), + format!("{{\n let mut $1 = {receiver_text};\n $0\n}}"), ) .add_to(acc, ctx.db); } @@ -187,7 +219,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "match", "match expr {}", - &format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"), + format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"), ) .add_to(acc, ctx.db); } @@ -195,7 +227,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "match", "match expr {}", - &format!( + format!( "match {receiver_text} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}" ), ) @@ -206,35 +238,35 @@ pub(crate) fn complete_postfix( postfix_snippet( "match", "match expr {}", - &format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"), + format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"), ) .add_to(acc, ctx.db); } } if let Some(try_enum) = &try_enum { let placeholder = suggest_receiver_name(dot_receiver, "1", &ctx.sema); + let if_then_snip = + if is_in_value { "{\n $2\n} else {\n $0\n}" } else { "{\n $0\n}" }; match try_enum { TryEnum::Result => { postfix_snippet( "ifl", "if let Ok {}", - &format!("if let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"), + format!("if let Ok({placeholder}) = {receiver_text} {if_then_snip}"), ) .add_to(acc, ctx.db); postfix_snippet( "lete", "let Ok else {}", - &format!( - "let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0" - ), + format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0"), ) .add_to(acc, ctx.db); postfix_snippet( "while", "while let Ok {}", - &format!("while let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"), + format!("while let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); } @@ -242,14 +274,14 @@ pub(crate) fn complete_postfix( postfix_snippet( "ifl", "if let Some {}", - &format!("if let Some({placeholder}) = {receiver_text} {{\n $0\n}}"), + format!("if let Some({placeholder}) = {receiver_text} {if_then_snip}"), ) .add_to(acc, ctx.db); postfix_snippet( "lete", "let Some else {}", - &format!( + format!( "let Some({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0" ), ) @@ -258,18 +290,20 @@ pub(crate) fn complete_postfix( postfix_snippet( "while", "while let Some {}", - &format!("while let Some({placeholder}) = {receiver_text} {{\n $0\n}}"), + format!("while let Some({placeholder}) = {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); } } } else if receiver_ty.is_bool() || receiver_ty.is_unknown() { - postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n $0\n}}")) + let if_then_snip = + if is_in_value { "{\n $1\n} else {\n $0\n}" } else { "{\n $0\n}" }; + postfix_snippet("if", "if expr {}", format!("if {receiver_text} {if_then_snip}")) .add_to(acc, ctx.db); postfix_snippet( "while", "while expr {}", - &format!("while {receiver_text} {{\n $0\n}}"), + format!("while {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() @@ -278,14 +312,14 @@ pub(crate) fn complete_postfix( postfix_snippet( "for", "for ele in expr {}", - &format!("for ele in {receiver_text} {{\n $0\n}}"), + format!("for ele in {receiver_text} {{\n $0\n}}"), ) .add_to(acc, ctx.db); } } if receiver_ty.is_bool() || receiver_ty.is_unknown() { - postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("not", "!expr", format!("!{receiver_text}")).add_to(acc, ctx.db); } let block_should_be_wrapped = if let ast::Expr::BlockExpr(block) = dot_receiver { @@ -300,11 +334,11 @@ pub(crate) fn complete_postfix( let (open_paren, close_paren) = if is_in_cond { ("(", ")") } else { ("", "") }; let unsafe_completion_string = format!("{open_paren}unsafe {open_brace}{receiver_text}{close_brace}{close_paren}"); - postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db); + postfix_snippet("unsafe", "unsafe {}", unsafe_completion_string).add_to(acc, ctx.db); let const_completion_string = format!("{open_paren}const {open_brace}{receiver_text}{close_brace}{close_paren}"); - postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db); + postfix_snippet("const", "const {}", const_completion_string).add_to(acc, ctx.db); } if let ast::Expr::Literal(literal) = dot_receiver.clone() @@ -313,11 +347,11 @@ pub(crate) fn complete_postfix( add_format_like_completions(acc, ctx, dot_receiver, cap, &literal_text, semi); } - postfix_snippet("return", "return expr", &format!("return {receiver_text}{semi}")) + postfix_snippet("return", "return expr", format!("return {receiver_text}{semi}")) .add_to(acc, ctx.db); if let Some(BreakableKind::Block | BreakableKind::Loop) = expr_ctx.in_breakable { - postfix_snippet("break", "break expr", &format!("break {receiver_text}{semi}")) + postfix_snippet("break", "break expr", format!("break {receiver_text}{semi}")) .add_to(acc, ctx.db); } } @@ -364,7 +398,7 @@ fn get_receiver_text( } let file_text = sema.db.file_text(range.file_id.file_id(sema.db)); let text = file_text.text(sema.db); - let indent_spaces = indent_of_tail_line(&text[TextRange::up_to(range.range.start())]); + let indent_spaces = indent_of_tail_line(&text[TextRange::up_to(range.range.end())]); let mut text = stdx::dedent_by(indent_spaces, &text[range.range]); // The receiver texts should be interpreted as-is, as they are expected to be @@ -434,6 +468,11 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) { .syntax() .children_with_tokens() .filter(|it| Some(it) != last_child_or_token.as_ref()) + .flat_map(|it| { + let has_ws = it.next_sibling_or_token().is_some_and(|it| it.kind().is_trivia()); + let need_ws = !has_ws && it.kind().is_any_identifier(); + itertools::chain([Either::Left(it)], need_ws.then_some(Either::Right(" "))) + }) .format("") .to_smolstr() .as_str(), @@ -445,10 +484,10 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) { } fn build_postfix_snippet_builder<'ctx>( - ctx: &'ctx CompletionContext<'_>, + ctx: &'ctx CompletionContext<'_, '_>, cap: SnippetCap, receiver: &'ctx ast::Expr, -) -> Option<impl Fn(&str, &str, &str) -> Builder + 'ctx> { +) -> Option<impl Fn(&str, &str, String) -> Builder + 'ctx> { let receiver_range = ctx.sema.original_range_opt(receiver.syntax())?.range; 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 @@ -461,12 +500,12 @@ fn build_postfix_snippet_builder<'ctx>( // Wrapping impl Fn in an option ruins lifetime inference for the parameters in a way that // can't be annotated for the closure, hence fix it by constructing it without the Option first fn build<'ctx>( - ctx: &'ctx CompletionContext<'_>, + ctx: &'ctx CompletionContext<'_, '_>, cap: SnippetCap, delete_range: TextRange, - ) -> impl Fn(&str, &str, &str) -> Builder + 'ctx { + ) -> impl Fn(&str, &str, String) -> Builder + 'ctx { move |label, detail, snippet| { - let edit = TextEdit::replace(delete_range, snippet.to_owned()); + let edit = TextEdit::replace(delete_range, snippet); let mut item = CompletionItem::new( CompletionItemKind::Snippet, ctx.source_range(), @@ -491,8 +530,8 @@ fn build_postfix_snippet_builder<'ctx>( fn add_custom_postfix_completions( acc: &mut Completions, - ctx: &CompletionContext<'_>, - postfix_snippet: impl Fn(&str, &str, &str) -> Builder, + ctx: &CompletionContext<'_, '_>, + postfix_snippet: impl Fn(&str, &str, String) -> Builder, receiver_text: &str, ) -> Option<()> { ImportScope::find_insert_use_container(&ctx.token.parent()?, &ctx.sema)?; @@ -503,9 +542,10 @@ fn add_custom_postfix_completions( None => return, }; let body = snippet.postfix_snippet(receiver_text); + let document = Documentation::new_owned(format!("```rust\n{body}\n```")); let mut builder = - postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), &body); - builder.documentation(Documentation::new_owned(format!("```rust\n{body}\n```"))); + postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), body); + builder.documentation(document); for import in imports.into_iter() { builder.add_import(import); } @@ -533,6 +573,22 @@ pub(crate) fn is_in_condition(it: &ast::Expr) -> bool { .unwrap_or(false) } +pub(crate) fn is_in_value(it: &ast::Expr) -> bool { + let Some(node) = it.syntax().parent() else { return false }; + let kind = node.kind(); + ast::LetStmt::can_cast(kind) + || ast::ArgList::can_cast(kind) + || ast::ArrayExpr::can_cast(kind) + || ast::ParenExpr::can_cast(kind) + || ast::BreakExpr::can_cast(kind) + || ast::ReturnExpr::can_cast(kind) + || ast::PrefixExpr::can_cast(kind) + || ast::FormatArgsArg::can_cast(kind) + || ast::RecordExprField::can_cast(kind) + || ast::BinExpr::cast(node.clone()).is_some_and(|expr| expr.rhs().as_ref() == Some(it)) + || ast::IndexExpr::cast(node).is_some_and(|expr| expr.index().as_ref() == Some(it)) +} + #[cfg(test)] mod tests { use expect_test::expect; @@ -1148,6 +1204,66 @@ fn main() { } #[test] + fn postfix_completion_if_else_in_value() { + check_edit( + "if", + r#" +fn main() { + let s = cond.is_some().$0; +} +"#, + r#" +fn main() { + let s = if cond.is_some() { + $1 +} else { + $0 +}; +} +"#, + ); + + check_edit( + "ifl", + r#" +//- minicore: option +fn main() { + let cond = Some("x"); + let s = cond.$0; +} +"#, + r#" +fn main() { + let cond = Some("x"); + let s = if let Some(${1:cond}) = cond { + $2 +} else { + $0 +}; +} +"#, + ); + + check_edit( + "if", + r#" +fn main() { + 2 + true.$0; +} +"#, + r#" +fn main() { + 2 + if true { + $1 +} else { + $0 +}; +} +"#, + ); + } + + #[test] fn postfix_completion_for_unsafe() { postfix_completion_for_block("unsafe"); } @@ -1186,7 +1302,9 @@ fn main() { ); check_edit( kind, - r#"fn main() { for i in 0..10 {}.$0 }"#, + r#" +//- minicore: iterator +fn main() { for i in 0..10 {}.$0 }"#, &format!("fn main() {{ {kind} {{ for i in 0..10 {{}} }} }}"), ); check_edit( @@ -1456,6 +1574,15 @@ fn main() { r#"fn main() { &raw const Foo::bar::SOME_CONST.$0 }"#, r#"fn main() { (&raw const Foo::bar::SOME_CONST) }"#, ); + + check_edit_with_config( + CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG }, + "group", + r#"macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn main() { id!(&raw const Foo::bar::SOME_CONST.$0) }"#, + r#"macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn main() { id!((&raw const Foo::bar::SOME_CONST)) }"#, + ); } #[test] @@ -1545,7 +1672,9 @@ fn foo(x: Option<i32>, y: Option<i32>) { let _f = || { x .and(y) - .map(|it| it+2) + .map(|it| { + it+2 + }) .$0 }; } @@ -1554,11 +1683,74 @@ fn foo(x: Option<i32>, y: Option<i32>) { fn foo(x: Option<i32>, y: Option<i32>) { let _f = || { let $0 = x - .and(y) - .map(|it| it+2); +.and(y) +.map(|it| { + it+2 +}); }; } "#, ); } + + #[test] + fn postfix_new() { + check_edit( + "new", + r#" +struct OtherThing; +struct RefCell<T>(T); +impl<T> RefCell<T> { + fn new(t: T) -> Self { RefCell(t) } +} + +fn main() { + let other_thing = OtherThing; + let thing: RefCell<OtherThing> = other_thing.$0; +} +"#, + r#" +struct OtherThing; +struct RefCell<T>(T); +impl<T> RefCell<T> { + fn new(t: T) -> Self { RefCell(t) } +} + +fn main() { + let other_thing = OtherThing; + let thing: RefCell<OtherThing> = RefCell::new(other_thing$0); +} +"#, + ); + + check_edit( + "new", + r#" +mod foo { + pub struct OtherThing; + pub struct RefCell<T>(T); + impl<T> RefCell<T> { + pub fn new(t: T) -> Self { RefCell(t) } + } +} + +fn main() { + let thing: foo::RefCell<foo::OtherThing> = foo::OtherThing.$0; +} +"#, + r#" +mod foo { + pub struct OtherThing; + pub struct RefCell<T>(T); + impl<T> RefCell<T> { + pub fn new(t: T) -> Self { RefCell(t) } + } +} + +fn main() { + let thing: foo::RefCell<foo::OtherThing> = foo::RefCell::new(foo::OtherThing$0); +} +"#, + ); + } } diff --git a/crates/ide-completion/src/completions/postfix/format_like.rs b/crates/ide-completion/src/completions/postfix/format_like.rs index 85a8899fd1..3b22e8a266 100644 --- a/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/crates/ide-completion/src/completions/postfix/format_like.rs @@ -44,7 +44,7 @@ static SNIPPET_RETURNS_NON_UNIT: &[&str] = &["format"]; pub(crate) fn add_format_like_completions( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_receiver: &ast::Expr, cap: SnippetCap, receiver_text: &ast::String, @@ -73,7 +73,7 @@ pub(crate) fn add_format_like_completions( format!(r#"{}({}, {}){semi}"#, macro_name, out, exprs.join(", ")) }; - postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db); + postfix_snippet(label, macro_name, snippet).add_to(acc, ctx.db); } } } diff --git a/crates/ide-completion/src/completions/ra_fixture.rs b/crates/ide-completion/src/completions/ra_fixture.rs index 5a8881edc7..08ad37b7f2 100644 --- a/crates/ide-completion/src/completions/ra_fixture.rs +++ b/crates/ide-completion/src/completions/ra_fixture.rs @@ -14,7 +14,7 @@ use crate::{ pub(crate) fn complete_ra_fixture( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, original: &ast::String, expanded: &ast::String, ) -> Option<()> { diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index 12c564af5c..1238a91dad 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -13,7 +13,7 @@ use crate::{ pub(crate) fn complete_record_pattern_fields( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, pattern_ctx: &PatternContext, ) { if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx { @@ -44,7 +44,7 @@ pub(crate) fn complete_record_pattern_fields( pub(crate) fn complete_record_expr_fields( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, record_expr: &ast::RecordExpr, &dot_prefix: &bool, ) { @@ -98,7 +98,7 @@ pub(crate) fn complete_record_expr_fields( pub(crate) fn add_default_update( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, ty: Option<&hir::TypeInfo<'_>>, ) { let default_trait = ctx.famous_defs().core_default_Default(); @@ -127,7 +127,7 @@ pub(crate) fn add_default_update( fn complete_fields( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, missing_fields: Vec<(hir::Field, hir::Type<'_>)>, ) { for (field, ty) in missing_fields { diff --git a/crates/ide-completion/src/completions/snippet.rs b/crates/ide-completion/src/completions/snippet.rs index 04450aea75..7432c5226b 100644 --- a/crates/ide-completion/src/completions/snippet.rs +++ b/crates/ide-completion/src/completions/snippet.rs @@ -10,7 +10,7 @@ use crate::{ pub(crate) fn complete_expr_snippet( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, &PathExprCtx { in_block_expr, .. }: &PathExprCtx<'_>, ) { @@ -50,7 +50,7 @@ macro_rules! $1 { pub(crate) fn complete_item_snippet( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, kind: &ItemListKind, ) { @@ -117,7 +117,12 @@ macro_rules! $1 { } } -fn snippet(ctx: &CompletionContext<'_>, cap: SnippetCap, label: &str, snippet: &str) -> Builder { +fn snippet( + ctx: &CompletionContext<'_, '_>, + cap: SnippetCap, + label: &str, + snippet: &str, +) -> Builder { let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label, ctx.edition); item.insert_snippet(cap, snippet); @@ -126,7 +131,7 @@ fn snippet(ctx: &CompletionContext<'_>, cap: SnippetCap, label: &str, snippet: & fn add_custom_completions( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, cap: SnippetCap, scope: SnippetScope, ) -> Option<()> { diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 20bbf0dd8b..0b2b6682aa 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -11,7 +11,7 @@ use crate::{ pub(crate) fn complete_type_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, location: &TypeLocation, ) { @@ -217,7 +217,7 @@ pub(crate) fn complete_type_path( pub(crate) fn complete_ascribed_type( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ascription: &TypeAscriptionTarget, ) -> Option<()> { diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs index f39b641649..1ff7dd6def 100644 --- a/crates/ide-completion/src/completions/use_.rs +++ b/crates/ide-completion/src/completions/use_.rs @@ -12,7 +12,7 @@ use crate::{ pub(crate) fn complete_use_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx<'_>, name_ref: &Option<ast::NameRef>, ) { diff --git a/crates/ide-completion/src/completions/vis.rs b/crates/ide-completion/src/completions/vis.rs index 28d906d91c..49a52f2986 100644 --- a/crates/ide-completion/src/completions/vis.rs +++ b/crates/ide-completion/src/completions/vis.rs @@ -7,7 +7,7 @@ use crate::{ pub(crate) fn complete_vis_path( acc: &mut Completions, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>, &has_in_token: &bool, ) { diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs index 80c1572972..21f624be2c 100644 --- a/crates/ide-completion/src/config.rs +++ b/crates/ide-completion/src/config.rs @@ -25,6 +25,7 @@ pub struct CompletionConfig<'a> { pub term_search_fuel: u64, pub full_function_signatures: bool, pub callable: Option<CallableSnippets>, + pub add_colons_to_module: bool, pub add_semicolon_to_unit: bool, pub snippet_cap: Option<SnippetCap>, pub insert_use: InsertUseConfig, diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index b9520e9132..f7fced3f06 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -458,10 +458,10 @@ pub(crate) enum ParamKind { /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. #[derive(Debug)] -pub(crate) struct CompletionContext<'a> { - pub(crate) sema: Semantics<'a, RootDatabase>, - pub(crate) scope: SemanticsScope<'a>, - pub(crate) db: &'a RootDatabase, +pub(crate) struct CompletionContext<'a, 'db> { + pub(crate) sema: Semantics<'db, RootDatabase>, + pub(crate) scope: SemanticsScope<'db>, + pub(crate) db: &'db RootDatabase, pub(crate) config: &'a CompletionConfig<'a>, pub(crate) position: FilePosition, @@ -487,7 +487,7 @@ pub(crate) struct CompletionContext<'a> { /// This is usually the parameter name of the function argument we are completing. pub(crate) expected_name: Option<NameOrNameRef>, /// The expected type of what we are completing. - pub(crate) expected_type: Option<Type<'a>>, + pub(crate) expected_type: Option<Type<'db>>, pub(crate) qualifier_ctx: QualifierCtx, @@ -523,7 +523,7 @@ pub(crate) enum CompleteSemicolon { CompleteComma, } -impl CompletionContext<'_> { +impl<'db> CompletionContext<'_, 'db> { /// The range of the identifier that is being completed. pub(crate) fn source_range(&self) -> TextRange { let kind = self.original_token.kind(); @@ -540,7 +540,7 @@ impl CompletionContext<'_> { } } - pub(crate) fn famous_defs(&self) -> FamousDefs<'_, '_> { + pub(crate) fn famous_defs(&self) -> FamousDefs<'_, 'db> { FamousDefs(&self.sema, self.krate) } @@ -732,13 +732,13 @@ impl CompletionContext<'_> { } // CompletionContext construction -impl<'db> CompletionContext<'db> { +impl<'a, 'db> CompletionContext<'a, 'db> { pub(crate) fn new( db: &'db RootDatabase, position @ FilePosition { file_id, offset }: FilePosition, - config: &'db CompletionConfig<'db>, + config: &'a CompletionConfig<'a>, trigger_character: Option<char>, - ) -> Option<(CompletionContext<'db>, CompletionAnalysis<'db>)> { + ) -> Option<(CompletionContext<'a, 'db>, CompletionAnalysis<'db>)> { let _p = tracing::info_span!("CompletionContext::new").entered(); let sema = Semantics::new(db); diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 58c0f683a3..faeb97f93f 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -22,7 +22,7 @@ use syntax::{ }; use crate::{ - completions::postfix::is_in_condition, + completions::postfix::{is_in_condition, is_in_value}, context::{ AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx, DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, @@ -1097,25 +1097,6 @@ fn classify_name_ref<'db>( .and_then(|next| next.first_token()) .is_some_and(|token| token.kind() == SyntaxKind::ELSE_KW) }; - let is_in_value = |it: &SyntaxNode| { - let Some(node) = it.parent() else { return false }; - let kind = node.kind(); - ast::LetStmt::can_cast(kind) - || ast::ArgList::can_cast(kind) - || ast::ArrayExpr::can_cast(kind) - || ast::ParenExpr::can_cast(kind) - || ast::BreakExpr::can_cast(kind) - || ast::ReturnExpr::can_cast(kind) - || ast::PrefixExpr::can_cast(kind) - || ast::FormatArgsArg::can_cast(kind) - || ast::RecordExprField::can_cast(kind) - || ast::BinExpr::cast(node.clone()) - .and_then(|expr| expr.rhs()) - .is_some_and(|expr| expr.syntax() == it) - || ast::IndexExpr::cast(node) - .and_then(|expr| expr.index()) - .is_some_and(|expr| expr.syntax() == it) - }; // We do not want to generate path completions when we are sandwiched between an item decl signature and its body. // ex. trait Foo $0 {} @@ -1429,7 +1410,7 @@ fn classify_name_ref<'db>( .find_map(ast::LetStmt::cast) .is_some_and(|it| it.semicolon_token().is_none()) || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw; - let in_value = is_in_value(it); + let in_value = is_in_value(&expr); let impl_ = fetch_immediate_impl_or_trait(sema, original_file, expr.syntax()) .and_then(Either::left); @@ -2094,12 +2075,12 @@ fn next_non_trivia_token(e: impl Into<SyntaxElement>) -> Option<SyntaxToken> { } fn next_non_trivia_sibling(ele: SyntaxElement) -> Option<SyntaxElement> { - let mut e = ele.next_sibling_or_token(); - while let Some(inner) = e { - if !inner.kind().is_trivia() { - return Some(inner); + let mut e = ele; + while let Some(next) = e.next_sibling_or_token() { + if !next.kind().is_trivia() { + return Some(next); } else { - e = inner.next_sibling_or_token(); + e = next; } } None diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index 6abf4f632a..cfadec6287 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -61,6 +61,9 @@ pub struct CompletionItem { pub documentation: Option<Documentation<'static>>, /// Whether this item is marked as deprecated + /// + /// NOTE: this field is used in the LSP protocol. For the use of this information in completion + /// scoring, see [`CompletionRelevance::is_deprecated`]. pub deprecated: bool, /// If completing a function call, ask the editor to show parameter popup @@ -194,6 +197,11 @@ pub struct CompletionRelevance { pub is_skipping_completion: bool, /// if inherent impl already exists in current module, user may not want to implement it again. pub has_local_inherent_impl: bool, + /// Set when the completion item is deprecated. + /// + /// NOTE: This is duplicated from [`CompletionItem::deprecated`] in order to allow using this + /// information in the calculation of the relevance score. + pub is_deprecated: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct CompletionRelevanceTraitInfo { @@ -286,6 +294,7 @@ impl CompletionRelevance { function, is_skipping_completion, has_local_inherent_impl, + is_deprecated, } = self; // only applicable for completions within use items @@ -362,6 +371,11 @@ impl CompletionRelevance { score -= 5; } + // lower rank for deprecated items + if is_deprecated { + score -= 5; + } + score } @@ -521,7 +535,7 @@ pub(crate) struct Builder { impl Builder { pub(crate) fn from_resolution( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, resolution: hir::ScopeDef, @@ -590,6 +604,9 @@ impl Builder { None => TextEdit::replace(self.source_range, insert_text), }; + // Copy `deprecated` to `self.relevance.is_deprecated` + let relevance = CompletionRelevance { is_deprecated: self.deprecated, ..self.relevance }; + let import_to_add = self .imports_to_add .into_iter() @@ -622,7 +639,7 @@ impl Builder { kind: self.kind, deprecated: self.deprecated, trigger_call_info: self.trigger_call_info, - relevance: self.relevance, + relevance, ref_match: self.ref_match, import_to_add, } @@ -693,6 +710,15 @@ impl Builder { self } pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder { + // The default value of `CompletionRelevance.is_deprecated` is `false`, so it being `true` + // would mean it was set manually. Advise using the other function instead. + // + // This is technically not necessary, because `deprecated` will get reconciled in + // `Builder::build` anyway -- it just helps keep the callers consistent. + assert!( + !relevance.is_deprecated, + "`deprecated` should be set using `Builder::set_deprecated` instead" + ); self.relevance = relevance; self } @@ -727,9 +753,25 @@ mod tests { use test_utils::assert_eq_text; use super::{ - CompletionRelevance, CompletionRelevancePostfixMatch, CompletionRelevanceTypeMatch, + CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch, + CompletionRelevanceTypeMatch, }; + #[test] + fn builder_deprecated_from_set_deprecated() { + // setting just `item.deprecated` also sets `item.relevance.is_deprecated` + let mut builder = CompletionItem::new( + CompletionItemKind::Expression, + Default::default(), + "", + syntax::Edition::DEFAULT, + ); + builder.set_deprecated(true); + let item = builder.build(&Default::default()); + assert!(item.deprecated); + assert!(item.relevance.is_deprecated); + } + /// Check that these are CompletionRelevance are sorted in ascending order /// by their relevance score. /// diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 3df511a5ad..4ca3257c5c 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -23,7 +23,7 @@ use ide_db::{ syntax_helpers::tree_diff::diff, text_edit::TextEdit, }; -use syntax::ast::make; +use syntax::{AstNode, syntax_editor::SyntaxEditor}; use crate::{ completions::Completions, @@ -258,7 +258,7 @@ pub fn completions( completions::attribute::complete_known_attribute_input( acc, ctx, - colon_prefix, + *colon_prefix, attr, extern_crate.as_ref(), ); @@ -296,23 +296,26 @@ pub fn resolve_completion_edits( let current_module = sema.scope(position_for_import)?.module(); let current_crate = current_module.krate(db); let current_edition = current_crate.edition(db); - let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); + let (editor, _) = SyntaxEditor::new(original_file.syntax().clone()); + let make = editor.make(); imports.into_iter().for_each(|import| { - let full_path = make::path_from_text_with_edition(&import.path, current_edition); + let full_path = make.path_from_text_with_edition(&import.path, current_edition); if import.as_underscore { - insert_use::insert_use_as_alias( - &new_ast, + insert_use::insert_use_as_alias_with_editor( + &scope, full_path, &config.insert_use, current_edition, + &editor, ); } else { - insert_use::insert_use(&new_ast, full_path, &config.insert_use); + insert_use::insert_use_with_editor(&scope, full_path, &config.insert_use, &editor); } }); - diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert); + let edit = editor.finish(); + diff(edit.old_root(), edit.new_root()).into_text_edit(&mut import_insert); Some(vec![import_insert.finish()]) } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index a636c0603b..fbbdffefe3 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -35,15 +35,15 @@ use crate::{ }; /// Interface for data and methods required for items rendering. #[derive(Debug, Clone)] -pub(crate) struct RenderContext<'a> { - completion: &'a CompletionContext<'a>, +pub(crate) struct RenderContext<'a, 'db> { + completion: &'a CompletionContext<'a, 'db>, is_private_editable: bool, import_to_add: Option<LocatedImport>, doc_aliases: Vec<SmolStr>, } -impl<'a> RenderContext<'a> { - pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> { +impl<'a, 'db> RenderContext<'a, 'db> { + pub(crate) fn new(completion: &'a CompletionContext<'a, 'db>) -> RenderContext<'a, 'db> { RenderContext { completion, is_private_editable: false, @@ -120,6 +120,15 @@ impl<'a> RenderContext<'a> { }) } + /// Whether an enum variant should be rendered as deprecated. + /// + /// A variant inherits deprecation from its parent enum, matching rustc's + /// behavior where `#[deprecated]` on an enum applies to its variants. + fn is_variant_deprecated(&self, variant: hir::EnumVariant) -> bool { + let db = self.db(); + variant.attrs(db).is_deprecated() || variant.parent_enum(db).attrs(db).is_deprecated() + } + // FIXME: remove this fn docs(&self, def: impl HasDocs) -> Option<Documentation<'a>> { def.docs(self.db()) @@ -127,7 +136,7 @@ impl<'a> RenderContext<'a> { } pub(crate) fn render_field( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, dot_access: &DotAccess<'_>, receiver: Option<SmolStr>, field: hir::Field, @@ -204,7 +213,7 @@ fn field_with_receiver(receiver: Option<&str>, field_name: &str) -> SmolStr { } pub(crate) fn render_tuple_field( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, receiver: Option<SmolStr>, field: usize, ty: &hir::Type<'_>, @@ -226,7 +235,7 @@ pub(crate) fn render_tuple_field( pub(crate) fn render_type_inference( ty_string: String, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ) -> CompletionItem { let mut builder = CompletionItem::new( @@ -245,7 +254,7 @@ pub(crate) fn render_type_inference( } pub(crate) fn render_path_resolution( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, resolution: ScopeDef, @@ -254,7 +263,7 @@ pub(crate) fn render_path_resolution( } pub(crate) fn render_pattern_resolution( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, pattern_ctx: &PatternContext, local_name: hir::Name, resolution: ScopeDef, @@ -263,7 +272,7 @@ pub(crate) fn render_pattern_resolution( } pub(crate) fn render_resolution_with_import( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, import_edit: LocatedImport, ) -> Option<Builder> { @@ -276,7 +285,7 @@ pub(crate) fn render_resolution_with_import( } pub(crate) fn render_resolution_with_import_pat( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, pattern_ctx: &PatternContext, import_edit: LocatedImport, ) -> Option<Builder> { @@ -286,7 +295,7 @@ pub(crate) fn render_resolution_with_import_pat( } pub(crate) fn render_expr( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, expr: &hir::term_search::Expr<'_>, ) -> Option<Builder> { let mut i = 1; @@ -349,7 +358,7 @@ pub(crate) fn render_expr( fn get_import_name( resolution: ScopeDef, - ctx: &RenderContext<'_>, + ctx: &RenderContext<'_, '_>, import_edit: &LocatedImport, ) -> Option<hir::Name> { // FIXME: Temporary workaround for handling aliased import. @@ -367,7 +376,7 @@ fn get_import_name( fn scope_def_to_name( resolution: ScopeDef, - ctx: &RenderContext<'_>, + ctx: &RenderContext<'_, '_>, import_edit: &LocatedImport, ) -> Option<hir::Name> { Some(match resolution { @@ -379,7 +388,7 @@ fn scope_def_to_name( } fn render_resolution_pat( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, pattern_ctx: &PatternContext, local_name: hir::Name, import_to_add: Option<LocatedImport>, @@ -397,7 +406,7 @@ fn render_resolution_pat( } fn render_resolution_path( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, import_to_add: Option<LocatedImport>, @@ -462,6 +471,11 @@ fn render_resolution_path( .insert_snippet(cap, ""); // set is snippet } } + let allow_module_path = matches!(path_ctx.kind, PathKind::Use) || !config.add_colons_to_module; + if !allow_module_path && matches!(resolution, ScopeDef::ModuleDef(Module(_))) { + insert_text = format_smolstr!("{insert_text}::"); + item.lookup_by(name.clone()).label(insert_text.clone()); + } adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into()); let mut set_item_relevance = |ty: Type<'_>| { @@ -506,7 +520,7 @@ fn render_resolution_path( } fn render_resolution_simple_( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, local_name: &hir::Name, import_to_add: Option<LocatedImport>, resolution: ScopeDef, @@ -580,9 +594,10 @@ fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<Documentati } } -fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> bool { +fn scope_def_is_deprecated(ctx: &RenderContext<'_, '_>, resolution: ScopeDef) -> bool { let db = ctx.db(); match resolution { + ScopeDef::ModuleDef(hir::ModuleDef::EnumVariant(it)) => ctx.is_variant_deprecated(it), ScopeDef::ModuleDef(it) => ctx.is_deprecated(it, it.as_assoc_item(db)), ScopeDef::GenericParam(it) => { ctx.is_deprecated(it, None /* generic params can't be assoc items */) @@ -595,7 +610,7 @@ fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> boo } pub(crate) fn render_type_keyword_snippet( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, label: &str, snippet: &str, @@ -619,7 +634,7 @@ pub(crate) fn render_type_keyword_snippet( } fn adds_ret_type_arrow( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, item: &mut Builder, insert_text: String, @@ -638,7 +653,7 @@ fn adds_ret_type_arrow( // FIXME: This checks types without possible coercions which some completions might want to do fn match_types( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, ty1: &hir::Type<'_>, ty2: &hir::Type<'_>, ) -> Option<CompletionRelevanceTypeMatch> { @@ -652,7 +667,7 @@ fn match_types( } fn compute_type_match( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option<CompletionRelevanceTypeMatch> { let expected_type = ctx.expected_type.as_ref()?; @@ -686,12 +701,12 @@ fn compute_has_local_inherent_impl( .any(|imp| imp.trait_(db).is_none() && imp.module(db) == curr_module) } -fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool { +fn compute_exact_name_match(ctx: &CompletionContext<'_, '_>, completion_name: &str) -> bool { ctx.expected_name.as_ref().is_some_and(|name| name.text() == completion_name) } fn compute_ref_match( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option<CompletionItemRefMode> { let expected_type = ctx.expected_type.as_ref()?; @@ -703,7 +718,9 @@ fn compute_ref_match( if let Some(expected_without_ref) = &expected_without_ref && (completion_without_ref.is_none() || completion_ty.could_unify_with(ctx.db, expected_without_ref)) - && completion_ty.autoderef(ctx.db).any(|ty| ty == *expected_without_ref) + && completion_ty + .autoderef(ctx.db) + .any(|ty| ty.could_unify_with(ctx.db, expected_without_ref)) { cov_mark::hit!(suggest_ref); let mutability = if expected_type.is_mutable_reference() { @@ -726,7 +743,7 @@ fn compute_ref_match( } fn path_ref_match( - completion: &CompletionContext<'_>, + completion: &CompletionContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, ty: &hir::Type<'_>, item: &mut Builder, @@ -828,7 +845,7 @@ mod tests { items.push(format!( "{tag} {} {} {relevance}\n", it.label.primary, - it.label.detail_right.clone().unwrap_or_default(), + it.label.detail_right.as_deref().unwrap_or_default(), )); if let Some((label, _indel, relevance)) = it.ref_match() { @@ -844,24 +861,33 @@ mod tests { expect.assert_eq(&actual); fn display_relevance(relevance: CompletionRelevance) -> String { - let relevance_factors = vec![ - (relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"), - ( - relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), - "type_could_unify", - ), - (relevance.exact_name_match, "name"), - (relevance.is_local, "local"), - ( - relevance.postfix_match == Some(CompletionRelevancePostfixMatch::Exact), - "snippet", - ), - (relevance.trait_.is_some_and(|it| it.is_op_method), "op_method"), - (relevance.requires_import, "requires_import"), - (relevance.has_local_inherent_impl, "has_local_inherent_impl"), + let CompletionRelevance { + exact_name_match, + type_match, + is_local, + trait_, + is_name_already_imported: _, + requires_import, + is_private_editable: _, + postfix_match, + function: _, + is_skipping_completion: _, + has_local_inherent_impl, + is_deprecated, + } = relevance; + let relevance_factors = [ + (type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"), + (type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), "type_could_unify"), + (exact_name_match, "name"), + (is_local, "local"), + (postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet"), + (trait_.is_some_and(|it| it.is_op_method), "op_method"), + (requires_import, "requires_import"), + (has_local_inherent_impl, "has_local_inherent_impl"), + (is_deprecated, "deprecated"), ] .into_iter() - .filter_map(|(cond, desc)| if cond { Some(desc) } else { None }) + .filter_map(|(cond, desc)| cond.then_some(desc)) .join("+"); format!("[{relevance_factors}]") @@ -869,6 +895,33 @@ mod tests { } #[test] + fn trait_imported_as_underscore_should_not_appear_auto_import_again() { + // make sure there has no `requires_import` + // see https://github.com/rust-lang/rust-analyzer/issues/19767 + check_relevance( + r#" +//- /dep.rs crate:dep +pub trait MyTrait { + fn my_method(&self); +} + +//- /main.rs crate:main deps:dep +use dep::MyTrait as _; +struct MyStruct; +impl dep::MyTrait for MyStruct { + fn my_method(&self) {} +} +fn main() { + MyStruct::my_method$0 +} +"#, + expect![[r#" + me my_method(…) fn(&self) [] + "#]], + ); + } + + #[test] fn set_struct_type_completion_info() { check_relevance( r#" @@ -894,7 +947,7 @@ fn main() { st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type_could_unify] ex dep::test_mod_b::Struct { } [type_could_unify] st Struct Struct [type_could_unify+requires_import] - md dep [] + md dep:: [] fn main() fn() [] fn test(…) fn(Struct) [] st Struct Struct [requires_import] @@ -932,7 +985,7 @@ fn main() { "#, expect![[r#" un Union Union [type_could_unify+requires_import] - md dep [] + md dep:: [] fn main() fn() [] fn test(…) fn(Union) [] en Union Union [requires_import] @@ -970,7 +1023,7 @@ fn main() { ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type_could_unify] ex dep::test_mod_b::Enum::variant [type_could_unify] en Enum Enum [type_could_unify+requires_import] - md dep [] + md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] en Enum Enum [requires_import] @@ -1007,7 +1060,7 @@ fn main() { expect![[r#" ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify] ex dep::test_mod_b::Enum::Variant [type_could_unify] - md dep [] + md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] "#]], @@ -1037,7 +1090,7 @@ fn main() { } "#, expect![[r#" - md dep [] + md dep:: [] fn main() fn() [] fn test(…) fn(fn(usize) -> i32) [] fn function fn(usize) -> i32 [requires_import] @@ -1070,7 +1123,7 @@ fn main() { "#, expect![[r#" ct CONST i32 [type_could_unify+requires_import] - md dep [] + md dep:: [] fn main() fn() [] fn test(…) fn(i32) [] ct CONST i64 [requires_import] @@ -1102,7 +1155,7 @@ fn main() { "#, expect![[r#" sc STATIC i32 [type_could_unify+requires_import] - md dep [] + md dep:: [] fn main() fn() [] fn test(…) fn(i32) [] sc STATIC i64 [requires_import] @@ -1242,6 +1295,7 @@ fn main() { Foo::Fo$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1293,6 +1347,7 @@ fn main() { Foo::Fo$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1437,6 +1492,7 @@ fn main() { Foo::Fo$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1477,15 +1533,16 @@ fn main() { let _: m::Spam = S$0 } detail: "fn()", }, CompletionItem { - label: "m", + label: "m::", detail_left: None, detail_right: None, source_range: 75..76, delete: 75..76, - insert: "m", + insert: "m::", kind: SymbolKind( Module, ), + lookup: "m", }, CompletionItem { label: "m::Spam::Bar(…)", @@ -1521,6 +1578,7 @@ fn main() { let _: m::Spam = S$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1558,6 +1616,7 @@ fn main() { let _: m::Spam = S$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1579,16 +1638,31 @@ fn main() { som$0 } expect![[r#" [ CompletionItem { - label: "something_deprecated", + label: "something_deprecated::", detail_left: None, detail_right: None, source_range: 55..58, delete: 55..58, - insert: "something_deprecated", + insert: "something_deprecated::", kind: SymbolKind( Module, ), + lookup: "something_deprecated", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1634,6 +1708,20 @@ fn main() { som$0 } lookup: "something_deprecated", detail: "fn()", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1663,6 +1751,20 @@ fn main() { A$0 } ), detail: "A", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1692,6 +1794,20 @@ fn main() { A$0 } ), detail: "A", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1741,6 +1857,7 @@ fn main() { A::$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -1776,6 +1893,7 @@ fn main() { A::$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: true, }, trigger_call_info: true, }, @@ -1807,6 +1925,20 @@ fn main() { A$0 } ), detail: "i32", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1836,6 +1968,20 @@ fn main() { A$0 } ), detail: "i32", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1862,6 +2008,20 @@ impl A$0 Trait, ), deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1888,6 +2048,20 @@ fn main() { A$0 } TypeAlias, ), deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1918,6 +2092,20 @@ fn main() { a$0 } lookup: "a!", detail: "macro_rules! a", deprecated: true, + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: true, + }, }, ] "#]], @@ -1960,6 +2148,7 @@ fn main() { A { the$0 } } function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: true, }, }, ] @@ -2020,6 +2209,7 @@ impl S { ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, CompletionItem { @@ -2112,6 +2302,7 @@ use self::E::*; ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, trigger_call_info: true, }, @@ -2183,6 +2374,7 @@ fn foo(s: S) { s.$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, ] @@ -2396,6 +2588,7 @@ fn f() -> i32 { function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, ] @@ -2502,6 +2695,7 @@ fn main() { function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, ref_match: "&@65", }, @@ -2624,8 +2818,8 @@ mod b { expect![[r#" st Fooa Fooa [] tt Foob [] - md a [] - md b [] + md a:: [] + md b:: [] "#]], ); } @@ -2732,6 +2926,26 @@ fn main() { fn main() fn() [] "#]], ); + check_relevance( + r#" +struct S<T>(T); +fn foo<T>(s: &mut S<T>) {} +fn main() { + let mut ssss = S(2u32); + foo($0); +} + "#, + expect![[r#" + st S(…) S(T) [] + st &mut S(…) [type] + lc ssss S<u32> [local] + lc &mut ssss [type+local] + st S S<{unknown}> [] + st &mut S [type] + fn foo(…) fn(&mut S<T>) [] + fn main() fn() [] + "#]], + ); } #[test] @@ -2763,7 +2977,7 @@ fn main() { tt Clone [] tt Copy [] fn bar(…) fn(Foo) [] - md core [] + md core:: [] fn main() fn() [] "#]], ); @@ -2805,7 +3019,7 @@ fn main() { st &S [type] st T T [] st &T [type] - md core [] + md core:: [] fn foo(…) fn(&S) [] fn main() fn() [] "#]], @@ -2854,7 +3068,7 @@ fn main() { st &mut S [type] st T T [] st &mut T [type] - md core [] + md core:: [] fn foo(…) fn(&mut S) [] fn main() fn() [] "#]], @@ -2957,7 +3171,7 @@ fn main() { st &T [type] fn bar() fn() -> T [] fn &bar() [type] - md core [] + md core:: [] fn foo(…) fn(&S) [] fn main() fn() [] "#]], @@ -3299,6 +3513,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, ref_match: "&@107", }, @@ -3387,6 +3602,7 @@ fn foo() { function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, ] @@ -3446,6 +3662,7 @@ fn main() { ), is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, ref_match: "&@92", }, @@ -3632,7 +3849,7 @@ fn f() { expect![[r#" st Buffer Buffer [] fn f() fn() [] - md std [] + md std:: [] tt BufRead [requires_import] st BufReader BufReader [requires_import] st BufWriter BufWriter [requires_import] @@ -3641,6 +3858,25 @@ fn f() { } #[test] + /// Issue: https://github.com/rust-lang/rust-analyzer/issues/18554 + fn float_consts_relevance() { + check_relevance( + r#" +//- minicore: float_consts +fn main() { + let x = f32::INF$0 +} +"#, + expect![[r#" + ct INFINITY pub const INFINITY: f32 [] + ct NEG_INFINITY pub const NEG_INFINITY: f32 [] + ct INFINITY f32 [type_could_unify+requires_import+deprecated] + ct NEG_INFINITY f32 [type_could_unify+requires_import+deprecated] + "#]], + ); + } + + #[test] fn completes_struct_with_raw_identifier() { check_edit( "type", @@ -3917,6 +4153,7 @@ fn main() { function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, CompletionItem { @@ -3952,6 +4189,7 @@ fn main() { function: None, is_skipping_completion: false, has_local_inherent_impl: false, + is_deprecated: false, }, }, ] diff --git a/crates/ide-completion/src/render/const_.rs b/crates/ide-completion/src/render/const_.rs index 134a77a899..c14fc1704c 100644 --- a/crates/ide-completion/src/render/const_.rs +++ b/crates/ide-completion/src/render/const_.rs @@ -6,12 +6,15 @@ use syntax::ToSmolStr; use crate::{item::CompletionItem, render::RenderContext}; -pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> { +pub(crate) fn render_const( + ctx: RenderContext<'_, '_>, + const_: hir::Const, +) -> Option<CompletionItem> { let _p = tracing::info_span!("render_const").entered(); render(ctx, const_) } -fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> { +fn render(ctx: RenderContext<'_, '_>, const_: hir::Const) -> Option<CompletionItem> { let db = ctx.db(); let name = const_.name(db)?; let (name, escaped_name) = diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 18151cffcd..97d5a25f49 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -27,7 +27,7 @@ enum FuncKind<'ctx> { } pub(crate) fn render_fn( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: Option<hir::Name>, func: hir::Function, @@ -37,7 +37,7 @@ pub(crate) fn render_fn( } pub(crate) fn render_method( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, dot_access: &DotAccess<'_>, receiver: Option<SmolStr>, local_name: Option<hir::Name>, @@ -48,7 +48,7 @@ pub(crate) fn render_method( } fn render( - ctx @ RenderContext { completion, .. }: RenderContext<'_>, + ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>, local_name: Option<hir::Name>, func: hir::Function, func_kind: FuncKind<'_>, @@ -183,7 +183,7 @@ fn render( fn compute_return_type_match( db: &dyn HirDatabase, - ctx: &RenderContext<'_>, + ctx: &RenderContext<'_, '_>, self_type: hir::Type<'_>, ret_type: &hir::Type<'_>, ) -> CompletionRelevanceReturnType { @@ -210,7 +210,7 @@ fn compute_return_type_match( pub(super) fn add_call_parens<'b>( builder: &'b mut Builder, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, cap: SnippetCap, name: SmolStr, escaped_name: SmolStr, @@ -286,7 +286,7 @@ pub(super) fn add_call_parens<'b>( builder.label(SmolStr::from_iter([&name, label_suffix])).insert_snippet(cap, snippet) } -fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { +fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { if let Some(derefed_ty) = ty.remove_ref() { for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { @@ -301,7 +301,7 @@ fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type<'_>) -> & "" } -fn detail(ctx: &CompletionContext<'_>, func: hir::Function) -> String { +fn detail(ctx: &CompletionContext<'_, '_>, func: hir::Function) -> String { let mut ret_ty = func.ret_type(ctx.db); let mut detail = String::new(); @@ -327,7 +327,7 @@ fn detail(ctx: &CompletionContext<'_>, func: hir::Function) -> String { detail } -fn detail_full(ctx: &CompletionContext<'_>, func: hir::Function) -> String { +fn detail_full(ctx: &CompletionContext<'_, '_>, func: hir::Function) -> String { let signature = format!("{}", func.display(ctx.db, ctx.display_target)); let mut detail = String::with_capacity(signature.len()); @@ -342,7 +342,7 @@ fn detail_full(ctx: &CompletionContext<'_>, func: hir::Function) -> String { detail } -fn params_display(ctx: &CompletionContext<'_>, detail: &mut String, func: hir::Function) { +fn params_display(ctx: &CompletionContext<'_, '_>, detail: &mut String, func: hir::Function) { if let Some(self_param) = func.self_param(ctx.db) { format_to!(detail, "{}", self_param.display(ctx.db, ctx.display_target)); let assoc_fn_params = func.assoc_fn_params(ctx.db); @@ -368,7 +368,7 @@ fn params_display(ctx: &CompletionContext<'_>, detail: &mut String, func: hir::F } fn params<'db>( - ctx: &CompletionContext<'db>, + ctx: &CompletionContext<'_, 'db>, func: hir::Function, func_kind: &FuncKind<'_>, has_dot_receiver: bool, diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index b7de3da468..9e0cec62e6 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -20,7 +20,7 @@ use crate::{ }; pub(crate) fn render_variant_lit( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, local_name: Option<hir::Name>, variant: hir::EnumVariant, @@ -34,7 +34,7 @@ pub(crate) fn render_variant_lit( } pub(crate) fn render_struct_literal( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, strukt: hir::Struct, path: Option<hir::ModPath>, @@ -48,7 +48,7 @@ pub(crate) fn render_struct_literal( } fn render( - ctx @ RenderContext { completion, .. }: RenderContext<'_>, + ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>, path_ctx: &PathCompletionCtx<'_>, thing: Variant, name: hir::Name, @@ -154,7 +154,7 @@ enum Variant { } impl Variant { - fn fields(self, ctx: &CompletionContext<'_>) -> Option<Vec<hir::Field>> { + fn fields(self, ctx: &CompletionContext<'_, '_>) -> Option<Vec<hir::Field>> { let fields = match self { Variant::Struct(it) => it.fields(ctx.db), Variant::EnumVariant(it) => it.fields(ctx.db), @@ -187,14 +187,12 @@ impl Variant { } } - fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool { + fn is_deprecated(self, ctx: &RenderContext<'_, '_>) -> bool { match self { Variant::Struct(it) => { ctx.is_deprecated(it, None /* structs can't be assoc items */) } - Variant::EnumVariant(it) => { - ctx.is_deprecated(it, None /* enum variants can't be assoc items */) - } + Variant::EnumVariant(it) => ctx.is_variant_deprecated(it), } } diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs index ff4cf9a75b..85a0761c17 100644 --- a/crates/ide-completion/src/render/macro_.rs +++ b/crates/ide-completion/src/render/macro_.rs @@ -11,7 +11,7 @@ use crate::{ }; pub(crate) fn render_macro( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, PathCompletionCtx { kind, has_macro_bang, has_call_parens, .. }: &PathCompletionCtx<'_>, name: hir::Name, @@ -22,7 +22,7 @@ pub(crate) fn render_macro( } pub(crate) fn render_macro_pat( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, _pattern_ctx: &PatternContext, name: hir::Name, macro_: hir::Macro, @@ -32,7 +32,7 @@ pub(crate) fn render_macro_pat( } fn render( - ctx @ RenderContext { completion, .. }: RenderContext<'_>, + ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>, is_use_path: bool, has_macro_bang: bool, has_call_parens: bool, @@ -91,7 +91,7 @@ fn render( } fn label( - ctx: &RenderContext<'_>, + ctx: &RenderContext<'_, '_>, needs_bang: bool, bra: &str, ket: &str, diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 022e97e4f7..392ecbc302 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -15,7 +15,7 @@ use crate::{ }; pub(crate) fn render_struct_pat( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, pattern_ctx: &PatternContext, strukt: hir::Struct, local_name: Option<Name>, @@ -44,7 +44,7 @@ pub(crate) fn render_struct_pat( } pub(crate) fn render_variant_pat( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, pattern_ctx: &PatternContext, path_ctx: Option<&PathCompletionCtx<'_>>, variant: hir::EnumVariant, @@ -104,7 +104,7 @@ pub(crate) fn render_variant_pat( } fn build_completion( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, label: SmolStr, lookup: SmolStr, pat: String, @@ -140,7 +140,7 @@ fn build_completion( } fn render_pat( - ctx: &RenderContext<'_>, + ctx: &RenderContext<'_, '_>, pattern_ctx: &PatternContext, name: &str, kind: StructKind, diff --git a/crates/ide-completion/src/render/type_alias.rs b/crates/ide-completion/src/render/type_alias.rs index 2b79ca2deb..ce1be2d551 100644 --- a/crates/ide-completion/src/render/type_alias.rs +++ b/crates/ide-completion/src/render/type_alias.rs @@ -7,7 +7,7 @@ use syntax::{SmolStr, ToSmolStr}; use crate::{item::CompletionItem, render::RenderContext}; pub(crate) fn render_type_alias( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, type_alias: hir::TypeAlias, ) -> Option<CompletionItem> { let _p = tracing::info_span!("render_type_alias").entered(); @@ -15,7 +15,7 @@ pub(crate) fn render_type_alias( } pub(crate) fn render_type_alias_with_eq( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, type_alias: hir::TypeAlias, ) -> Option<CompletionItem> { let _p = tracing::info_span!("render_type_alias_with_eq").entered(); @@ -23,7 +23,7 @@ pub(crate) fn render_type_alias_with_eq( } fn render( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, type_alias: hir::TypeAlias, with_eq: bool, ) -> Option<CompletionItem> { diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs index 7164c94fde..e7ee59d489 100644 --- a/crates/ide-completion/src/render/union_literal.rs +++ b/crates/ide-completion/src/render/union_literal.rs @@ -14,7 +14,7 @@ use crate::{ }; pub(crate) fn render_union_literal( - ctx: RenderContext<'_>, + ctx: RenderContext<'_, '_>, un: hir::Union, path: Option<hir::ModPath>, local_name: Option<Name>, diff --git a/crates/ide-completion/src/render/variant.rs b/crates/ide-completion/src/render/variant.rs index ce35ab135f..f86af6cdcb 100644 --- a/crates/ide-completion/src/render/variant.rs +++ b/crates/ide-completion/src/render/variant.rs @@ -17,7 +17,7 @@ pub(crate) struct RenderedLiteral { /// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for /// the `name` argument for an anonymous type. pub(crate) fn render_record_lit( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, snippet_cap: Option<SnippetCap>, fields: &[hir::Field], path: &str, @@ -63,7 +63,7 @@ pub(crate) fn render_record_lit( /// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for /// the `name` argument for an anonymous type. pub(crate) fn render_tuple_lit( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, snippet_cap: Option<SnippetCap>, fields: &[hir::Field], path: &str, @@ -93,7 +93,7 @@ pub(crate) fn render_tuple_lit( /// fields, plus a boolean for whether the list is comprehensive (contains no /// private fields and its item is not marked `#[non_exhaustive]`). pub(crate) fn visible_fields( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, fields: &[hir::Field], item: impl HasAttrs + HasCrate + Copy, ) -> Option<(Vec<hir::Field>, bool)> { diff --git a/crates/ide-completion/src/snippet.rs b/crates/ide-completion/src/snippet.rs index d326098f94..67ca9db230 100644 --- a/crates/ide-completion/src/snippet.rs +++ b/crates/ide-completion/src/snippet.rs @@ -150,7 +150,7 @@ impl Snippet { } /// Returns [`None`] if the required items do not resolve. - pub(crate) fn imports(&self, ctx: &CompletionContext<'_>) -> Option<Vec<LocatedImport>> { + pub(crate) fn imports(&self, ctx: &CompletionContext<'_, '_>) -> Option<Vec<LocatedImport>> { import_edits(ctx, &self.requires) } @@ -163,7 +163,10 @@ impl Snippet { } } -fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option<Vec<LocatedImport>> { +fn import_edits( + ctx: &CompletionContext<'_, '_>, + requires: &[ModPath], +) -> Option<Vec<LocatedImport>> { let import_cfg = ctx.config.find_path_config(ctx.is_nightly); let resolve = |import| { diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index 02e299b2a9..e574d4de0a 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -72,6 +72,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), + add_colons_to_module: true, add_semicolon_to_unit: true, snippet_cap: SnippetCap::new(true), insert_use: InsertUseConfig { diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs index 131911be91..6dcd4f9cdd 100644 --- a/crates/ide-completion/src/tests/attribute.rs +++ b/crates/ide-completion/src/tests/attribute.rs @@ -64,7 +64,7 @@ pub struct Foo(#[m$0] i32); at unsafe(…) at used at warn(…) - md mac + md mac:: kw crate:: kw self:: "#]], @@ -128,7 +128,7 @@ pub struct Foo(#[$0] i32); at unsafe(…) at used at warn(…) - md mac + md mac:: kw crate:: kw self:: "#]], @@ -162,7 +162,7 @@ struct Foo; at repr(…) at unsafe(…) at warn(…) - md proc_macros + md proc_macros:: kw crate:: kw self:: "#]], @@ -463,7 +463,7 @@ struct Foo; at repr(…) at unsafe(…) at warn(…) - md core + md core:: kw crate:: kw self:: "#]], @@ -1137,7 +1137,7 @@ mod derive { de PartialEq, Eq de PartialEq, Eq, PartialOrd, Ord de PartialEq, PartialOrd - md core + md core:: kw crate:: kw self:: "#]], @@ -1159,7 +1159,7 @@ mod derive { de Eq de Eq, PartialOrd, Ord de PartialOrd - md core + md core:: kw crate:: kw self:: "#]], @@ -1181,7 +1181,7 @@ mod derive { de Eq de Eq, PartialOrd, Ord de PartialOrd - md core + md core:: kw crate:: kw self:: "#]], @@ -1202,7 +1202,7 @@ mod derive { de Default macro Default de PartialOrd de PartialOrd, Ord - md core + md core:: kw crate:: kw self:: "#]], @@ -1219,8 +1219,8 @@ mod derive { "#, expect![[r#" de DeriveIdentity (use proc_macros::DeriveIdentity) proc_macro DeriveIdentity - md core - md proc_macros + md core:: + md proc_macros:: kw crate:: kw self:: "#]], @@ -1234,8 +1234,8 @@ use proc_macros::DeriveIdentity; "#, expect![[r#" de DeriveIdentity proc_macro DeriveIdentity - md core - md proc_macros + md core:: + md proc_macros:: kw crate:: kw self:: "#]], diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 294434297e..c1205f9e18 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -5,8 +5,8 @@ use crate::{ CompletionConfig, config::AutoImportExclusionType, tests::{ - BASE_ITEMS_FIXTURE, TEST_CONFIG, check, check_edit, check_with_base_items, - completion_list_with_config, + BASE_ITEMS_FIXTURE, TEST_CONFIG, check, check_edit, check_edit_with_config, + check_with_base_items, completion_list_with_config, }, }; @@ -48,8 +48,8 @@ fn baz() { fn create_foo(…) fn(&FooDesc) fn function() fn() ma makro!(…) macro_rules! makro - md _69latrick - md module + md _69latrick:: + md module:: sc STATIC Unit st FooDesc FooDesc st Record Record @@ -149,8 +149,8 @@ impl Unit { me self.foo() fn(self) lc self Unit ma makro!(…) macro_rules! makro - md module - md qualified + md module:: + md qualified:: sp Self Unit sc STATIC Unit st Record Record @@ -212,8 +212,8 @@ impl Unit { en Enum Enum fn function() fn() ma makro!(…) macro_rules! makro - md module - md qualified + md module:: + md qualified:: sc STATIC Unit st Record Record st Tuple Tuple @@ -1150,6 +1150,22 @@ fn break_value_no_block() { } #[test] +fn complete_module_colons() { + check_edit( + "module", + r#"mod module {} fn foo() { $0 }"#, + r#"mod module {} fn foo() { module:: }"#, + ); + + check_edit_with_config( + CompletionConfig { add_colons_to_module: false, ..TEST_CONFIG }, + "module", + r#"mod module {} fn foo() { $0 }"#, + r#"mod module {} fn foo() { module }"#, + ); +} + +#[test] fn else_completion_after_if() { check( r#" @@ -2224,7 +2240,7 @@ pub struct UnstableThisShouldNotBeListed; "#, expect![[r#" fn main() fn() - md std + md std:: bt u32 u32 kw async kw const @@ -2278,7 +2294,7 @@ pub struct UnstableButWeAreOnNightlyAnyway; "#, expect![[r#" fn main() fn() - md std + md std:: st UnstableButWeAreOnNightlyAnyway UnstableButWeAreOnNightlyAnyway bt u32 u32 kw async @@ -2333,7 +2349,7 @@ pub mod intrinsics {} "#, expect![[r#" fn main() fn() - md std + md std:: bt u32 u32 kw async kw const @@ -2383,10 +2399,10 @@ fn main() { pub mod intrinsics {} "#, expect![[r#" - fn main() fn() - md intrinsics - md std - bt u32 u32 + fn main() fn() + md intrinsics:: + md std:: + bt u32 u32 kw async kw const kw crate:: @@ -2622,7 +2638,7 @@ fn main() { ma helper!(…) macro_rules! helper ma m!(…) macro_rules! m ma makro!(…) macro_rules! makro - md module + md module:: sc STATIC Unit st Record Record st Tuple Tuple @@ -3085,12 +3101,12 @@ fn bar() { ma format_args_nl!(…) macro_rules! format_args_nl ma panic!(…) macro_rules! panic ma print!(…) macro_rules! print - md core - md result (use core::result) - md rust_2015 (use core::prelude::rust_2015) - md rust_2018 (use core::prelude::rust_2018) - md rust_2021 (use core::prelude::rust_2021) - md rust_2024 (use core::prelude::rust_2024) + md core:: + md result:: (use core::result) + md rust_2015:: (use core::prelude::rust_2015) + md rust_2018:: (use core::prelude::rust_2018) + md rust_2021:: (use core::prelude::rust_2021) + md rust_2024:: (use core::prelude::rust_2024) tt Clone tt Copy tt FromIterator @@ -3162,6 +3178,37 @@ fn foo() { } #[test] +fn deprecated_enum_marks_variants_deprecated() { + check( + r#" +#[deprecated] +enum Foo { Bar } +fn main() { let _ = Foo::$0; } +"#, + expect![[r#" + ev Bar Bar DEPRECATED + "#]], + ); +} + +#[test] +fn deprecated_variant_of_undeprecated_enum_still_deprecated() { + check( + r#" +enum Foo { + #[deprecated] Bar, + Baz, +} +fn main() { let _ = Foo::$0; } +"#, + expect![[r#" + ev Bar Bar DEPRECATED + ev Baz Baz + "#]], + ); +} + +#[test] fn non_std_test_attr_macro() { check( r#" @@ -3174,9 +3221,9 @@ fn foo() { } "#, expect![[r#" - fn foo() fn() - md proc_macros - bt u32 u32 + fn foo() fn() + md proc_macros:: + bt u32 u32 kw async kw const kw crate:: @@ -3224,9 +3271,9 @@ fn foo() { } "#, expect![[r#" - fn foo() fn() - md proc_macros - bt u32 u32 + fn foo() fn() + md proc_macros:: + bt u32 u32 kw async kw const kw crate:: @@ -3798,3 +3845,59 @@ fn baz(v: impl Bar) { "#]], ); } + +#[test] +fn regression_21697() { + check( + r#" +trait SuperTrait { + type AssocTy; +} + +trait SubTrait<T = <Self as SuperTrait>::AssocTy>: SuperTrait {} + +fn tryme(param: impl SubTrait) { + param$0 +} + "#, + expect![[r#" + fn tryme(…) fn(impl SubTrait<<impl SubTrait<<… as SuperTrait>::AssocTy> + ?Sized as SuperTrait>::AssocTy> + ?Sized) + lc param impl SubTrait<<impl SubTrait<<… as SuperTrait>::AssocTy> + ?Sized as SuperTrait>::AssocTy> + ?Sized + tt SubTrait + tt SuperTrait + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 60ae077d01..231623a42f 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -781,9 +781,9 @@ fn main() { } "#, expect![[r#" + me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED - me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED "#]], ); } @@ -1772,7 +1772,7 @@ fn function() { "#, expect![[r#" st FooStruct (use outer::FooStruct) BarStruct - md foo (use outer::foo) + md foo:: (use outer::foo) fn foo_fun() (use outer::foo_fun) fn() "#]], ); @@ -1809,9 +1809,8 @@ fn intrinsics() { r#" //- /core.rs crate:core pub mod intrinsics { - extern "rust-intrinsic" { - pub fn transmute<Src, Dst>(src: Src) -> Dst; - } + #[rustc_intrinsic] + pub unsafe fn transmute<Src, Dst>(src: Src) -> Dst; } pub mod mem { pub use crate::intrinsics::transmute; @@ -1829,9 +1828,8 @@ fn intrinsics() { r#" //- /core.rs crate:core pub mod intrinsics { - extern "rust-intrinsic" { - pub fn transmute<Src, Dst>(src: Src) -> Dst; - } + #[rustc_intrinsic] + pub unsafe fn transmute<Src, Dst>(src: Src) -> Dst; } pub mod mem { pub use crate::intrinsics::transmute; diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs index 45024ad216..bb79af7e98 100644 --- a/crates/ide-completion/src/tests/item.rs +++ b/crates/ide-completion/src/tests/item.rs @@ -2,7 +2,7 @@ //! //! Except for use items which are tested in [super::use_tree] and mod declarations with are tested //! in [crate::completions::mod_]. -use expect_test::expect; +use expect_test::{Expect, expect}; use crate::tests::{check, check_edit, check_with_base_items}; @@ -15,7 +15,7 @@ impl Tra$0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -41,7 +41,7 @@ impl Trait for Str$0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -118,7 +118,7 @@ fn completes_where() { expect![[r#" en Enum (adds ->) Enum ma makro!(…) macro_rules! makro - md module (adds ->) + md module:: (adds ->) st Record (adds ->) Record st Tuple (adds ->) Tuple st Unit (adds ->) Unit @@ -135,6 +135,12 @@ fn completes_where() { "#]], ); check_with_base_items( + r"fn func() -> foo::Bar $0", + expect![[r#" + kw where + "#]], + ); + check_with_base_items( r"enum Enum $0", expect![[r#" kw where @@ -155,6 +161,62 @@ fn completes_where() { } #[test] +fn completes_where_in_stmt_list() { + fn check_in_stmt_list(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + check(&format!("const _: () = {{{ra_fixture}}};"), expect); + } + check_in_stmt_list( + r"struct Struct $0", + expect![[r#" + kw where + "#]], + ); + check_in_stmt_list( + r"struct Struct $0 {}", + expect![[r#" + kw where + "#]], + ); + check_in_stmt_list( + r"fn func() $0", + expect![[r#" + bt u32 (adds ->) u32 + kw crate:: (adds ->) + kw dyn (adds ->) + kw fn (adds ->) + kw for (adds ->) + kw impl (adds ->) + kw self:: (adds ->) + kw where + "#]], + ); + check_in_stmt_list( + r"fn func() -> foo::Bar $0", + expect![[r#" + kw where + "#]], + ); + check_in_stmt_list( + r"enum Enum $0", + expect![[r#" + kw where + "#]], + ); + check_in_stmt_list( + r"enum Enum $0 {}", + expect![[r#" + kw where + "#]], + ); + check_in_stmt_list( + r"trait Trait $0 {}", + expect![[r#" + kw where + "#]], + ); +} + +#[test] fn before_record_field() { check_with_base_items( r#" @@ -301,7 +363,7 @@ fn bar() { ma expand_to_test!(…) macro_rules! expand_to_test ma makro!(…) macro_rules! makro ma test!(…) macro test - md module + md module:: sc STATIC Unit st Record Record st Tuple Tuple diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs index 0b2be0265f..430c61887a 100644 --- a/crates/ide-completion/src/tests/item_list.rs +++ b/crates/ide-completion/src/tests/item_list.rs @@ -43,7 +43,7 @@ fn in_source_file_item_list() { r#"$0"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw async kw const kw crate:: @@ -77,7 +77,7 @@ fn in_item_list_after_attr() { r#"#[attr] $0"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw async kw const kw crate:: @@ -111,7 +111,7 @@ fn in_item_list_after_inner_attr() { r#"#![attr] $0"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw async kw const kw crate:: @@ -145,7 +145,7 @@ fn in_qualified_path() { r#"crate::$0"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: "#]], ) } @@ -315,7 +315,7 @@ fn in_impl_assoc_item_list() { r#"impl Struct { $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw async kw const kw crate:: @@ -335,7 +335,7 @@ fn in_impl_assoc_item_list_after_attr() { r#"impl Struct { #[attr] $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw async kw const kw crate:: @@ -355,7 +355,7 @@ fn in_trait_assoc_item_list() { r"trait Foo { $0 }", expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw async kw const kw crate:: @@ -373,7 +373,7 @@ fn in_trait_assoc_fn_missing_body() { r#"trait Foo { fn function(); $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw async kw const kw crate:: @@ -391,7 +391,7 @@ fn in_trait_assoc_const_missing_body() { r#"trait Foo { const CONST: (); $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw async kw const kw crate:: @@ -409,7 +409,7 @@ fn in_trait_assoc_type_aliases_missing_ty() { r#"trait Foo { type Type; $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw async kw const kw crate:: @@ -448,7 +448,7 @@ impl Test for () { fn fn function1() fn fn function2() ma makro!(…) macro_rules! makro - md module + md module:: ta type Type1 = kw crate:: kw self:: @@ -514,7 +514,7 @@ fn after_unit_struct() { r#"struct S; f$0"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw async kw const kw crate:: @@ -664,7 +664,7 @@ fn inside_extern_blocks() { r#"extern { $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw crate:: kw fn kw pub @@ -681,7 +681,7 @@ fn inside_extern_blocks() { r#"unsafe extern { $0 }"#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: kw crate:: kw fn kw pub diff --git a/crates/ide-completion/src/tests/predicate.rs b/crates/ide-completion/src/tests/predicate.rs index 682b8904e5..9826a8ed7b 100644 --- a/crates/ide-completion/src/tests/predicate.rs +++ b/crates/ide-completion/src/tests/predicate.rs @@ -13,7 +13,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Foo<…> Foo<'_, {unknown}, _> st Record Record st Tuple Tuple @@ -39,7 +39,7 @@ struct Foo<'lt, T, const C: usize> where T: $0 {} "#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: tt Trait kw crate:: kw self:: @@ -57,7 +57,7 @@ struct Foo<'lt, T, const C: usize> where 'lt: $0 {} "#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: tt Trait kw crate:: kw self:: @@ -73,7 +73,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {} "#, expect![[r#" ma makro!(…) macro_rules! makro - md module + md module:: tt Trait kw crate:: kw self:: @@ -90,7 +90,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Foo<…> Foo<'_, {unknown}, _> st Record Record st Tuple Tuple @@ -119,7 +119,7 @@ impl Record { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: sp Self Record st Record Record st Tuple Tuple @@ -149,7 +149,7 @@ struct Foo<T> where T: $0 {} pub trait Trait {} "#, expect![[r#" - md std + md std:: kw crate:: kw self:: "#]], @@ -169,7 +169,7 @@ struct Foo<T> where T: $0 {} pub trait Trait {} "#, expect![[r#" - md std + md std:: tt Trait kw crate:: kw self:: diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index c1274f6640..ddb9294469 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -176,7 +176,7 @@ fn main() { fn main() fn() lc foo Foo lc thing i32 - md core + md core:: st Foo Foo st Foo {…} Foo { foo1: u32, foo2: u32 } tt Default diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 55059a4035..0454f4e350 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -65,7 +65,7 @@ pub mod prelude { } "#, expect![[r#" - md std + md std:: st Option Option bt u32 u32 "#]], @@ -95,7 +95,7 @@ mod macros { expect![[r#" fn f() fn() ma concat!(…) macro_rules! concat - md std + md std:: bt u32 u32 "#]], ); @@ -123,8 +123,8 @@ pub mod prelude { } "#, expect![[r#" - md core - md std + md core:: + md std:: st String String bt u32 u32 "#]], @@ -153,7 +153,7 @@ pub mod prelude { "#, expect![[r#" fn f() fn() - md std + md std:: bt u32 u32 "#]], ); @@ -181,8 +181,8 @@ pub mod prelude { } "#, expect![[r#" - md std - "#]], + md std:: + "#]], ); } @@ -714,7 +714,7 @@ mod m { "#, expect![[r#" fn z() fn() - md z + md z:: "#]], ); } @@ -1126,7 +1126,7 @@ fn foo { ::$0 } "#, Some(':'), expect![[r#" - md core + md core:: "#]], ); check_with_trigger_character( @@ -1136,7 +1136,7 @@ fn foo { /* test */::$0 } "#, Some(':'), expect![[r#" - md core + md core:: "#]], ); @@ -1488,7 +1488,7 @@ fn here_we_go() { "#, expect![[r#" fn here_we_go() fn() - md foo + md foo:: st Bar (alias Qux) (use foo::Bar) Bar bt u32 u32 kw const diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 7d4a7fe6b8..24080334ae 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -14,7 +14,7 @@ struct Foo<'lt, T, const C: usize> { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: sp Self Foo<'_, {unknown}, _> st Foo<…> Foo<'_, {unknown}, _> st Record Record @@ -43,7 +43,7 @@ struct Foo<'lt, T, const C: usize>(f$0); expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: sp Self Foo<'_, {unknown}, _> st Foo<…> Foo<'_, {unknown}, _> st Record Record @@ -75,7 +75,7 @@ fn x<'lt, T, const C: usize>() -> $0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -102,7 +102,7 @@ fn x() u$0 expect![[r#" en Enum (adds ->) Enum ma makro!(…) macro_rules! makro - md module (adds ->) + md module:: (adds ->) st Record (adds ->) Record st Tuple (adds ->) Tuple st Unit (adds ->) Unit @@ -126,7 +126,7 @@ fn x() $0 expect![[r#" en Enum (adds ->) Enum ma makro!(…) macro_rules! makro - md module (adds ->) + md module:: (adds ->) st Record (adds ->) Record st Tuple (adds ->) Tuple st Unit (adds ->) Unit @@ -142,6 +142,26 @@ fn x() $0 kw where "#]], ); + + check_with_base_items( + r#" +mod foo { pub struct Bar; } +fn x() foo::$0 +"#, + expect![[r#" + st Bar (adds ->) Bar + "#]], + ); + + check_with_base_items( + r#" +mod foo { pub struct Bar; } +fn x() foo::b$0 +"#, + expect![[r#" + st Bar (adds ->) Bar + "#]], + ); } #[test] @@ -196,7 +216,7 @@ fn foo() $0 "#, r#" mod foo { pub type Num = u32; } -fn foo() -> foo +fn foo() -> foo:: "#, ); @@ -233,7 +253,7 @@ fn foo()$0 "#, r#" mod foo { pub type Num = u32; } -fn foo() ->foo +fn foo() ->foo:: "#, ); } @@ -286,7 +306,7 @@ fn x() u$0 {&2u32} expect![[r#" en Enum (adds ->) Enum ma makro!(…) macro_rules! makro - md module (adds ->) + md module:: (adds ->) st Record (adds ->) Record st Tuple (adds ->) Tuple st Unit (adds ->) Unit @@ -326,7 +346,7 @@ fn x<'lt, T, const C: usize>(_: &()) -> &$0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -360,7 +380,7 @@ fn foo() -> B$0 { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -388,7 +408,7 @@ const FOO: $0 = Foo(2); expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Foo<…> Foo<{unknown}> st Record Record st Tuple Tuple @@ -417,7 +437,7 @@ static FOO: $0 = Foo(2); expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Foo<…> Foo<{unknown}> st Record Record st Tuple Tuple @@ -448,7 +468,7 @@ fn f2() { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -480,7 +500,7 @@ fn f2() { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -509,7 +529,7 @@ fn f2(x: u64) -> $0 { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -539,7 +559,7 @@ fn f2(x: $0) { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -575,8 +595,8 @@ fn foo<'lt, T, const C: usize>() { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md a - md module + md a:: + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -608,7 +628,7 @@ fn foo<'lt, T, const C: usize>() { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Foo<…> Foo<{unknown}> st Record Record st Tuple Tuple @@ -640,7 +660,7 @@ fn foo<'lt, T, const C: usize>() { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -666,7 +686,7 @@ fn foo<'lt, T, const C: usize>() { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -709,7 +729,7 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -738,7 +758,7 @@ fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {} expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -763,7 +783,7 @@ impl Tr<$0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: sp Self dyn Tr<{unknown}> + 'static st Record Record st S S @@ -814,7 +834,7 @@ fn f(t: impl MyTrait<u$0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -843,7 +863,7 @@ fn f(t: impl MyTrait<u8, u$0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -890,7 +910,7 @@ fn f(t: impl MyTrait<u$0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -919,7 +939,7 @@ fn f(t: impl MyTrait<u8, u$0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -968,7 +988,7 @@ fn f(t: impl MyTrait<Item1 = $0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -997,7 +1017,7 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0 expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Record Record st Tuple Tuple st Unit Unit @@ -1049,7 +1069,7 @@ struct Foo { pub struct S; "#, expect![[r#" - md std + md std:: sp Self Foo st Foo Foo bt u32 u32 @@ -1078,7 +1098,7 @@ struct Foo { pub struct S; "#, expect![[r#" - md std + md std:: sp Self Foo st Foo Foo st S S @@ -1108,7 +1128,7 @@ fn completes_const_and_type_generics_separately() { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Foo Foo st Record Record st Tuple Tuple @@ -1162,7 +1182,7 @@ fn completes_const_and_type_generics_separately() { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Foo Foo st Record Record st Tuple Tuple @@ -1213,7 +1233,7 @@ fn completes_const_and_type_generics_separately() { expect![[r#" en Enum Enum ma makro!(…) macro_rules! makro - md module + md module:: st Foo Foo st Record Record st Tuple Tuple @@ -1424,7 +1444,7 @@ struct Bar; impl $0 for Bar { } "#, expect![[r#" - md module + md module:: tt Foo tt Trait kw crate:: @@ -1447,7 +1467,7 @@ mod outer { impl outer::$0 for Bar { } "#, expect![[r#" - md inner + md inner:: tt Foo "#]], ); diff --git a/crates/ide-completion/src/tests/visibility.rs b/crates/ide-completion/src/tests/visibility.rs index b404011dfe..113c41226f 100644 --- a/crates/ide-completion/src/tests/visibility.rs +++ b/crates/ide-completion/src/tests/visibility.rs @@ -44,7 +44,7 @@ mod foo { mod bar {} "#, expect![[r#" - md foo + md foo:: "#]], ); check( @@ -59,7 +59,7 @@ mod qux { mod bar {} "#, expect![[r#" - md qux + md qux:: "#]], ); check( @@ -74,7 +74,7 @@ mod qux { mod bar {} "#, expect![[r#" - md foo + md foo:: "#]], ); } diff --git a/crates/ide-db/src/active_parameter.rs b/crates/ide-db/src/active_parameter.rs index 8bd4c6c46b..506645261b 100644 --- a/crates/ide-db/src/active_parameter.rs +++ b/crates/ide-db/src/active_parameter.rs @@ -27,7 +27,7 @@ impl<'db> ActiveParameter<'db> { /// Returns information about the call argument this token is part of. pub fn at_arg( - sema: &'db Semantics<'db, RootDatabase>, + sema: &Semantics<'db, RootDatabase>, list: ast::ArgList, at: TextSize, ) -> Option<Self> { diff --git a/crates/ide-db/src/generated/lints.rs b/crates/ide-db/src/generated/lints.rs index 52a5a95450..264bb4fa81 100644 --- a/crates/ide-db/src/generated/lints.rs +++ b/crates/ide-db/src/generated/lints.rs @@ -258,6 +258,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "dead_code_pub_in_binary", + description: r##"detect public items in executable crates that are never used"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "default_overrides_default_fields", description: r##"detect `Default` impl that should use the type's default field values"##, default_severity: Severity::Error, @@ -736,7 +743,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[ Lint { label: "linker_messages", description: r##"warnings emitted at runtime by the target-specific linker program"##, - default_severity: Severity::Allow, + default_severity: Severity::Warning, warn_since: None, deny_since: None, }, @@ -1238,6 +1245,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { + label: "tail_call_track_caller", + description: r##"detects tail calls of functions marked with `#[track_caller]`"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { label: "tail_expr_drop_order", description: r##"Detect and warn on significant change in drop order in tail expression location"##, default_severity: Severity::Allow, @@ -4313,7 +4327,7 @@ defined in Rust. They may be called both from within Rust and via FFI. pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize { let mut sum = 0; for _ in 0..n { - sum += args.arg::<usize>(); + sum += args.next_arg::<usize>(); } sum } @@ -4324,6 +4338,22 @@ pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize { deny_since: None, }, Lint { + label: "c_variadic_experimental_arch", + description: r##"# `c_variadic_experimental_arch` + +Allows defining c-variadic functions on targets where this feature has not yet undergone sufficient testing for stabilization. + +The tracking issue for this feature is: [#155973] + +[#155973]: https://github.com/rust-lang/rust/issues/155973 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "c_variadic_naked_functions", description: r##"# `c_variadic_naked_functions` @@ -4806,22 +4836,6 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { - label: "char_max_len", - description: r##"# `char_max_len` - - - -The tracking issue for this feature is: [#121714] - -[#121714]: https://github.com/rust-lang/rust/issues/121714 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "clamp_magnitude", description: r##"# `clamp_magnitude` @@ -5552,6 +5566,20 @@ The tracking issue for this feature is: [#95174] deny_since: None, }, Lint { + label: "const_param_ty_unchecked", + description: r##"# `const_param_ty_unchecked` + +Allows skipping `ConstParamTy_` trait implementation checks + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "const_path_separators", description: r##"# `const_path_separators` @@ -6004,6 +6032,22 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { + label: "core_io", + description: r##"# `core_io` + + + +The tracking issue for this feature is: [#154046] + +[#154046]: https://github.com/rust-lang/rust/issues/154046 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "core_io_borrowed_buf", description: r##"# `core_io_borrowed_buf` @@ -6020,6 +6064,20 @@ The tracking issue for this feature is: [#117693] deny_since: None, }, Lint { + label: "core_io_internals", + description: r##"# `core_io_internals` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "core_private_bignum", description: r##"# `core_private_bignum` @@ -6986,13 +7044,96 @@ The tracking issue for this feature is: [#143874] label: "diagnostic_on_move", description: r##"# `diagnostic_on_move` -Allows giving on-move borrowck custom diagnostic messages for a type - The tracking issue for this feature is: [#154181] -[#154181]: https://github.com/rust-lang/rust/issues/154181 - ------------------------ + +The `diagnostic_on_move` feature allows use of the `#[diagnostic::on_move]` attribute. It should be +placed on struct, enum and union declarations, though it is not an error to be located in other +positions. This attribute is a hint to the compiler to supplement the error message when the +annotated type is involved in a borrowcheck error. + +For example, [`File`] is annotated as such: +```rust +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move(note = "you can use `File::try_clone` \ + to duplicate a `File` instance")] +pub struct File { + // ... +} +``` + +When you try to use a `File` after it's already been moved, it will helpfully tell you about `try_clone`. + +The message and label can also be customized: + +```rust +#![feature(diagnostic_on_move)] + +use std::marker::PhantomData; + +#[diagnostic::on_move( + message = "`{Self}` cannot be used multiple times", + label = "this token may only be used once", + note = "you can create a new `Token` with `Token::conjure()`" +)] +pub struct Token<'brand> { + spooky: PhantomData<&'brand ()>, +} + +impl Token<'_> { + pub fn conjure<'u>() -> Token<'u> { + Token { + spooky: PhantomData, + } + } +} +``` +The user may try to use it like this: +```rust,compile_fail,E0382 +# #![feature(diagnostic_on_move)] +# +# use std::marker::PhantomData; +# +# #[diagnostic::on_move( +# message = "`{Self}` cannot be used multiple times", +# label = "this token may only be used once", +# note = "you can create a new `Token` with `Token::conjure()`" +# )] +# pub struct Token<'brand> { +# spooky: PhantomData<&'brand ()>, +# } +# +# impl Token<'_> { +# pub fn conjure<'u>() -> Token<'u> { +# Token { +# spooky: PhantomData, +# } +# } +# } +# fn main() { +let token = Token::conjure(); +let _ = (token, token); +# } +``` +This will result in the following error: +```text +error[E0382]: `Token` cannot be used multiple times + --> src/main.rs:24:21 + | + 1 | let token = Token::conjure(); + | ----- this token may only be used once + 2 | let _ = (token, token); + | ----- ^^^^^ value used here after move + | | + | value moved here + | + = note: you can create a new `Token` with `Token::conjure()` +``` + +[`File`]: https://doc.rust-lang.org/nightly/std/fs/struct.File.html "File in std::fs" +[#154181]: https://github.com/rust-lang/rust/issues/154181 "Tracking Issue for #[diagnostic::on_move]" "##, default_severity: Severity::Allow, warn_since: None, @@ -7015,6 +7156,66 @@ The tracking issue for this feature is: [#152900] deny_since: None, }, Lint { + label: "diagnostic_on_unmatch_args", + description: r##"# `diagnostic_on_unmatch_args` + +The tracking issue for this feature is: [#155642] + +[#155642]: https://github.com/rust-lang/rust/issues/155642 + +------------------------ + +The `diagnostic_on_unmatch_args` feature adds the +`#[diagnostic::on_unmatch_args(...)]` attribute for declarative macros. +It lets a macro definition customize diagnostics for matcher failures after all arms have been +tried, such as incomplete invocations or trailing extra arguments. + +This attribute currently applies to declarative macros such as `macro_rules!` and `pub macro`. +It is currently used for errors emitted by declarative macro matching itself; fragment parser +errors still use their existing diagnostics. + +```rust,compile_fail +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args( + message = "invalid arguments to {This} macro invocation", + label = "expected a type and value here", + note = "this macro expects a type and a value, like `pair!(u8, 0)`", + note = "see <link/to/docs>", +)] +macro_rules! pair { + ($ty:ty, $value:expr) => {}; +} + +pair!(u8); +``` + +This emits output like: + +```text +error: invalid arguments to pair macro invocation + --> example.rs:13:9 + | +9 | macro_rules! pair { + | ----------------- when calling this macro +... +13 | pair!(u8); + | ^ expected a type and value here + | +note: while trying to match `,` + --> example.rs:10:12 + | +10 | ($ty:ty, $value:expr) => {}; + | ^ + = note: this macro expects a type and a value, like `pair!(u8, 0)` + = note: see <link/to/docs> +``` +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "dir_entry_ext2", description: r##"# `dir_entry_ext2` @@ -8077,6 +8278,22 @@ The tracking issue for this feature is: [#99842] deny_since: None, }, Lint { + label: "float_masks", + description: r##"# `float_masks` + + + +The tracking issue for this feature is: [#154064] + +[#154064]: https://github.com/rust-lang/rust/issues/154064 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "float_minimum_maximum", description: r##"# `float_minimum_maximum` @@ -8516,7 +8733,7 @@ The tracking issue for this feature is: [#130539] }, Lint { label: "generic_const_args", - description: r##"# `generic_const_args` + description: r##"# generic_const_args Allows using generics in more complex const expressions, based on definitional equality. @@ -8525,6 +8742,35 @@ The tracking issue for this feature is: [#151972] [#151972]: https://github.com/rust-lang/rust/issues/151972 ------------------------ + +Warning: This feature is incomplete; its design and syntax may change. + +This feature enables many of the same use cases supported by [generic_const_exprs], +but based on the machinery developed for [min_generic_const_args]. In a way, it is +meant to be an interim successor for GCE (though it might not currently support all +the valid cases that supported by GCE). + +See also: [generic_const_items] + +[min_generic_const_args]: min-generic-const-args.md +[generic_const_exprs]: generic-const-exprs.md +[generic_const_items]: generic-const-items.md + +## Examples + +<!-- NOTE(ignore) generic_const_args requires -Znext-solver to compile --> +```rust,ignore (requires-Z-next-solver) +#![feature(generic_const_items)] +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![expect(incomplete_features)] + +type const ADD1<const N: usize>: usize = const { N + 1 }; + +type const INC<const N: usize>: usize = ADD1::<N>; + +const ARR: [(); ADD1::<0>] = [(); INC::<0>]; +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -8532,15 +8778,79 @@ The tracking issue for this feature is: [#151972] }, Lint { label: "generic_const_exprs", - description: r##"# `generic_const_exprs` + description: r##"# generic_const_exprs -Allows non-trivial generic constants which have to have wfness manually propagated to callers +Allows non-trivial generic constants which have to be shown to successfully evaluate +to a value by being part of an item signature. The tracking issue for this feature is: [#76560] + [#76560]: https://github.com/rust-lang/rust/issues/76560 ------------------------ + +Warning: This feature is incomplete; its design and syntax may change. + +See also: [min_generic_const_args], [generic_const_args] + +[min_generic_const_args]: min-generic-const-args.md +[generic_const_args]: generic-const-args.md + +## Examples + +```rust +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +// Use parameters that depend on a generic argument. +struct Foo<const N: usize> +where + [(); N + 1]:, +{ + array: [usize; N + 1], +} + +// Use generic parameters in const operations. +trait Bar { + const X: usize; + const Y: usize; +} + +// Note `B::X * B::Y`. +const fn baz<B: Bar>(x: [usize; B::X], y: [usize; B::Y]) -> [usize; B::X * B::Y] { + let mut out = [0; B::X * B::Y]; + let mut i = 0; + while i < B::Y { + let mut j = 0; + while j < B::X { + out[i * B::X + j] = y[i].saturating_mul(x[j]); + j += 1; + } + i += 1; + } + out +} + + +// Create a new type based on a generic argument. +pub struct Grow<const N: usize> { + arr: [usize; N], +} + +impl<const N: usize> Grow<N> { + pub const fn grow(self, val: usize) -> Grow<{ N + 1 }> { + let mut new_arr = [0; { N + 1 }]; + let mut idx = 0; + while idx < N { + new_arr[idx] = self.arr[idx]; + idx += 1; + } + new_arr[N] = val; + Grow { arr: new_arr } + } +} +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -8548,7 +8858,7 @@ The tracking issue for this feature is: [#76560] }, Lint { label: "generic_const_items", - description: r##"# `generic_const_items` + description: r##"# generic_const_items Allows generic parameters and where-clauses on free & associated const items. @@ -8557,6 +8867,50 @@ The tracking issue for this feature is: [#113521] [#113521]: https://github.com/rust-lang/rust/issues/113521 ------------------------ + +Warning: This feature is an [experiment] and lacks an RFC. +There are no guarantees that it will ever be stabilized. + +See also: [generic_const_exprs], [min_generic_const_args]. + +[experiment]: https://lang-team.rust-lang.org/how_to/experiment.html +[generic_const_exprs]: generic-const-exprs.md +[min_generic_const_args]: min-generic-const-args.md + +## Examples + +### Generic constant values + +```rust +#![allow(incomplete_features)] +#![feature(generic_const_items)] + +const GENERIC_VAL<const ARG: usize>: usize = ARG + 1; + +#[test] +fn generic_const_arg() { + assert_eq!(GENERIC_VAL::<1>, 2); + assert_eq!(GENERIC_VAL::<2>, 3); +} +``` + +### Conditional constants + +```rust +#![allow(incomplete_features)] +#![feature(generic_const_items)] + +// `GENERIC_VAL::<0>` will fail to compile +const GENERIC_VAL<const ARG: usize>: usize = if ARG > 0 { ARG + 1 } else { panic!("0 value") }; + +// Will fail to compile if the `Copy` derive is removed. +const COPY_MARKER<C: Copy>: () = (); + +#[derive(Clone, Copy)] +struct Foo; + +const FOO_IS_COPY: () = COPY_MARKER::<Foo>; +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -8671,6 +9025,22 @@ This feature has no tracking issue, and is therefore likely internal to the comp deny_since: None, }, Lint { + label: "gpu_launch_sized_workgroup_mem", + description: r##"# `gpu_launch_sized_workgroup_mem` + + + +The tracking issue for this feature is: [#135513] + +[#135513]: https://github.com/rust-lang/rust/issues/135513 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "guard_patterns", description: r##"# `guard_patterns` @@ -9034,8 +9404,24 @@ The tracking issue for this feature is: [#99069] deny_since: None, }, Lint { - label: "integer_extend_truncate", - description: r##"# `integer_extend_truncate` + label: "integer_cast_extras", + description: r##"# `integer_cast_extras` + + + +The tracking issue for this feature is: [#154650] + +[#154650]: https://github.com/rust-lang/rust/issues/154650 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "integer_widen_truncate", + description: r##"# `integer_widen_truncate` @@ -10547,15 +10933,116 @@ The tracking issue for this feature is: [#154042] }, Lint { label: "min_generic_const_args", - description: r##"# `min_generic_const_args` + description: r##"# min_generic_const_args -Enables the generic const args MVP (only bare paths, not arbitrary computation). +Enables the generic const args MVP (paths to type const items and constructors for ADTs and primitives). The tracking issue for this feature is: [#132980] [#132980]: https://github.com/rust-lang/rust/issues/132980 ------------------------ + +Warning: This feature is incomplete; its design and syntax may change. + +This feature acts as a minimal alternative to [generic_const_exprs] that allows a smaller subset of functionality, +and uses a different approach for implementation. It is intentionally more restrictive, which helps with avoiding edge +cases that make the `generic_const_exprs` hard to implement properly. See [Feature background][feature_background] +for more details. + +Related features: [generic_const_args], [generic_const_items]. + +[feature_background]: https://github.com/rust-lang/project-const-generics/blob/main/documents/min_const_generics_plan.md +[generic_const_exprs]: generic-const-exprs.md +[generic_const_args]: generic-const-args.md +[generic_const_items]: generic-const-items.md + +## `type const` syntax + +This feature introduces new syntax: `type const`. +Constants marked as `type const` are allowed to be used in type contexts, e.g.: + +```compile_fail +#![allow(incomplete_features)] +#![feature(min_generic_const_args)] + +type const X: usize = 1; +const Y: usize = 1; + +struct Foo { + good_arr: [(); X], // Allowed + bad_arr: [(); Y], // Will not compile, `Y` must be `type const`. +} +``` + +## Examples + +```rust +#![allow(incomplete_features)] +#![feature(min_generic_const_args)] + +trait Bar { + type const VAL: usize; + type const VAL2: usize; +} + +struct Baz; + +impl Bar for Baz { + type const VAL: usize = 2; + type const VAL2: usize = const { Self::VAL * 2 }; +} + +struct Foo<B: Bar> { + arr1: [usize; B::VAL], + arr2: [usize; B::VAL2], +} +``` + +Note that with [generic_const_exprs] the same example would look as follows: + +```rust +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +trait Bar { + const VAL: usize; + const VAL2: usize; +} + +struct Baz; + +impl Bar for Baz { + const VAL: usize = 2; + const VAL2: usize = const { Self::VAL * 2 }; +} + +struct Foo<B: Bar> +where + [(); B::VAL]:, + [(); B::VAL2]:, +{ + arr1: [usize; B::VAL], + arr2: [usize; B::VAL2], +} +``` + +Use of const functions is allowed: + +```rust +#![allow(incomplete_features)] +#![feature(min_generic_const_args)] + +const VAL: usize = 1; + +const fn inc(val: usize) -> usize { + val + 1 +} + +type const INC: usize = const { inc(VAL) }; + +const ARR: [usize; INC] = [0; INC]; +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -11802,6 +12289,22 @@ The tracking issue for this feature is: [#142503] deny_since: None, }, Lint { + label: "pathbuf_into_string", + description: r##"# `pathbuf_into_string` + + + +The tracking issue for this feature is: [#156203] + +[#156203]: https://github.com/rust-lang/rust/issues/156203 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "pattern", description: r##"# `pattern` @@ -11958,6 +12461,20 @@ The tracking issue for this feature is: [#130494] deny_since: None, }, Lint { + label: "pin_macro_internals", + description: r##"# `pin_macro_internals` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "pointer_is_aligned_to", description: r##"# `pointer_is_aligned_to` @@ -14442,6 +14959,22 @@ The tracking issue for this feature is: [#119639] deny_since: None, }, Lint { + label: "tcp_keepalive", + description: r##"# `tcp_keepalive` + + + +The tracking issue for this feature is: [#155889] + +[#155889]: https://github.com/rust-lang/rust/issues/155889 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "tcp_linger", description: r##"# `tcp_linger` @@ -14912,7 +15445,7 @@ pub fn main() { foo(&1); // Use trait alias for trait objects. - let a: &Bar = &123; + let a: &dyn Bar = &123; println!("{:?}", a); let b = Box::new(456) as Box<dyn Foo>; println!("{:?}", b); @@ -16265,6 +16798,22 @@ The tracking issue for this feature is: [#146954] deny_since: None, }, Lint { + label: "view_types", + description: r##"# `view_types` + +Allows view types. + +The tracking issue for this feature is: [#155938] + +[#155938]: https://github.com/rust-lang/rust/issues/155938 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "waker_fn", description: r##"# `waker_fn` @@ -16445,6 +16994,22 @@ This feature is internal to the Rust compiler and is not intended for general us deny_since: None, }, Lint { + label: "windows_permissions_ext", + description: r##"# `windows_permissions_ext` + + + +The tracking issue for this feature is: [#152956] + +[#152956]: https://github.com/rust-lang/rust/issues/152956 + +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { label: "windows_process_exit_code_from", description: r##"# `windows_process_exit_code_from` diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index 08cf1eeed3..838aac2283 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs @@ -7,7 +7,7 @@ use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics}; use span::{Edition, FileId}; use syntax::{ AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset, - ast::{self, make}, + ast::{self, make, syntax_factory::SyntaxFactory}, }; use crate::{ @@ -57,6 +57,31 @@ pub fn mod_path_to_ast(path: &hir::ModPath, edition: Edition) -> ast::Path { make::path_from_segments(segments, is_abs) } +pub fn mod_path_to_ast_with_factory( + make: &SyntaxFactory, + path: &hir::ModPath, + edition: Edition, +) -> ast::Path { + let _p = tracing::info_span!("mod_path_to_ast").entered(); + + let mut segments = Vec::new(); + let mut is_abs = false; + match path.kind { + hir::PathKind::Plain => {} + hir::PathKind::SELF => segments.push(make.path_segment_self()), + hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make.path_segment_super())), + hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => { + segments.push(make.path_segment_crate()) + } + hir::PathKind::Abs => is_abs = true, + } + + segments.extend(path.segments().iter().map(|segment| { + make.path_segment(make.name_ref(&segment.display_no_db(edition).to_smolstr())) + })); + make.path_from_segments(segments, is_abs) +} + /// Iterates all `ModuleDef`s and `Impl` blocks of the given file. pub fn visit_file_defs( sema: &Semantics<'_, RootDatabase>, diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 9018552afb..e0501b5e44 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -314,7 +314,7 @@ pub struct LocatedImport { pub item_to_import: ItemInNs, /// The path import candidate, resolved. /// - /// Not necessary matches the import: + /// Not necessarily matches the import: /// For any associated constant from the trait, we try to access as `some::path::SomeStruct::ASSOC_` /// the original item is the associated constant, but the import has to be a trait that /// defines this constant. @@ -454,6 +454,7 @@ impl<'db> ImportAssets<'db> { |trait_to_import| { !scope_definitions .contains(&ScopeDef::ModuleDef(ModuleDef::Trait(trait_to_import))) + && !scope.can_use_trait_methods(trait_to_import) }, ), } diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs index fe30a4dc5c..c3949f8713 100644 --- a/crates/ide-db/src/imports/insert_use.rs +++ b/crates/ide-db/src/imports/insert_use.rs @@ -6,13 +6,11 @@ use std::cmp::Ordering; use hir::Semantics; use syntax::{ - Direction, NodeOrToken, SyntaxKind, SyntaxNode, algo, + NodeOrToken, SyntaxKind, SyntaxNode, ast::{ - self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind, - edit_in_place::Removable, make, + self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind, edit::IndentLevel, }, syntax_editor::{Position, SyntaxEditor}, - ted, }; use crate::{ @@ -150,24 +148,6 @@ impl ImportScope { ImportScopeKind::Block(block) => block.syntax(), } } - - pub fn clone_for_update(&self) -> Self { - Self { - kind: match &self.kind { - ImportScopeKind::File(file) => ImportScopeKind::File(file.clone_for_update()), - ImportScopeKind::Module(item_list) => { - ImportScopeKind::Module(item_list.clone_for_update()) - } - ImportScopeKind::Block(block) => ImportScopeKind::Block(block.clone_for_update()), - }, - required_cfgs: self.required_cfgs.iter().map(|attr| attr.clone_for_update()).collect(), - } - } -} - -/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. -pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { - insert_use_with_alias_option(scope, path, cfg, None); } /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. @@ -180,11 +160,43 @@ pub fn insert_use_with_editor( insert_use_with_alias_option_with_editor(scope, path, cfg, None, syntax_editor); } -pub fn insert_use_as_alias( +pub fn insert_uses_with_editor( + scope: &ImportScope, + paths: impl IntoIterator<Item = ast::Path>, + cfg: &InsertUseConfig, + syntax_editor: &SyntaxEditor, +) { + let paths = paths.into_iter().collect::<Vec<_>>(); + if paths.len() > 1 + && scope.as_syntax_node().parent().is_none() + && scope.required_cfgs.is_empty() + && !scope.as_syntax_node().children().any(|node| ast::Use::cast(node).is_some()) + { + let make = syntax_editor.make(); + let elements = paths + .into_iter() + .flat_map(|path| { + let use_tree = make.use_tree(path, None, None, false); + let use_item = make.use_(None, None, use_tree); + [use_item.syntax().clone().into(), make.whitespace("\n").into()] + }) + .chain([make.whitespace("\n").into()]) + .collect(); + syntax_editor.insert_all(Position::first_child_of(scope.as_syntax_node()), elements); + return; + } + + for path in paths { + insert_use_with_editor(scope, path, cfg, syntax_editor); + } +} + +pub fn insert_use_as_alias_with_editor( scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig, edition: span::Edition, + editor: &SyntaxEditor, ) { let text: &str = "use foo as _"; let parse = syntax::SourceFile::parse(text, edition); @@ -196,71 +208,7 @@ pub fn insert_use_as_alias( .expect("Failed to make ast node `Rename`"); let alias = node.rename(); - insert_use_with_alias_option(scope, path, cfg, alias); -} - -fn insert_use_with_alias_option( - scope: &ImportScope, - path: ast::Path, - cfg: &InsertUseConfig, - alias: Option<ast::Rename>, -) { - let _p = tracing::info_span!("insert_use_with_alias_option").entered(); - let mut mb = match cfg.granularity { - ImportGranularity::Crate => Some(MergeBehavior::Crate), - ImportGranularity::Module => Some(MergeBehavior::Module), - ImportGranularity::One => Some(MergeBehavior::One), - ImportGranularity::Item => None, - }; - if !cfg.enforce_granularity { - let file_granularity = guess_granularity_from_scope(scope); - mb = match file_granularity { - ImportGranularityGuess::Unknown => mb, - ImportGranularityGuess::Item => None, - ImportGranularityGuess::Module => Some(MergeBehavior::Module), - // We use the user's setting to infer if this is module or item. - ImportGranularityGuess::ModuleOrItem => match mb { - Some(MergeBehavior::Module) | None => mb, - // There isn't really a way to decide between module or item here, so we just pick one. - // FIXME: Maybe it is possible to infer based on semantic analysis? - Some(MergeBehavior::One | MergeBehavior::Crate) => Some(MergeBehavior::Module), - }, - ImportGranularityGuess::Crate => Some(MergeBehavior::Crate), - ImportGranularityGuess::CrateOrModule => match mb { - Some(MergeBehavior::Crate | MergeBehavior::Module) => mb, - Some(MergeBehavior::One) | None => Some(MergeBehavior::Crate), - }, - ImportGranularityGuess::One => Some(MergeBehavior::One), - }; - } - - let mut use_tree = make::use_tree(path, None, alias, false); - if mb == Some(MergeBehavior::One) && use_tree.path().is_some() { - use_tree = use_tree.clone_for_update(); - use_tree.wrap_in_tree_list(); - } - let use_item = make::use_(None, None, use_tree).clone_for_update(); - for attr in - scope.required_cfgs.iter().map(|attr| attr.syntax().clone_subtree().clone_for_update()) - { - ted::insert(ted::Position::first_child_of(use_item.syntax()), attr); - } - - // merge into existing imports if possible - if let Some(mb) = mb { - let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it)); - for existing_use in - scope.as_syntax_node().children().filter_map(ast::Use::cast).filter(filter) - { - if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { - ted::replace(existing_use.syntax(), merged.syntax()); - return; - } - } - } - // either we weren't allowed to merge or there is no import that fits the merge conditions - // so look for the place we have to insert to - insert_use_(scope, use_item, cfg.group); + insert_use_with_alias_option_with_editor(scope, path, cfg, alias, editor); } fn insert_use_with_alias_option_with_editor( @@ -300,14 +248,14 @@ fn insert_use_with_alias_option_with_editor( }; } - let use_tree = make.use_tree(path, None, alias, false); - if mb == Some(MergeBehavior::One) && use_tree.path().is_some() { - use_tree.wrap_in_tree_list(); - } - let use_item = make::use_(None, None, use_tree); - for attr in scope.required_cfgs.iter().map(|attr| attr.syntax().clone()) { - syntax_editor.insert(Position::first_child_of(use_item.syntax()), attr); + let mut use_tree = make.use_tree(path, None, alias, false); + if mb == Some(MergeBehavior::One) + && use_tree.path().is_some() + && let Some(wrapped) = use_tree.wrap_in_tree_list_with_editor() + { + use_tree = wrapped; } + let use_item = make.use_(scope.required_cfgs.iter().cloned().rev(), None, use_tree); // merge into existing imports if possible if let Some(mb) = mb { @@ -326,24 +274,14 @@ fn insert_use_with_alias_option_with_editor( insert_use_with_editor_(scope, use_item, cfg.group, syntax_editor); } -pub fn ast_to_remove_for_path_in_use_stmt(path: &ast::Path) -> Option<Box<dyn Removable>> { - // FIXME: improve this - if path.parent_path().is_some() { - return None; - } - let use_tree = path.syntax().parent().and_then(ast::UseTree::cast)?; +pub fn remove_use_tree_if_simple(use_tree: &ast::UseTree, editor: &SyntaxEditor) { if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() { - return None; + return; } if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) { - return Some(Box::new(use_)); - } - Some(Box::new(use_tree)) -} - -pub fn remove_path_if_in_use_stmt(path: &ast::Path) { - if let Some(node) = ast_to_remove_for_path_in_use_stmt(path) { - node.remove(); + syntax::syntax_editor::Removable::remove(&use_, editor); + } else { + syntax::syntax_editor::Removable::remove(use_tree, editor); } } @@ -482,123 +420,6 @@ fn guess_granularity_from_scope(scope: &ImportScope) -> ImportGranularityGuess { } } -fn insert_use_(scope: &ImportScope, use_item: ast::Use, group_imports: bool) { - let scope_syntax = scope.as_syntax_node(); - let insert_use_tree = - use_item.use_tree().expect("`use_item` should have a use tree for `insert_path`"); - let group = ImportGroup::new(&insert_use_tree); - let path_node_iter = scope_syntax - .children() - .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node))) - .flat_map(|(use_, node)| { - let tree = use_.use_tree()?; - Some((tree, node)) - }); - - if group_imports { - // Iterator that discards anything that's not in the required grouping - // This implementation allows the user to rearrange their import groups as this only takes the first group that fits - let group_iter = path_node_iter - .clone() - .skip_while(|(use_tree, ..)| ImportGroup::new(use_tree) != group) - .take_while(|(use_tree, ..)| ImportGroup::new(use_tree) == group); - - // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place - let mut last = None; - // find the element that would come directly after our new import - let post_insert: Option<(_, SyntaxNode)> = group_iter - .inspect(|(.., node)| last = Some(node.clone())) - .find(|(use_tree, _)| use_tree_cmp(&insert_use_tree, use_tree) != Ordering::Greater); - - if let Some((.., node)) = post_insert { - cov_mark::hit!(insert_group); - // insert our import before that element - return ted::insert(ted::Position::before(node), use_item.syntax()); - } - if let Some(node) = last { - cov_mark::hit!(insert_group_last); - // there is no element after our new import, so append it to the end of the group - return ted::insert(ted::Position::after(node), use_item.syntax()); - } - - // the group we were looking for actually doesn't exist, so insert - - let mut last = None; - // find the group that comes after where we want to insert - let post_group = path_node_iter - .inspect(|(.., node)| last = Some(node.clone())) - .find(|(use_tree, ..)| ImportGroup::new(use_tree) > group); - if let Some((.., node)) = post_group { - cov_mark::hit!(insert_group_new_group); - ted::insert(ted::Position::before(&node), use_item.syntax()); - if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) { - ted::insert(ted::Position::after(node), make::tokens::single_newline()); - } - return; - } - // there is no such group, so append after the last one - if let Some(node) = last { - cov_mark::hit!(insert_group_no_group); - ted::insert(ted::Position::after(&node), use_item.syntax()); - ted::insert(ted::Position::after(node), make::tokens::single_newline()); - return; - } - } else { - // There exists a group, so append to the end of it - if let Some((_, node)) = path_node_iter.last() { - cov_mark::hit!(insert_no_grouping_last); - ted::insert(ted::Position::after(node), use_item.syntax()); - return; - } - } - - let l_curly = match &scope.kind { - ImportScopeKind::File(_) => None, - // don't insert the imports before the item list/block expr's opening curly brace - ImportScopeKind::Module(item_list) => item_list.l_curly_token(), - // don't insert the imports before the item list's opening curly brace - ImportScopeKind::Block(block) => block.l_curly_token(), - }; - // there are no imports in this file at all - // so put the import after all inner module attributes and possible license header comments - if let Some(last_inner_element) = scope_syntax - .children_with_tokens() - // skip the curly brace - .skip(l_curly.is_some() as usize) - .take_while(|child| match child { - NodeOrToken::Node(node) => { - is_inner_attribute(node.clone()) && ast::Item::cast(node.clone()).is_none() - } - NodeOrToken::Token(token) => { - [SyntaxKind::WHITESPACE, SyntaxKind::COMMENT, SyntaxKind::SHEBANG] - .contains(&token.kind()) - } - }) - .filter(|child| child.as_token().is_none_or(|t| t.kind() != SyntaxKind::WHITESPACE)) - .last() - { - cov_mark::hit!(insert_empty_inner_attr); - ted::insert(ted::Position::after(&last_inner_element), use_item.syntax()); - ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline()); - } else { - match l_curly { - Some(b) => { - cov_mark::hit!(insert_empty_module); - ted::insert(ted::Position::after(&b), make::tokens::single_newline()); - ted::insert(ted::Position::after(&b), use_item.syntax()); - } - None => { - cov_mark::hit!(insert_empty_file); - ted::insert( - ted::Position::first_child_of(scope_syntax), - make::tokens::blank_line(), - ); - ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax()); - } - } - } -} - fn insert_use_with_editor_( scope: &ImportScope, use_item: ast::Use, @@ -636,12 +457,18 @@ fn insert_use_with_editor_( if let Some((.., node)) = post_insert { cov_mark::hit!(insert_group); // insert our import before that element - return syntax_editor.insert(Position::before(node), use_item.syntax()); + return syntax_editor.insert_all( + Position::before(node), + vec![use_item.syntax().clone().into(), make.whitespace("\n").into()], + ); } if let Some(node) = last { cov_mark::hit!(insert_group_last); // there is no element after our new import, so append it to the end of the group - return syntax_editor.insert(Position::after(node), use_item.syntax()); + return syntax_editor.insert_all( + Position::after(node), + vec![make.whitespace("\n").into(), use_item.syntax().clone().into()], + ); } // the group we were looking for actually doesn't exist, so insert @@ -653,24 +480,29 @@ fn insert_use_with_editor_( .find(|(use_tree, ..)| ImportGroup::new(use_tree) > group); if let Some((.., node)) = post_group { cov_mark::hit!(insert_group_new_group); - syntax_editor.insert(Position::before(&node), use_item.syntax()); - if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) { - syntax_editor.insert(Position::after(node), make.whitespace("\n")); - } + syntax_editor.insert_all( + Position::before(&node), + vec![use_item.syntax().clone().into(), make.whitespace("\n\n").into()], + ); return; } // there is no such group, so append after the last one if let Some(node) = last { cov_mark::hit!(insert_group_no_group); - syntax_editor.insert(Position::after(&node), use_item.syntax()); - syntax_editor.insert(Position::after(node), make.whitespace("\n")); + syntax_editor.insert_all( + Position::after(&node), + vec![make.whitespace("\n\n").into(), use_item.syntax().clone().into()], + ); return; } } else { // There exists a group, so append to the end of it if let Some((_, node)) = path_node_iter.last() { cov_mark::hit!(insert_no_grouping_last); - syntax_editor.insert(Position::after(node), use_item.syntax()); + syntax_editor.insert_all( + Position::after(node), + vec![make.whitespace("\n").into(), use_item.syntax().clone().into()], + ); return; } } @@ -701,20 +533,38 @@ fn insert_use_with_editor_( .last() { cov_mark::hit!(insert_empty_inner_attr); - syntax_editor.insert(Position::after(&last_inner_element), use_item.syntax()); - syntax_editor.insert(Position::after(last_inner_element), make.whitespace("\n")); + let indent = if l_curly.is_some() { + IndentLevel::from_node(scope_syntax) + 1 + } else { + IndentLevel::zero() + }; + syntax_editor.insert_all( + Position::after(&last_inner_element), + vec![ + make.whitespace(&format!("\n\n{indent}")).into(), + use_item.syntax().clone().into(), + ], + ); } else { match l_curly { Some(b) => { cov_mark::hit!(insert_empty_module); - syntax_editor.insert(Position::after(&b), make.whitespace("\n")); - syntax_editor.insert_with_whitespace(Position::after(&b), use_item.syntax()); + let indent = IndentLevel::from_node(scope_syntax) + 1; + syntax_editor.insert_all( + Position::after(&b), + vec![ + make.whitespace(&format!("\n{indent}")).into(), + use_item.syntax().clone().into(), + make.whitespace("\n").into(), + ], + ); } None => { cov_mark::hit!(insert_empty_file); - syntax_editor - .insert(Position::first_child_of(scope_syntax), make.whitespace("\n\n")); - syntax_editor.insert(Position::first_child_of(scope_syntax), use_item.syntax()); + syntax_editor.insert_all( + Position::first_child_of(scope_syntax), + vec![use_item.syntax().clone().into(), make.whitespace("\n\n").into()], + ); } } } diff --git a/crates/ide-db/src/imports/insert_use/tests.rs b/crates/ide-db/src/imports/insert_use/tests.rs index 6c7b97458d..4fa05c4603 100644 --- a/crates/ide-db/src/imports/insert_use/tests.rs +++ b/crates/ide-db/src/imports/insert_use/tests.rs @@ -1342,14 +1342,14 @@ fn check_with_config( }; let sema = &Semantics::new(&db); let source_file = sema.parse(file_id); + let (editor, _) = SyntaxEditor::new(source_file.syntax().clone()); let file = pos .and_then(|pos| source_file.syntax().token_at_offset(pos.expect_offset()).next()?.parent()) .and_then(|it| ImportScope::find_insert_use_container(&it, sema)) .unwrap_or_else(|| ImportScope { - kind: ImportScopeKind::File(source_file), + kind: ImportScopeKind::File(source_file.clone()), required_cfgs: vec![], - }) - .clone_for_update(); + }); let path = ast::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT) .tree() .syntax() @@ -1357,8 +1357,9 @@ fn check_with_config( .find_map(ast::Path::cast) .unwrap(); - insert_use(&file, path, config); - let result = file.as_syntax_node().ancestors().last().unwrap().to_string(); + insert_use_with_editor(&file, path, config, &editor); + let edit = editor.finish(); + let result = edit.new_root().to_string(); assert_eq_text!(&trim_indent(ra_fixture_after), &result); } diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs index 76645464dd..bbd351a4cc 100644 --- a/crates/ide-db/src/imports/merge_imports.rs +++ b/crates/ide-db/src/imports/merge_imports.rs @@ -256,10 +256,7 @@ pub fn try_normalize_import(use_item: &ast::Use, style: NormalizationStyle) -> O Some(use_item) } -pub fn try_normalize_use_tree_mut( - use_tree: &ast::UseTree, - style: NormalizationStyle, -) -> Option<()> { +fn try_normalize_use_tree_mut(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> { if style == NormalizationStyle::One { let mut modified = false; modified |= use_tree.wrap_in_tree_list().is_some(); diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index 6b72a30339..6180e3186c 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -112,7 +112,7 @@ impl Clone for RootDatabase { storage: self.storage.clone(), files: self.files.clone(), crates_map: self.crates_map.clone(), - nonce: Nonce::new(), + nonce: self.nonce, } } } @@ -402,10 +402,16 @@ impl<'a> MiniCore<'a> { impl std::fmt::Debug for MiniCore<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("MiniCore") - // don't print the whole contents if they correspond to the default - .field(if self.0 == test_utils::MiniCore::RAW_SOURCE { &"<default>" } else { &self.0 }) - .finish() + let mut d = f.debug_tuple("MiniCore"); + if self.0 == test_utils::MiniCore::RAW_SOURCE { + // Don't print the whole contents if they correspond to the default. + // The `format_args!` makes it so that the output is + // `MiniCore(<default>)` and not `MiniCore("<default>"). + d.field(&format_args!("<default>")); + } else { + d.field(&self.0); + }; + d.finish() } } diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index 2d4a6b8b5b..661f0cff8e 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -1,6 +1,6 @@ //! See [`PathTransform`]. -use crate::helpers::mod_path_to_ast; +use crate::helpers::mod_path_to_ast_with_factory; use either::Either; use hir::{ AsAssocItem, FindPathConfig, HirDisplay, HirFileId, ModuleDef, SemanticsScope, @@ -151,7 +151,7 @@ impl<'a> PathTransform<'a> { prettify_macro_expansion( db, node, - &db.expansion_span_map(file_id), + db.expansion_span_map(file_id), self.target_scope.module().krate(db).into(), ) } @@ -218,8 +218,7 @@ impl<'a> PathTransform<'a> { } } (Either::Left(k), None) => { - if let Some(default) = - k.default(db, target_module.krate(db).to_display_target(db)) + if let Some(default) = k.default_source_code(db, target_module) && let Some(default) = default.expr() { const_substs.insert(k, default.syntax().clone()); @@ -354,6 +353,7 @@ impl Ctx<'_> { } fn transform_path_(&self, editor: &SyntaxEditor, path: &ast::Path) -> Option<()> { + let make = editor.make(); if path.qualifier().is_some() { return None; } @@ -397,7 +397,14 @@ impl Ctx<'_> { hir::ModuleDef::Trait(trait_ref), cfg, )?; - match make::ty_path(mod_path_to_ast(&found_path, self.target_edition)) { + match make + .ty_path(mod_path_to_ast_with_factory( + make, + &found_path, + self.target_edition, + )) + .into() + { ast::Type::PathType(path_ty) => Some(path_ty), _ => None, } @@ -447,7 +454,7 @@ impl Ctx<'_> { allow_unstable: true, }; let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?; - let res = mod_path_to_ast(&found_path, self.target_edition); + let res = mod_path_to_ast_with_factory(make, &found_path, self.target_edition); let (res_editor, res) = SyntaxEditor::with_ast_node(&res); if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) && let Some(segment) = res.segment() @@ -501,7 +508,8 @@ impl Ctx<'_> { )?; if let Some(qual) = - mod_path_to_ast(&found_path, self.target_edition).qualifier() + mod_path_to_ast_with_factory(make, &found_path, self.target_edition) + .qualifier() { editor.replace( path.syntax(), @@ -524,8 +532,9 @@ impl Ctx<'_> { fn transform_ident_pat(&self, editor: &SyntaxEditor, ident_pat: &ast::IdentPat) -> Option<()> { let name = ident_pat.name()?; + let make = editor.make(); - let temp_path = make::path_from_text(&name.text()); + let temp_path = make.path_from_text(&name.text()); let resolution = self.source_scope.speculative_resolve(&temp_path)?; @@ -580,7 +589,7 @@ impl Ctx<'_> { let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?; editor.replace( ident_pat.syntax(), - mod_path_to_ast(&found_path, self.target_edition).syntax(), + mod_path_to_ast_with_factory(make, &found_path, self.target_edition).syntax(), ); Some(()) } diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs index 12a48d65ac..fb7edb1acd 100644 --- a/crates/ide-db/src/prime_caches.rs +++ b/crates/ide-db/src/prime_caches.rs @@ -5,7 +5,7 @@ use std::panic::AssertUnwindSafe; use base_db::all_crates; -use hir::{Symbol, import_map::ImportMap}; +use hir::{Symbol, import_map::ImportMap, sym}; use rustc_hash::FxHashMap; use salsa::{Cancelled, Database}; @@ -315,5 +315,5 @@ fn crate_name(db: &RootDatabase, krate: Crate) -> Symbol { .display_name .as_deref() .cloned() - .unwrap_or_else(|| Symbol::integer(salsa::plumbing::AsId::as_id(&krate).index() as usize)) + .unwrap_or_else(|| sym::Integer::get(salsa::plumbing::AsId::as_id(&krate).index() as usize)) } diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index b18ed69d80..ff4d5a2886 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -28,7 +28,9 @@ use crate::{ }; use base_db::AnchoredPathBuf; use either::Either; -use hir::{FieldSource, FileRange, InFile, ModuleSource, Name, Semantics, sym}; +use hir::{FieldSource, FileRange, HasCrate, InFile, ModuleSource, Name, Semantics, sym}; +use itertools::Itertools; +use rustc_hash::FxHashSet; use span::{Edition, FileId, SyntaxContext}; use stdx::{TupleExt, never}; use syntax::{ @@ -405,6 +407,11 @@ fn rename_reference( source_edit_from_references(sema.db, references, def, &new_name, edition), ) })); + + if let Definition::Field(field) = def { + rename_field_constructors(sema, field, &new_name, &mut source_change, config); + } + if rename_definition == RenameDefinition::Yes { // This needs to come after the references edits, because we change the annotation of existing edits // if a conflict is detected. @@ -415,6 +422,104 @@ fn rename_reference( Ok(source_change) } +fn rename_field_constructors( + sema: &Semantics<'_, RootDatabase>, + field: hir::Field, + new_name: &Name, + source_change: &mut SourceChange, + config: &RenameConfig, +) { + let db = sema.db; + let old_name = field.name(db); + let adt = field.parent_def(db).adt(db); + adt.ty(db).iterate_assoc_items(db, |assoc_item| { + let ctor = assoc_item.as_function()?; + if ctor.has_self_param(db) { + return None; + } + if ctor.ret_type(db).as_adt() != Some(adt) { + return None; + } + + let source = sema.source(ctor); + let return_values = sema + .fn_return_points(ctor) + .into_iter() + .filter_map(|ret| ret.value.expr()) + .chain(source.and_then(|source| source.value.body()?.tail_expr())); + // FIXME: We could maybe skip ifs etc.. + + let get_renamed_field = |mut expr| { + while let ast::Expr::ParenExpr(e) = &expr { + expr = e.expr()?; + } + let ast::Expr::RecordExpr(expr) = expr else { return None }; + if sema.type_of_expr(&expr.clone().into())?.original.as_adt()? != adt { + return None; + }; + expr.record_expr_field_list()?.fields().find_map(|record_field| { + if record_field.name_ref().is_none() + && Name::new_root(&record_field.field_name()?.text()) == old_name + && let ast::Expr::PathExpr(field_name) = record_field.expr()? + { + field_name.path() + } else { + None + } + }) + }; + let renamed_fields = return_values + .map(get_renamed_field) + .map(|renamed_field| { + let renamed_field = renamed_field?; + let hir::PathResolution::Local(local) = sema.resolve_path(&renamed_field)? else { + return None; + }; + let range = sema.original_range_opt(renamed_field.syntax())?.range; + Some((range, local)) + }) + .collect::<Option<Vec<_>>>()?; + + let edition = ctor.krate(db).edition(db); + let locals = renamed_fields.iter().map(|&(_, local)| local).collect::<FxHashSet<_>>(); + let mut all_locals_source_change = SourceChange::default(); + for local in locals { + let mut local_source_change = Definition::Local(local) + .rename(sema, new_name.as_str(), RenameDefinition::Yes, config) + .ok()?; + + let (edit, _snippet) = + local_source_change.source_file_edits.values_mut().exactly_one().ok()?; + + // The struct literal will have an edit `old_name -> old_name: new_name`, and we need to remove + // that, as we want an overlapping edit `old_name -> new_name`. + for &(field_range, _) in &renamed_fields { + edit.cancel_edits_touching(field_range); + } + + all_locals_source_change = + std::mem::take(&mut all_locals_source_change).merge(local_source_change); + } + let (edit, _snippet) = + all_locals_source_change.source_file_edits.values_mut().exactly_one().ok()?; + for &(field_range, _) in &renamed_fields { + edit.union(TextEdit::replace(field_range, new_name.display(db, edition).to_string())) + .unwrap(); + } + + let file_id = *all_locals_source_change.source_file_edits.keys().exactly_one().ok()?; + if let Some((edit, _snippet)) = source_change.source_file_edits.get_mut(&file_id) { + for &(field_range, _) in &renamed_fields { + edit.cancel_edits_touching(field_range); + } + } + + *source_change = std::mem::take(source_change).merge(all_locals_source_change); + + None::<std::convert::Infallible> + }); +} + pub fn source_edit_from_references( db: &RootDatabase, references: &[FileReference], @@ -596,28 +701,36 @@ fn source_edit_from_def( for source in local.sources(sema.db) { let source = match source.source.clone().original_ast_node_rooted(sema.db) { Some(source) => source, - None => match source - .source - .syntax() - .original_file_range_opt(sema.db) - .map(TupleExt::head) - { - Some(FileRange { file_id: file_id2, range }) => { - file_id = Some(file_id2); - edit.replace( - range, - new_name.display(sema.db, file_id2.edition(sema.db)).to_string(), - ); - continue; - } - None => { - bail!("Can't rename local that is defined in a macro declaration") + None => { + match source + .as_ident_pat() + .and_then(|x| x.name()) + .and_then(|x| sema.original_range_opt(x.syntax())) + .or_else(|| { + source + .source + .syntax() + .original_file_range_opt(sema.db) + .map(TupleExt::head) + }) { + Some(FileRange { file_id: file_id2, range }) => { + file_id = Some(file_id2); + edit.replace( + range, + new_name.display(sema.db, file_id2.edition(sema.db)).to_string(), + ); + continue; + } + None => { + bail!("Can't rename local that is defined in a macro declaration") + } } - }, + } }; file_id = Some(source.file_id); if let Either::Left(pat) = source.value { let name_range = pat.name().unwrap().syntax().text_range(); + // special cases required for renaming fields/locals in Record patterns if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) { if let Some(name_ref) = pat_field.name_ref() { diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index f41e293070..d59df3601f 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -440,7 +440,7 @@ impl Definition { } } - pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> { + pub fn usages<'a, 'db>(self, sema: &'a Semantics<'db, RootDatabase>) -> FindUsages<'a, 'db> { FindUsages { def: self, rename: None, @@ -456,10 +456,10 @@ impl Definition { } #[derive(Clone)] -pub struct FindUsages<'a> { +pub struct FindUsages<'a, 'db> { def: Definition, rename: Option<&'a Rename>, - sema: &'a Semantics<'a, RootDatabase>, + sema: &'a Semantics<'db, RootDatabase>, scope: Option<&'a SearchScope>, /// The container of our definition should it be an assoc item assoc_item_container: Option<hir::AssocItemContainer>, @@ -473,7 +473,7 @@ pub struct FindUsages<'a> { exclude_library_files: bool, } -impl<'a> FindUsages<'a> { +impl<'a, 'db> FindUsages<'a, 'db> { /// Enable searching for `Self` when the definition is a type or `self` for modules. pub fn include_self_refs(mut self) -> Self { self.include_self_kw_refs = def_to_ty(self.sema, &self.def); @@ -858,7 +858,7 @@ impl<'a> FindUsages<'a> { } fn search( - this: &FindUsages<'_>, + this: &FindUsages<'_, '_>, finder: &Finder<'_>, name: &str, files: impl Iterator<Item = (Arc<str>, EditionedFileId, TextRange)>, diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs index 81b679ead2..766d7251a1 100644 --- a/crates/ide-db/src/source_change.rs +++ b/crates/ide-db/src/source_change.rs @@ -1,11 +1,10 @@ //! This modules defines type to represent changes to the source code, that flow //! from the server to the client. //! -//! It can be viewed as a dual for `Change`. +//! It can be viewed as a dual for [`Change`][vfs::Change]. use std::{collections::hash_map::Entry, fmt, iter, mem}; -use crate::imports::insert_use::{ImportScope, ImportScopeKind}; use crate::text_edit::{TextEdit, TextEditBuilder}; use crate::{SnippetCap, assists::Command, syntax_helpers::tree_diff::diff}; use base_db::AnchoredPathBuf; @@ -16,7 +15,7 @@ use rustc_hash::FxHashMap; use span::FileId; use stdx::never; use syntax::{ - AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, + AstNode, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextSize, syntax_editor::{SyntaxAnnotation, SyntaxEditor}, }; @@ -229,12 +228,12 @@ pub struct SourceChangeBuilder { pub snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>, /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. - pub mutated_tree: Option<TreeMutator>, + mutated_tree: Option<TreeMutator>, /// Keeps track of where to place snippets pub snippet_builder: Option<SnippetBuilder>, } -pub struct TreeMutator { +struct TreeMutator { immutable: SyntaxNode, mutable_clone: SyntaxNode, } @@ -245,23 +244,6 @@ pub struct SnippetBuilder { places: Vec<PlaceSnippet>, } -impl TreeMutator { - pub fn new(immutable: &SyntaxNode) -> TreeMutator { - let immutable = immutable.ancestors().last().unwrap(); - let mutable_clone = immutable.clone_for_update(); - TreeMutator { immutable, mutable_clone } - } - - pub fn make_mut<N: AstNode>(&self, node: &N) -> N { - N::cast(self.make_syntax_mut(node.syntax())).unwrap() - } - - pub fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { - let ptr = SyntaxNodePtr::new(node); - ptr.to_node(&self.mutable_clone) - } -} - impl SourceChangeBuilder { pub fn new(file_id: impl Into<FileId>) -> SourceChangeBuilder { SourceChangeBuilder { @@ -366,34 +348,6 @@ impl SourceChangeBuilder { } } - pub fn make_mut<N: AstNode>(&mut self, node: N) -> N { - self.mutated_tree.get_or_insert_with(|| TreeMutator::new(node.syntax())).make_mut(&node) - } - - pub fn make_import_scope_mut(&mut self, scope: ImportScope) -> ImportScope { - ImportScope { - kind: match scope.kind.clone() { - ImportScopeKind::File(it) => ImportScopeKind::File(self.make_mut(it)), - ImportScopeKind::Module(it) => ImportScopeKind::Module(self.make_mut(it)), - ImportScopeKind::Block(it) => ImportScopeKind::Block(self.make_mut(it)), - }, - required_cfgs: scope.required_cfgs.iter().map(|it| self.make_mut(it.clone())).collect(), - } - } - /// Returns a copy of the `node`, suitable for mutation. - /// - /// Syntax trees in rust-analyzer are typically immutable, and mutating - /// operations panic at runtime. However, it is possible to make a copy of - /// the tree and mutate the copy freely. Mutation is based on interior - /// mutability, and different nodes in the same tree see the same mutations. - /// - /// The typical pattern for an assist is to find specific nodes in the read - /// phase, and then get their mutable counterparts using `make_mut` in the - /// mutable state. - pub fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode { - self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) - } - /// Remove specified `range` of text. pub fn delete(&mut self, range: TextRange) { self.edit.delete(range) @@ -434,12 +388,6 @@ impl SourceChangeBuilder { self.add_snippet(PlaceSnippet::Before(node.syntax().clone().into())); } - /// Adds a tabstop snippet to place the cursor after `node` - pub fn add_tabstop_after(&mut self, _cap: SnippetCap, node: impl AstNode) { - assert!(node.syntax().parent().is_some()); - self.add_snippet(PlaceSnippet::After(node.syntax().clone().into())); - } - /// Adds a tabstop snippet to place the cursor before `token` pub fn add_tabstop_before_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { assert!(token.parent().is_some()); @@ -458,23 +406,6 @@ impl SourceChangeBuilder { self.add_snippet(PlaceSnippet::Over(node.syntax().clone().into())) } - /// Adds a snippet to move the cursor selected over `token` - pub fn add_placeholder_snippet_token(&mut self, _cap: SnippetCap, token: SyntaxToken) { - assert!(token.parent().is_some()); - self.add_snippet(PlaceSnippet::Over(token.into())) - } - - /// Adds a snippet to move the cursor selected over `nodes` - /// - /// This allows for renaming newly generated items without having to go - /// through a separate rename step. - pub fn add_placeholder_snippet_group(&mut self, _cap: SnippetCap, nodes: Vec<SyntaxNode>) { - assert!(nodes.iter().all(|node| node.parent().is_some())); - self.add_snippet(PlaceSnippet::OverGroup( - nodes.into_iter().map(|node| node.into()).collect(), - )) - } - fn add_snippet(&mut self, snippet: PlaceSnippet) { let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] }); snippet_builder.places.push(snippet); @@ -553,9 +484,6 @@ enum PlaceSnippet { After(SyntaxElement), /// Place a placeholder snippet in place of the element Over(SyntaxElement), - /// Place a group of placeholder snippets which are linked together - /// in place of the elements - OverGroup(Vec<SyntaxElement>), } impl PlaceSnippet { @@ -564,9 +492,6 @@ impl PlaceSnippet { PlaceSnippet::Before(it) => vec![Snippet::Tabstop(it.text_range().start())], PlaceSnippet::After(it) => vec![Snippet::Tabstop(it.text_range().end())], PlaceSnippet::Over(it) => vec![Snippet::Placeholder(it.text_range())], - PlaceSnippet::OverGroup(it) => { - vec![Snippet::PlaceholderGroup(it.into_iter().map(|it| it.text_range()).collect())] - } } } } diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs index 09e6115320..76fea5c262 100644 --- a/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -193,7 +193,10 @@ impl NameGenerator { pub fn for_impl_trait_as_generic(&mut self, ty: &ast::ImplTraitType) -> SmolStr { let c = ty .type_bound_list() - .and_then(|bounds| bounds.syntax().text().char_at(0.into())) + .and_then(|bounds| { + let ty = bounds.bounds().next()?.ty()?; + ty.syntax().text().char_at(0.into()).filter(|ch| ch.is_alphabetic()) + }) .unwrap_or('T'); self.suggest_name(&c.to_string()) diff --git a/crates/ide-db/src/text_edit.rs b/crates/ide-db/src/text_edit.rs index d2a73710d5..2dd558b0b7 100644 --- a/crates/ide-db/src/text_edit.rs +++ b/crates/ide-db/src/text_edit.rs @@ -133,9 +133,9 @@ impl TextEdit { let mut res = offset; for indel in &self.indels { if indel.delete.start() >= offset { - break; + continue; } - if offset < indel.delete.end() { + if indel.delete.contains(offset) { return None; } res += TextSize::of(&indel.insert); @@ -151,6 +151,10 @@ impl TextEdit { pub fn change_annotation(&self) -> Option<ChangeAnnotationId> { self.annotation } + + pub fn cancel_edits_touching(&mut self, touching: TextRange) { + self.indels.retain(|indel| indel.delete.intersect(touching).is_none()); + } } impl IntoIterator for TextEdit { diff --git a/crates/ide-db/src/traits.rs b/crates/ide-db/src/traits.rs index 60bdc2d82c..d38d9b6708 100644 --- a/crates/ide-db/src/traits.rs +++ b/crates/ide-db/src/traits.rs @@ -34,38 +34,33 @@ pub fn get_missing_assoc_items( // may share the same name as a function or constant. let mut impl_fns_consts = FxHashSet::default(); let mut impl_type = FxHashSet::default(); - let edition = imp.module(sema.db).krate(sema.db).edition(sema.db); for item in imp.items(sema.db) { match item { hir::AssocItem::Function(it) => { - impl_fns_consts.insert(it.name(sema.db).display(sema.db, edition).to_string()); + impl_fns_consts.insert(it.name(sema.db)); } hir::AssocItem::Const(it) => { if let Some(name) = it.name(sema.db) { - impl_fns_consts.insert(name.display(sema.db, edition).to_string()); + impl_fns_consts.insert(name); } } hir::AssocItem::TypeAlias(it) => { - impl_type.insert(it.name(sema.db).display(sema.db, edition).to_string()); + impl_type.insert(it.name(sema.db)); } } } - resolve_target_trait(sema, impl_def).map_or(vec![], |target_trait| { + imp.trait_(sema.db).map_or(vec![], |target_trait| { target_trait .items(sema.db) .into_iter() .filter(|i| match i { - hir::AssocItem::Function(f) => !impl_fns_consts - .contains(&f.name(sema.db).display(sema.db, edition).to_string()), - hir::AssocItem::TypeAlias(t) => { - !impl_type.contains(&t.name(sema.db).display(sema.db, edition).to_string()) + hir::AssocItem::Function(f) => !impl_fns_consts.contains(&f.name(sema.db)), + hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db)), + hir::AssocItem::Const(c) => { + c.name(sema.db).map(|n| !impl_fns_consts.contains(&n)).unwrap_or_default() } - hir::AssocItem::Const(c) => c - .name(sema.db) - .map(|n| !impl_fns_consts.contains(&n.display(sema.db, edition).to_string())) - .unwrap_or_default(), }) .collect() }) @@ -158,7 +153,8 @@ mod tests { let file = sema.parse(position.file_id); let impl_block: ast::Impl = sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap(); - let items = crate::traits::get_missing_assoc_items(&sema, &impl_block); + let items = + hir::attach_db(&db, || crate::traits::get_missing_assoc_items(&sema, &impl_block)); let actual = items .into_iter() .map(|item| item.name(&db).unwrap().display(&db, Edition::CURRENT).to_string()) diff --git a/crates/ide-db/src/use_trivial_constructor.rs b/crates/ide-db/src/use_trivial_constructor.rs index a91d436afc..0b94a1fa5a 100644 --- a/crates/ide-db/src/use_trivial_constructor.rs +++ b/crates/ide-db/src/use_trivial_constructor.rs @@ -4,7 +4,7 @@ use hir::StructKind; use span::Edition; use syntax::{ ToSmolStr, - ast::{Expr, Path, make}, + ast::{Expr, Path, make, syntax_factory::SyntaxFactory}, }; /// given a type return the trivial constructor (if one exists) @@ -37,3 +37,34 @@ pub fn use_trivial_constructor( None } + +pub fn use_trivial_constructor_with_factory( + make: &SyntaxFactory, + db: &crate::RootDatabase, + path: Path, + ty: &hir::Type<'_>, + edition: Edition, +) -> Option<Expr> { + match ty.as_adt() { + Some(hir::Adt::Enum(x)) => { + if let &[variant] = &*x.variants(db) + && variant.kind(db) == hir::StructKind::Unit + { + let path = make.path_qualified( + path, + make.path_segment( + make.name_ref(&variant.name(db).display_no_db(edition).to_smolstr()), + ), + ); + + return Some(make.expr_path(path)); + } + } + Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => { + return Some(make.expr_path(path)); + } + _ => {} + } + + None +} diff --git a/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs b/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs index 2a7b0098ed..e6adac9da7 100644 --- a/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs +++ b/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticsContext, adjusted_display_range}; // // This diagnostic is triggered if the `await` keyword is used outside of an async function or block pub(crate) fn await_outside_of_async( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::AwaitOutsideOfAsync, ) -> Diagnostic { let display_range = @@ -52,6 +52,7 @@ async fn bar() { fn await_inside_closure() { check_diagnostics( r#" +//- minicore: future async fn foo() {} async fn bar() { @@ -66,6 +67,7 @@ async fn bar() { fn await_inside_async_block() { check_diagnostics( r#" +//- minicore: future async fn foo() {} fn bar() { @@ -79,6 +81,7 @@ fn bar() { fn await_in_complex_context() { check_diagnostics( r#" +//- minicore: future async fn foo() {} fn bar() { diff --git a/crates/ide-diagnostics/src/handlers/bad_rtn.rs b/crates/ide-diagnostics/src/handlers/bad_rtn.rs index ae42a88c31..c84b29dbe2 100644 --- a/crates/ide-diagnostics/src/handlers/bad_rtn.rs +++ b/crates/ide-diagnostics/src/handlers/bad_rtn.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: bad-rtn // // This diagnostic is shown when a RTN (Return Type Notation, `Type::method(..): Send`) is written in an improper place. -pub(crate) fn bad_rtn(ctx: &DiagnosticsContext<'_>, d: &hir::BadRtn) -> Diagnostic { +pub(crate) fn bad_rtn(ctx: &DiagnosticsContext<'_, '_>, d: &hir::BadRtn) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::Ra("bad-rtn", Severity::Error), diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index cbcaab6c74..b7265c47b6 100644 --- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if the `break` keyword is used outside of a loop. pub(crate) fn break_outside_of_loop( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::BreakOutsideOfLoop, ) -> Diagnostic { let message = if d.bad_value_break { @@ -147,7 +147,7 @@ fn test() { r#" //- minicore: option, try fn test() { - try { + let _: Option<_> = try { || { let x = Some(2); Some(x?) diff --git a/crates/ide-diagnostics/src/handlers/duplicate_field.rs b/crates/ide-diagnostics/src/handlers/duplicate_field.rs new file mode 100644 index 0000000000..08748bf8af --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/duplicate_field.rs @@ -0,0 +1,123 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: duplicate-field +// +// This diagnostic is triggered when a record expression or pattern specifies +// the same field more than once. +pub(crate) fn duplicate_field( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::DuplicateField, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0062"), + "field specified more than once", + d.field.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn duplicate_field_in_struct_literal() { + check_diagnostics( + r#" +struct S { foo: i32, bar: i32 } +fn main() { + let _ = S { + foo: 1, + bar: 2, + foo: 3, + //^^^^^^ error: field specified more than once + }; +} +"#, + ); + } + + #[test] + fn duplicate_field_in_enum_variant_literal() { + check_diagnostics( + r#" +enum E { V { foo: i32 } } +fn main() { + let _ = E::V { + foo: 1, + foo: 2, + //^^^^^^ error: field specified more than once + }; +} +"#, + ); + } + + #[test] + fn no_duplicate_when_each_field_specified_once() { + check_diagnostics( + r#" +struct S { foo: i32, bar: i32 } +fn main() { + let _ = S { foo: 1, bar: 2 }; +} +"#, + ); + } + + #[test] + fn no_duplicate_for_unknown_field_falls_through_to_no_such_field() { + check_diagnostics( + r#" +struct S { foo: i32 } +fn main() { + let _ = S { + foo: 1, + bar: 2, + //^^^^^^ 💡 error: no such field + }; +} +"#, + ); + } + + #[test] + fn duplicate_field_in_struct_pattern() { + check_diagnostics( + r#" +struct S { foo: i32, bar: i32 } +fn f(s: S) { + let S { + foo, + bar, + foo, + //^^^ error: field specified more than once + .. + } = s; + let _ = (foo, bar); +} +"#, + ); + } + + #[test] + fn duplicate_field_in_enum_variant_pattern() { + check_diagnostics( + r#" +enum E { V { foo: i32, bar: i32 } } +fn f(e: E) { + match e { + E::V { + foo, + bar, + foo, + //^^^ error: field specified more than once + .. + } => { let _ = (foo, bar); } + } +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs b/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs index b284d9b351..8df9959859 100644 --- a/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs +++ b/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is triggered when lifetimes are elided in paths. It is a lint only for some cases, // and a hard error for others. pub(crate) fn elided_lifetimes_in_path( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::ElidedLifetimesInPath, ) -> Diagnostic { if d.hard_error { diff --git a/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs b/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs new file mode 100644 index 0000000000..ab2c3ccd12 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs @@ -0,0 +1,50 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: expected-array-or-slice-pat +// +// This diagnostic is triggered when an array or slice pattern is matched +// against a type that is neither an array nor a slice. +pub(crate) fn expected_array_or_slice_pat( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ExpectedArrayOrSlicePat<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0529"), + format!( + "expected an array or slice, found {}", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn expected_array_or_slice() { + check_diagnostics( + r#" +fn f([_a, _b]: i32) {} + //^^^^^^^^ error: expected an array or slice, found i32 +"#, + ); + } + + #[test] + fn expected_array_or_slice_let_pattern() { + check_diagnostics( + r#" +fn f(x: i32) { + let [_a, _b] = x; + //^^^^^^^^ error: expected an array or slice, found i32 +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/expected_function.rs b/crates/ide-diagnostics/src/handlers/expected_function.rs index afd1687ae0..25e9dc09eb 100644 --- a/crates/ide-diagnostics/src/handlers/expected_function.rs +++ b/crates/ide-diagnostics/src/handlers/expected_function.rs @@ -6,7 +6,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if a call is made on something that is not callable. pub(crate) fn expected_function( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::ExpectedFunction<'_>, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/crates/ide-diagnostics/src/handlers/field_shorthand.rs b/crates/ide-diagnostics/src/handlers/field_shorthand.rs index 1dc6a7bf9c..0c77fbbd55 100644 --- a/crates/ide-diagnostics/src/handlers/field_shorthand.rs +++ b/crates/ide-diagnostics/src/handlers/field_shorthand.rs @@ -220,4 +220,24 @@ fn f(a: A) { "#, ); } + + #[test] + fn diagnostic_range_respect_allows() { + check_diagnostics( + r#" +#![allow(clippy::redundant_field_names, unused)] + +struct Foo { + bar: u32, +} + +fn main() { + let bar = 23; + let foo = Foo { + bar: bar, + }; +} + "#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs b/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs new file mode 100644 index 0000000000..8b5a235bfb --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs @@ -0,0 +1,55 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: functional-record-update-on-non-struct +// +// This diagnostic is triggered when functional record update syntax is used on +// something other than a struct. +pub(crate) fn functional_record_update_on_non_struct( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::FunctionalRecordUpdateOnNonStruct, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0436"), + "functional record update syntax requires a struct", + d.base_expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn enum_variant_record_update() { + check_diagnostics( + r#" +enum E { + V { x: i32, y: i32 }, +} + +fn f(e: E) { + let _ = E::V { x: 0, ..e }; + //^ error: functional record update syntax requires a struct +} +"#, + ); + } + + #[test] + fn struct_record_update() { + check_diagnostics( + r#" +struct S { + x: i32, + y: i32, +} + +fn f(s: S) { + let _ = S { x: 0, ..s }; +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs index 9ae6f013c7..515878fd47 100644 --- a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs +++ b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs @@ -12,7 +12,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // This diagnostic is shown when generic arguments are provided for a type that does not accept // generic arguments. pub(crate) fn generic_args_prohibited( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::GenericArgsProhibited, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( @@ -42,7 +42,7 @@ fn describe_reason(reason: GenericArgsProhibitedReason) -> String { format!("generic arguments are not allowed on {kind}") } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::GenericArgsProhibited) -> Option<Vec<Assist>> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::GenericArgsProhibited) -> Option<Vec<Assist>> { let file_id = d.args.file_id.file_id()?; let syntax = d.args.to_node(ctx.sema.db); let range = match &syntax { diff --git a/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs b/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs new file mode 100644 index 0000000000..926c517bc9 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs @@ -0,0 +1,43 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: generic-default-refers-to-self +// +// This diagnostic is shown when a generic default refers to `Self` +pub(crate) fn generic_default_refers_to_self( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::GenericDefaultRefersToSelf, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0735"), + "generic parameters cannot use `Self` in their defaults", + d.segment.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn plain_self() { + check_diagnostics( + r#" +struct Foo<T = Self>(T); + // ^^^^ error: generic parameters cannot use `Self` in their defaults +"#, + ); + } + + #[test] + fn self_as_generic() { + check_diagnostics( + r#" +struct Wrapper<T>(T); +struct Foo<T = Wrapper<Self>>(T); + // ^^^^ error: generic parameters cannot use `Self` in their defaults +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs index be4fe763a0..09f3e8bfb3 100644 --- a/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -7,7 +7,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // // This diagnostic is shown for code with inactive `#[cfg]` attributes. pub(crate) fn inactive_code( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::InactiveCode, ) -> Option<Diagnostic> { // If there's inactive code somewhere in a macro, don't propagate to the call-site. diff --git a/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs index a0c364b001..bd8b804af4 100644 --- a/crates/ide-diagnostics/src/handlers/incoherent_impl.rs +++ b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs @@ -6,7 +6,10 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // Diagnostic: incoherent-impl // // This diagnostic is triggered if the targe type of an impl is from a foreign crate. -pub(crate) fn incoherent_impl(ctx: &DiagnosticsContext<'_>, d: &hir::IncoherentImpl) -> Diagnostic { +pub(crate) fn incoherent_impl( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::IncoherentImpl, +) -> Diagnostic { let display_range = adjusted_display_range(ctx, InFile::new(d.file_id, d.impl_), &|node| { Some(TextRange::new( node.syntax().text_range().start(), diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 5410f8b58a..bda3f9bf0a 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -13,7 +13,10 @@ use crate::{ // Diagnostic: incorrect-ident-case // // This diagnostic is triggered if an item name doesn't follow [Rust naming convention](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html). -pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { +pub(crate) fn incorrect_case( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::IncorrectCase, +) -> Diagnostic { let code = match d.expected_case { CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"), CaseType::UpperSnakeCase => DiagnosticCode::RustcLint("non_upper_case_globals"), @@ -33,7 +36,7 @@ pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCas .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> { let root = ctx.sema.db.parse_or_expand(d.file); let name_node = d.ident.to_node(&root); let def = NameClass::classify(&ctx.sema, &name_node)?.defined()?; @@ -1006,18 +1009,18 @@ fn func() { #![allow(unused_variables)] #[warn(nonstandard_style)] fn foo() { - let BAR; + let BAR: i32; // ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar` #[allow(non_snake_case)] - let FOO; + let FOO: i32; } #[warn(nonstandard_style)] fn foo() { - let BAR; + let BAR: i32; // ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar` #[expect(non_snake_case)] - let FOO; + let FOO: i32; #[allow(non_snake_case)] struct qux; // ^^^ 💡 warn: Structure `qux` should have UpperCamelCase name, e.g. `Qux` @@ -1060,7 +1063,7 @@ mod FINE_WITH_BAD_CASE; struct QUX; const foo: i32 = 0; fn BAR() { - let BAZ; + let BAZ: i32; _ = BAZ; } "#, diff --git a/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs index 25220704e0..5ee02279a2 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs +++ b/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs @@ -5,7 +5,7 @@ use hir::IncorrectGenericsLenKind; // // This diagnostic is triggered if the number of generic arguments does not match their declaration. pub(crate) fn incorrect_generics_len( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::IncorrectGenericsLen, ) -> Diagnostic { let owner_description = d.def.description(); diff --git a/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs b/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs index b71586d6be..c2b70a204e 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs +++ b/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs @@ -6,7 +6,7 @@ use syntax::SyntaxKind; // // This diagnostic is triggered the order of provided generic arguments does not match their declaration. pub(crate) fn incorrect_generics_order( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::IncorrectGenericsOrder, ) -> Diagnostic { let provided_description = match d.provided_arg.value.kind() { diff --git a/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/crates/ide-diagnostics/src/handlers/invalid_cast.rs index 405d8df685..bd8fa69e28 100644 --- a/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -18,7 +18,10 @@ macro_rules! format_ty { // Diagnostic: invalid-cast // // This diagnostic is triggered if the code contains an illegal cast -pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_>) -> Diagnostic { +pub(crate) fn invalid_cast( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::InvalidCast<'_>, +) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); let (code, message) = match d.error { CastError::CastToBool => ( @@ -111,7 +114,7 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_ // // This diagnostic is triggered when casting to an unsized type pub(crate) fn cast_to_unsized( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::CastToUnsized<'_>, ) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); @@ -387,7 +390,7 @@ struct Bar; impl Foo for Bar {} -fn to_raw<T>(_: *mut T) -> *mut () { +fn to_raw<T: ?Sized>(_: *mut T) -> *mut () { loop {} } @@ -987,7 +990,7 @@ fn main() { fn rustc_issue_106883() { check_diagnostics_with_disabled( r#" -//- minicore: sized, deref +//- minicore: sized, deref, coerce_unsized, unsize use core::ops::Deref; struct Foo; diff --git a/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs b/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs index 9aa7aed169..8522041b52 100644 --- a/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs +++ b/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is shown when the derive attribute is used on an item other than a `struct`, // `enum` or `union`. pub(crate) fn invalid_derive_target( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::InvalidDeriveTarget, ) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); diff --git a/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs b/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs new file mode 100644 index 0000000000..225d3e0b46 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs @@ -0,0 +1,90 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: invalid-lhs-of-assignment +// +// This diagnostic is triggered if the left-hand side of an assignment can't be assigned to. +pub(crate) fn invalid_lhs_of_assignment( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::InvalidLhsOfAssignment, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0067"), + "invalid left-hand side of assignment", + d.lhs.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn unit_struct_literal() { + check_diagnostics( + r#" +//- minicore: add +struct Struct; +impl core::ops::AddAssign for Struct { + fn add_assign(&mut self, _other: Self) {} +} +fn test() { + Struct += Struct; + // ^^^^^^ error: invalid left-hand side of assignment +} + "#, + ); + } + + #[test] + fn struct_literal() { + check_diagnostics( + r#" +//- minicore: add +struct Struct { foo: i32, bar: i32 } +impl core::ops::AddAssign for Struct { + fn add_assign(&mut self, _other: Self) {} +} +fn test() { + Struct { foo: 0, bar: 0 } += Struct { foo: 1, bar: 2 }; + // ^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid left-hand side of assignment +} + "#, + ); + } + + #[test] + fn destructuring_assignment() { + // no diagnostic, as `=` is not a _compound_ assignment + check_diagnostics( + r#" +//- minicore: add +struct Struct { foo: i32, bar: i32 } +impl core::ops::AddAssign for Struct { + fn add_assign(&mut self, _other: Self) {} +} +fn test(mut foo: i32, mut bar: i32) { + Struct { foo, bar } = Struct { foo: 1, bar: 2 }; +} + "#, + ); + } + + #[test] + fn destructuring_compound_assignment() { + check_diagnostics( + r#" +//- minicore: add +struct Struct { foo: i32, bar: i32 } +impl core::ops::AddAssign for Struct { + fn add_assign(&mut self, _other: Self) {} +} +fn test(foo: i32, bar: i32) { + Struct { foo, bar } += Struct { foo: 1, bar: 2 }; + // ^^^^^^^^^^^^^^^^^^^ error: invalid left-hand side of assignment +} + "#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 20bfcc2dee..24f1e3ad83 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -2,12 +2,11 @@ //! example. use hir::{FindPathConfig, PathResolution, Semantics}; +use ide_db::imports::insert_use::insert_uses_with_editor; use ide_db::text_edit::TextEdit; use ide_db::{ - EditionedFileId, FileRange, FxHashMap, RootDatabase, - helpers::mod_path_to_ast, - imports::insert_use::{ImportScope, insert_use}, - source_change::SourceChangeBuilder, + EditionedFileId, FileRange, FxHashMap, RootDatabase, helpers::mod_path_to_ast, + imports::insert_use::ImportScope, source_change::SourceChangeBuilder, }; use itertools::Itertools; use stdx::{format_to, never}; @@ -138,7 +137,7 @@ pub(crate) fn json_in_items( .stable() .with_fixes(Some(vec![{ let mut scb = SourceChangeBuilder::new(vfs_file_id); - let scope = scb.make_import_scope_mut(import_scope); + let editor = scb.make_editor(import_scope.as_syntax_node()); let current_module = semantics_scope.module(); let cfg = FindPathConfig { @@ -148,6 +147,7 @@ pub(crate) fn json_in_items( allow_unstable: true, }; + let mut imports_to_insert = Vec::new(); if !scope_has("Serialize") && let Some(PathResolution::Def(it)) = serialize_resolved && let Some(it) = current_module.find_use_path( @@ -157,7 +157,7 @@ pub(crate) fn json_in_items( cfg, ) { - insert_use(&scope, mod_path_to_ast(&it, edition), &config.insert_use); + imports_to_insert.push(mod_path_to_ast(&it, edition)); } if !scope_has("Deserialize") && let Some(PathResolution::Def(it)) = deserialize_resolved @@ -168,8 +168,16 @@ pub(crate) fn json_in_items( cfg, ) { - insert_use(&scope, mod_path_to_ast(&it, edition), &config.insert_use); + imports_to_insert.push(mod_path_to_ast(&it, edition)); } + + insert_uses_with_editor( + &import_scope, + imports_to_insert, + &config.insert_use, + &editor, + ); + scb.add_file_edits(vfs_file_id, editor); let mut sc = scb.finish(); sc.insert_source_edit(vfs_file_id, edit.finish()); fix("convert_json_to_struct", "Convert JSON to struct", sc, range) diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs index a44b043f43..b6571e02ef 100644 --- a/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -11,7 +11,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // Diagnostic: proc-macro-disabled // // This diagnostic is shown for proc macros that have been specifically disabled via `rust-analyzer.procMacro.ignored`. -pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic { +pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MacroError) -> Diagnostic { // Use more accurate position if available. let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); Diagnostic::new( @@ -25,7 +25,10 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> // Diagnostic: macro-def-error // // This diagnostic is shown for macro expansion errors. -pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefError) -> Diagnostic { +pub(crate) fn macro_def_error( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MacroDefError, +) -> Diagnostic { // Use more accurate position if available. let display_range = match d.name { Some(name) => ctx.sema.diagnostics_display_range_for_range(d.node.with_value(name)), diff --git a/crates/ide-diagnostics/src/handlers/malformed_derive.rs b/crates/ide-diagnostics/src/handlers/malformed_derive.rs index 7d0c71f4fa..c7d8991f45 100644 --- a/crates/ide-diagnostics/src/handlers/malformed_derive.rs +++ b/crates/ide-diagnostics/src/handlers/malformed_derive.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is shown when the derive attribute has invalid input. pub(crate) fn malformed_derive( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MalformedDerive, ) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 4c0985c7ae..f6293e35d0 100644 --- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -12,7 +12,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. pub(crate) fn mismatched_tuple_struct_pat_arg_count( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MismatchedTupleStructPatArgCount, ) -> Diagnostic { let s = if d.found == 1 { "" } else { "s" }; @@ -33,7 +33,7 @@ pub(crate) fn mismatched_tuple_struct_pat_arg_count( // // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. pub(crate) fn mismatched_arg_count( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MismatchedArgCount, ) -> Diagnostic { let s = if d.expected == 1 { "" } else { "s" }; @@ -47,7 +47,7 @@ pub(crate) fn mismatched_arg_count( } fn invalid_args_range( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, source: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>, expected: usize, found: usize, @@ -205,6 +205,7 @@ trait Foo { fn method(&self, _arg: usize) {} } fn f() { let x; + // ^ error: type annotations needed x.method(); } "#, @@ -453,6 +454,8 @@ fn g() { b::<1, 3>(0, 2); b(0, 1, 2); + // ^ error: type annotations needed + // | full type: `fn b<_, _>(u8, u8)` //^ error: expected 4 arguments, found 3 } "#, diff --git a/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs b/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs new file mode 100644 index 0000000000..8cae405c92 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs @@ -0,0 +1,114 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: mismatched-array-pat-len +// +// This diagnostic is triggered when an array pattern's element count does not +// match the array's declared length. +pub(crate) fn mismatched_array_pat_len( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MismatchedArrayPatLen, +) -> Diagnostic { + let (code, message) = if d.has_rest { + ( + "E0528", + format!( + "pattern requires at least {} element{} but array has {}", + d.found, + if d.found == 1 { "" } else { "s" }, + d.expected, + ), + ) + } else { + ( + "E0527", + format!( + "pattern requires {} element{} but array has {}", + d.found, + if d.found == 1 { "" } else { "s" }, + d.expected, + ), + ) + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError(code), + message, + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn array_pattern_too_few_elements() { + check_diagnostics( + r#" +fn f(arr: [i32; 3]) { + let [_a, _b] = arr; + //^^^^^^^^ error: pattern requires 2 elements but array has 3 +} +"#, + ); + } + + #[test] + fn array_pattern_too_many_elements() { + check_diagnostics( + r#" +fn f(arr: [i32; 2]) { + let [_a, _b, _c] = arr; + //^^^^^^^^^^^^ error: pattern requires 3 elements but array has 2 +} +"#, + ); + } + + #[test] + fn array_pattern_with_rest_too_short() { + check_diagnostics( + r#" +fn f(arr: [i32; 2]) { + let [_a, _b, _c, ..] = arr; + //^^^^^^^^^^^^^^^^ error: pattern requires at least 3 elements but array has 2 +} +"#, + ); + } + + #[test] + fn array_pattern_with_rest_ok() { + check_diagnostics( + r#" +fn f(arr: [i32; 5]) { + let [_a, _b, ..] = arr; +} +"#, + ); + } + + #[test] + fn array_pattern_exact_length_ok() { + check_diagnostics( + r#" +fn f(arr: [i32; 3]) { + let [_a, _b, _c] = arr; +} +"#, + ); + } + + #[test] + fn array_pattern_singular_element_uses_singular() { + check_diagnostics( + r#" +fn f(arr: [i32; 3]) { + let [_a] = arr; + //^^^^ error: pattern requires 1 element but array has 3 +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index 85368cc09f..607f0cbd23 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -18,6 +18,7 @@ use stdx::format_to; use syntax::{ AstNode, Edition, SyntaxNode, SyntaxNodePtr, ToSmolStr, ast::{self, make}, + syntax_editor::SyntaxEditor, }; use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; @@ -33,7 +34,10 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // // let a = A { a: 10 }; // ``` -pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic { +pub(crate) fn missing_fields( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MissingFields, +) -> Diagnostic { let mut message = String::from("missing structure fields:\n"); for (field, _) in &d.missed_fields { format_to!(message, "- {}\n", field.display(ctx.sema.db, ctx.edition)); @@ -51,7 +55,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Assist>> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields) -> Option<Vec<Assist>> { // Note that although we could add a diagnostics to // fill the missing tuple field, e.g : // `struct A(usize);` @@ -112,16 +116,20 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass } }); + let old_field_list = field_list_parent.record_expr_field_list()?; + let root = old_field_list.syntax().ancestors().last()?; + let (editor, _) = SyntaxEditor::new(root); + let make = editor.make(); + let generate_fill_expr = |ty: &Type<'_>| match ctx.config.expr_fill_default { - ExprFillDefaultMode::Todo => make::ext::expr_todo(), - ExprFillDefaultMode::Underscore => make::ext::expr_underscore(), + ExprFillDefaultMode::Todo => make.expr_todo(), + ExprFillDefaultMode::Underscore => make.expr_underscore().into(), ExprFillDefaultMode::Default => { - get_default_constructor(ctx, d, ty).unwrap_or_else(make::ext::expr_todo) + get_default_constructor(ctx, d, ty).unwrap_or_else(|| make.expr_todo()) } }; - let old_field_list = field_list_parent.record_expr_field_list()?; - let new_field_list = old_field_list.clone_for_update(); + let mut new_fields = Vec::new(); for (f, ty) in missing_fields.iter() { let field_expr = if let Some(local_candidate) = locals.get(&f.name(ctx.sema.db)) { cov_mark::hit!(field_shorthand); @@ -156,31 +164,39 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass if expr.is_some() { expr } else { Some(generate_fill_expr(ty)) } }; - let field = make::record_expr_field( - make::name_ref(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()), + let field = make.record_expr_field( + make.name_ref(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()), field_expr, ); - new_field_list.add_field(field.clone_for_update()); + new_fields.push(field); } - build_text_edit(new_field_list.syntax(), old_field_list.syntax()) + old_field_list.add_fields(&editor, new_fields); + let new_field_list = editor.finish().find_element(old_field_list.syntax())?; + build_text_edit(&new_field_list, old_field_list.syntax()) } Either::Right(field_list_parent) => { let missing_fields = ctx.sema.record_pattern_missing_fields(field_list_parent); let old_field_list = field_list_parent.record_pat_field_list()?; - let new_field_list = old_field_list.clone_for_update(); + let root = old_field_list.syntax().ancestors().last()?; + let (editor, _) = SyntaxEditor::new(root); + let make = editor.make(); + + let mut new_fields = Vec::new(); for (f, _) in missing_fields.iter() { - let field = make::record_pat_field_shorthand( - make::ident_pat( + let field = make.record_pat_field_shorthand( + make.ident_pat( false, false, - make::name(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()), + make.name(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()), ) .into(), ); - new_field_list.add_field(field.clone_for_update()); + new_fields.push(field); } - build_text_edit(new_field_list.syntax(), old_field_list.syntax()) + old_field_list.add_fields(&editor, new_fields); + let new_field_list = editor.finish().find_element(old_field_list.syntax())?; + build_text_edit(&new_field_list, old_field_list.syntax()) } } } @@ -202,7 +218,7 @@ fn make_ty( } fn get_default_constructor( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields, ty: &Type<'_>, ) -> Option<ast::Expr> { @@ -293,12 +309,15 @@ fn baz(s: S) -> i32 { #[test] fn missing_record_pat_field_box() { check_diagnostics( - r" + r#" +#![feature(lang_items)] +#[lang = "owned_box"] +struct Box<T>(T); struct S { s: Box<u32> } fn x(a: S) { let S { box s } = a; } -", +"#, ) } diff --git a/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/crates/ide-diagnostics/src/handlers/missing_lifetime.rs index b10cdaa14e..760bb7309d 100644 --- a/crates/ide-diagnostics/src/handlers/missing_lifetime.rs +++ b/crates/ide-diagnostics/src/handlers/missing_lifetime.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered when a lifetime argument is missing. pub(crate) fn missing_lifetime( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingLifetime, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index d52fc73870..7bc7955c4e 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if `match` block is missing one or more match arms. pub(crate) fn missing_match_arms( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingMatchArms, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( @@ -300,7 +300,7 @@ fn main() { } match (true, false) { (true, false, true) => (), - //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, bool) + //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, {unknown}) (true) => (), // ^^^^ error: expected (bool, bool), found bool } @@ -1198,4 +1198,20 @@ fn main() { ); } } + + #[test] + fn no_overloaded_deref_is_not_projection() { + check_diagnostics( + r#" +const FOO: &str = ""; + +fn foo() { + match "" { + FOO => {} + _ => {} + } +} + "#, + ); + } } diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 6a37702fc5..b4ddb239c8 100644 --- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -10,7 +10,10 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // Diagnostic: missing-unsafe // // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. -pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic { +pub(crate) fn missing_unsafe( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MissingUnsafe, +) -> Diagnostic { let code = match d.lint { UnsafeLint::HardError => DiagnosticCode::RustcHardError("E0133"), UnsafeLint::UnsafeOpInUnsafeFn => DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn"), @@ -38,7 +41,7 @@ fn display_unsafety_reason(reason: UnsafetyReason) -> &'static str { } } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option<Vec<Assist>> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingUnsafe) -> Option<Vec<Assist>> { // The fixit will not work correctly for macro expansions, so we don't offer it in that case. if d.node.file_id.is_macro() { return None; @@ -271,25 +274,6 @@ fn main() { } #[test] - fn no_missing_unsafe_diagnostic_with_legacy_safe_intrinsic() { - check_diagnostics( - r#" -extern "rust-intrinsic" { - #[rustc_safe_intrinsic] - pub fn bitreverse(x: u32) -> u32; // Safe intrinsic - pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic -} - -fn main() { - let _ = bitreverse(12); - let _ = floorf32(12.0); - //^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block -} -"#, - ); - } - - #[test] fn no_missing_unsafe_diagnostic_with_deprecated_safe_2024() { check_diagnostics( r#" @@ -414,30 +398,6 @@ fn main() { } #[test] - fn add_unsafe_block_when_calling_unsafe_intrinsic() { - check_fix( - r#" -extern "rust-intrinsic" { - pub fn floorf32(x: f32) -> f32; -} - -fn main() { - let _ = floorf32$0(12.0); -} -"#, - r#" -extern "rust-intrinsic" { - pub fn floorf32(x: f32) -> f32; -} - -fn main() { - let _ = unsafe { floorf32(12.0) }; -} -"#, - ) - } - - #[test] fn unsafe_expr_as_a_receiver_of_a_method_call() { check_fix( r#" @@ -485,7 +445,7 @@ fn main() { let b = &raw const x.a; - let tmp = Vec::from([1, 2, 3]); + let tmp = [1, 2, 3]; let c = &raw const tmp[x.a]; // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block @@ -1059,7 +1019,7 @@ impl FooTrait for S2 { fn no_false_positive_on_format_args_since_1_89_0() { check_diagnostics( r#" -//- minicore: fmt +//- minicore: fmt, builtin_impls fn test() { let foo = 10; let bar = true; diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 6331090d9c..e61719acf5 100644 --- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -5,7 +5,7 @@ use hir::HirDisplay; // // This diagnostic is triggered on moving non copy things out of references. pub(crate) fn moved_out_of_ref( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::MovedOutOfRef<'_>, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 18280a4add..31becd1d74 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -8,7 +8,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // Diagnostic: need-mut // // This diagnostic is triggered on mutating an immutable variable. -pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option<Diagnostic> { +pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NeedMut) -> Option<Diagnostic> { let root = ctx.sema.db.parse_or_expand(d.span.file_id); let node = d.span.value.to_node(&root); let mut span = d.span; @@ -63,7 +63,10 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option // Diagnostic: unused-mut // // This diagnostic is triggered when a mutable variable isn't actually mutated. -pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option<Diagnostic> { +pub(crate) fn unused_mut( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnusedMut, +) -> Option<Diagnostic> { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); let fixes = (|| { let file_id = ast.file_id.file_id()?; @@ -87,7 +90,6 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Op use_range, )]) })(); - let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); Some( Diagnostic::new_with_syntax_node_ptr( ctx, diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index 944622bb1d..7959fddc75 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -15,7 +15,7 @@ use crate::{ // Diagnostic: no-such-field // // This diagnostic is triggered if created structure does not have field provided in record. -pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { +pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NoSuchField) -> Diagnostic { let (code, message) = if d.private.is_some() { ("E0451", "field is private") } else if let VariantId::EnumVariantId(_) = d.variant { @@ -30,7 +30,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> { // FIXME: quickfix for pattern let root = ctx.sema.db.parse_or_expand(d.field.file_id); match &d.field.value.to_node(&root) { diff --git a/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index bc10e82854..ee2f6bf319 100644 --- a/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs @@ -12,7 +12,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // This diagnostic is triggered if a `let` statement without an `else` branch has a non-exhaustive // pattern. pub(crate) fn non_exhaustive_let( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::NonExhaustiveLet, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs b/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs new file mode 100644 index 0000000000..be9c07b1ac --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs @@ -0,0 +1,46 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: non-exhaustive-record-expr +// +// This diagnostic is triggered if a struct expression constructs a `#[non_exhaustive]` +// struct from another crate. +pub(crate) fn non_exhaustive_record_expr( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::NonExhaustiveRecordExpr, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0639"), + "cannot create non-exhaustive struct using struct expression", + d.expr.map(|it| it.into()), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn reports_external_non_exhaustive_struct_literal() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +#[non_exhaustive] +pub struct S { + pub field: u32, +} + +fn local_ok() { + let _ = S { field: 0 }; +} + +//- /main.rs crate:main deps:lib +fn main() { + let _ = lib::S { field: 0 }; + //^^^^^^^^^^^^^^^^^^^ error: cannot create non-exhaustive struct using struct expression +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs b/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs index 68f2b19657..44fc9f482b 100644 --- a/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs +++ b/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is shown when a `Fn`-trait-style generic parameters (`Trait(A, B) -> C`) // was used on non-`Fn` trait/type. pub(crate) fn parenthesized_generic_args_without_fn_trait( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::ParenthesizedGenericArgsWithoutFnTrait, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs b/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs new file mode 100644 index 0000000000..459ec175b1 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs @@ -0,0 +1,86 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: pattern-arg-in-extern-fn +// +// This diagnostic is triggered if a pattern was declared as an argument in a foreign function declaration. +pub(crate) fn pattern_arg_in_extern_fn( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::PatternArgInExternFn, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0130"), + "patterns aren't allowed in foreign function declarations", + d.node.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn tuple_pattern() { + check_diagnostics( + r#" +unsafe extern { fn foo((a, b): (u32, u32)); } + // ^^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + } + + #[test] + fn struct_pattern() { + check_diagnostics( + r#" +struct Foo(u32, u32); +unsafe extern { fn foo(Foo(a, b): Foo); } + // ^^^^^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + + check_diagnostics( + r#" +struct Foo{ bar: u32, baz: u32 } +unsafe extern { fn foo(Foo { bar, baz }: Foo); } + // ^^^^^^^^^^^^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + } + + #[test] + fn pattern_is_second_arg() { + check_diagnostics( + r#" +struct Foo(u32, u32); +unsafe extern { fn foo(okay: u32, Foo(a, b): Foo); } + // ^^^^^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + } + + #[test] + fn non_simple_ident() { + check_diagnostics( + r#" +unsafe extern { fn foo(ref a: u32); } + // ^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + + check_diagnostics( + r#" +unsafe extern { fn foo(mut a: u32); } + // ^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + + check_diagnostics( + r#" +unsafe extern { fn foo(a @ _: u32); } + // ^^^^^ error: patterns aren't allowed in foreign function declarations + "#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs index 6d33ae0cf9..92f3c6961e 100644 --- a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is triggered if the referenced associated item is not visible from the current // module. pub(crate) fn private_assoc_item( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::PrivateAssocItem, ) -> Diagnostic { // FIXME: add quickfix diff --git a/crates/ide-diagnostics/src/handlers/private_field.rs b/crates/ide-diagnostics/src/handlers/private_field.rs index 90c27bdcef..9515afed76 100644 --- a/crates/ide-diagnostics/src/handlers/private_field.rs +++ b/crates/ide-diagnostics/src/handlers/private_field.rs @@ -10,7 +10,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // Diagnostic: private-field // // This diagnostic is triggered if the accessed field is not visible from the current module. -pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) -> Diagnostic { +pub(crate) fn private_field(ctx: &DiagnosticsContext<'_, '_>, d: &hir::PrivateField) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::RustcHardError("E0616"), diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index 2ec41d0528..b5a47e508e 100644 --- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -10,7 +10,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // This diagnostic is triggered when there is a redundant `return` at the end of a function // or closure. pub(crate) fn remove_trailing_return( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &RemoveTrailingReturn, ) -> Option<Diagnostic> { if d.return_expr.file_id.macro_file().is_some() { @@ -36,7 +36,7 @@ pub(crate) fn remove_trailing_return( ) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option<Vec<Assist>> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &RemoveTrailingReturn) -> Option<Vec<Assist>> { let root = ctx.sema.db.parse_or_expand(d.return_expr.file_id); let return_expr = d.return_expr.value.to_node(&root); let stmt = return_expr.syntax().parent().and_then(ast::ExprStmt::cast); @@ -333,7 +333,7 @@ fn foo(x: usize) -> u8 { } } "#, - std::iter::once("remove-unnecessary-else".to_owned()), + &["remove-unnecessary-else"], ); check_fix( r#" diff --git a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs index 04f48ae3db..aa7b57e292 100644 --- a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs +++ b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs @@ -19,7 +19,7 @@ use crate::{ // This diagnostic is triggered when there is an `else` block for an `if` expression whose // then branch diverges (e.g. ends with a `return`, `continue`, `break` e.t.c). pub(crate) fn remove_unnecessary_else( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &RemoveUnnecessaryElse, ) -> Option<Diagnostic> { if d.if_expr.file_id.macro_file().is_some() { @@ -40,7 +40,7 @@ pub(crate) fn remove_unnecessary_else( ) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option<Vec<Assist>> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &RemoveUnnecessaryElse) -> Option<Vec<Assist>> { let root = ctx.sema.db.parse_or_expand(d.if_expr.file_id); let if_expr = d.if_expr.value.to_node(&root); let if_expr = ctx.sema.original_ast_node(if_expr)?; diff --git a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index 37ce5f583f..f974c55023 100644 --- a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -12,7 +12,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // // This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`. pub(crate) fn replace_filter_map_next_with_find_map( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::ReplaceFilterMapNextWithFindMap, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( @@ -26,7 +26,7 @@ pub(crate) fn replace_filter_map_next_with_find_map( } fn fixes( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::ReplaceFilterMapNextWithFindMap, ) -> Option<Vec<Assist>> { let root = ctx.sema.db.parse_or_expand(d.file); diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs index c5b2f499d3..9e7393c89c 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs @@ -7,7 +7,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, adjusted_d // // Diagnoses incorrect safety annotations of trait impls. pub(crate) fn trait_impl_incorrect_safety( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TraitImplIncorrectSafety, ) -> Diagnostic { Diagnostic::new( diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs index 2c05544701..5f5e155bd7 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs @@ -8,7 +8,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // // Diagnoses missing trait items in a trait impl. pub(crate) fn trait_impl_missing_assoc_item( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TraitImplMissingAssocItems, ) -> Diagnostic { let missing = d.missing.iter().format_with(", ", |(name, item), f| { diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs b/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs index 96911d4781..a9dc0d5d72 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs @@ -6,7 +6,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // Only traits defined in the current crate can be implemented for arbitrary types pub(crate) fn trait_impl_orphan( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TraitImplOrphan, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index 6a380481d4..ee972f2d1d 100644 --- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs @@ -16,7 +16,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // Diagnoses redundant trait items in a trait impl. pub(crate) fn trait_impl_redundant_assoc_item( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TraitImplRedundantAssocItems, ) -> Diagnostic { let db = ctx.sema.db; @@ -74,7 +74,7 @@ pub(crate) fn trait_impl_redundant_assoc_item( /// add assoc item into the trait def body fn quickfix_for_redundant_assoc_item( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TraitImplRedundantAssocItems, redundant_item_def: String, range: TextRange, diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 98a4474ef1..250c692d16 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -20,7 +20,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_dis // This diagnostic is triggered when the type of an expression or pattern does not match // the expected type. pub(crate) fn type_mismatch( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, ) -> Option<Diagnostic> { if d.expected.is_unknown() || d.actual.is_unknown() { @@ -64,7 +64,7 @@ pub(crate) fn type_mismatch( ) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<Assist>> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<Assist>> { let mut fixes = Vec::new(); if let Some(expr_ptr) = d.expr_or_pat.value.cast::<ast::Expr>() { @@ -80,7 +80,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Option<Vec< } fn add_reference( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile<AstPtr<ast::Expr>>, acc: &mut Vec<Assist>, @@ -102,7 +102,7 @@ fn add_reference( } fn add_missing_ok_or_some( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile<AstPtr<ast::Expr>>, acc: &mut Vec<Assist>, @@ -197,7 +197,7 @@ fn add_missing_ok_or_some( } fn remove_unnecessary_wrapper( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile<AstPtr<ast::Expr>>, acc: &mut Vec<Assist>, @@ -279,7 +279,7 @@ fn remove_unnecessary_wrapper( } fn remove_semicolon( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile<AstPtr<ast::Expr>>, acc: &mut Vec<Assist>, @@ -310,7 +310,7 @@ fn remove_semicolon( } fn str_ref_to_owned( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile<AstPtr<ast::Expr>>, acc: &mut Vec<Assist>, @@ -338,7 +338,8 @@ fn str_ref_to_owned( #[cfg(test)] mod tests { use crate::tests::{ - check_diagnostics, check_diagnostics_with_disabled, check_fix, check_has_fix, check_no_fix, + check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled, + check_has_fix, check_no_fix, }; #[test] @@ -739,7 +740,7 @@ fn foo() -> Result<(), ()> { check_fix( r#" -//- minicore: result +//- minicore: result, iterator fn foo() -> Result<(), ()> { for _ in 0..5 {}$0 } @@ -755,7 +756,7 @@ fn foo() -> Result<(), ()> { #[test] fn wrapped_unit_as_return_expr() { - check_fix( + check_fix_with_disabled( r#" //- minicore: result fn foo(b: bool) -> Result<(), String> { @@ -773,6 +774,7 @@ fn foo(b: bool) -> Result<(), String> { Err("oh dear".to_owned()) }"#, + &["E0599"], ); } @@ -822,7 +824,7 @@ fn foo() -> SomeOtherEnum { 0$0 } #[test] fn unwrap_return_type() { - check_fix( + check_fix_with_disabled( r#" //- minicore: option, result fn div(x: i32, y: i32) -> i32 { @@ -840,6 +842,7 @@ fn div(x: i32, y: i32) -> i32 { x / y } "#, + &["E0282"], ); } @@ -897,7 +900,7 @@ fn div(x: i32, y: i32) -> i32 { #[test] fn unwrap_return_type_option_tail_unit() { - check_fix( + check_fix_with_disabled( r#" //- minicore: option, result fn div(x: i32, y: i32) { @@ -915,12 +918,13 @@ fn div(x: i32, y: i32) { } } "#, + &["E0282"], ); } #[test] fn unwrap_return_type_handles_generic_functions() { - check_fix( + check_fix_with_disabled( r#" //- minicore: option, result fn div<T>(x: T) -> T { @@ -938,12 +942,13 @@ fn div<T>(x: T) -> T { x } "#, + &["E0282"], ); } #[test] fn unwrap_return_type_handles_type_aliases() { - check_fix( + check_fix_with_disabled( r#" //- minicore: option, result type MyResult<T> = T; @@ -965,12 +970,13 @@ fn div(x: i32, y: i32) -> MyResult<i32> { x / y } "#, + &["E0282"], ); } #[test] fn unwrap_tail_expr() { - check_fix( + check_fix_with_disabled( r#" //- minicore: result fn foo() -> () { @@ -983,12 +989,13 @@ fn foo() -> () { println!("Hello, world!"); } "#, + &["E0282"], ); } #[test] fn unwrap_to_empty_block() { - check_fix( + check_fix_with_disabled( r#" //- minicore: result fn foo() -> () { @@ -998,6 +1005,7 @@ fn foo() -> () { r#" fn foo() -> () {} "#, + &["E0282"], ); } @@ -1190,9 +1198,7 @@ fn f() { let &() = &mut (); //^^^ error: expected &mut (), found &() match &() { - // FIXME: we should only show the deep one. &9 => () - //^^ error: expected &(), found &i32 //^ error: expected (), found i32 } } @@ -1342,6 +1348,8 @@ pub fn foo<T: Foo>(_: T) -> (T::Out,) { loop { } } fn main() { let _x = foo(2); + // ^^ error: type annotations needed + // ^^^ error: the trait bound `i32: Foo` is not satisfied } "#, ); diff --git a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs new file mode 100644 index 0000000000..a03352fe31 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs @@ -0,0 +1,162 @@ +use either::Either; +use hir::{HirDisplay, SpanAst}; +use stdx::format_to; +use syntax::{AstNode, SyntaxNodePtr, ast}; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: type-must-be-known +// +// This diagnostic is triggered when rust-analyzer cannot infer some type. +pub(crate) fn type_must_be_known<'db>( + ctx: &DiagnosticsContext<'db, '_>, + d: &hir::TypeMustBeKnown<'db>, +) -> Diagnostic { + let mut at_point = d.at_point.map(|it| it.syntax_node_ptr()); + let mut top_term = d.top_term.clone(); + + // Do some adjustments to the node: FIXME: We should probably do that at the emitting site. + let node = ctx.sema.to_node(d.at_point); + if let SpanAst::Expr(expr) = &node + && let Some(Either::Left(top_ty)) = &d.top_term + && let Some(expr_ty) = ctx.sema.type_of_expr(expr) + && expr_ty.original == *top_ty + && !top_ty.is_unknown() + && let Some(parent) = expr.syntax().parent().and_then(ast::CallExpr::cast) + && let Some(callable) = top_ty.as_callable(ctx.db()) + && let ret_ty = callable.return_type() + && ret_ty.contains_unknown() + { + top_term = Some(Either::Left(ret_ty)); + at_point.value = SyntaxNodePtr::new(parent.syntax()); + } + + let message = match &top_term { + Some(top_term) if !matches!(top_term, Either::Left(ty) if ty.is_unknown()) => { + let mut message = "type annotations needed\nfull type: `".to_owned(); + match top_term { + Either::Left(ty) => { + format_to!(message, "{}", ty.display(ctx.db(), ctx.display_target)) + } + Either::Right(konst) => message.push_str(konst), + } + message.push_str("`\n"); + message + } + Some(_) => "type annotations needed".to_owned(), + None => "type annotations needed; type must be known at this point".to_owned(), + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0282"), + message, + at_point, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn some_expressions_require_knowing_type() { + check_diagnostics( + r#" +fn foo() { + let var = loop {}; + // ^^^ 💡 warn: unused variable + var(); + // ^^^ error: type annotations needed; type must be known at this point + let var = loop {}; + // ^^^ 💡 warn: unused variable + var[0]; + // ^^^ error: type annotations needed; type must be known at this point +} + "#, + ); + } + + #[test] + fn binding_without_type() { + check_diagnostics( + r#" +fn any<T>() -> T { loop {} } +fn foo() { + let _x = any(); + // ^^^^^ error: type annotations needed +} + "#, + ); + } + + #[test] + fn struct_with_generic() { + check_diagnostics( + r#" +struct X<T>(T); +fn any<T>() -> X<T> { loop {} } +fn foo() { + let _x = any(); + // ^^^^^ error: type annotations needed + // | full type: `X<{unknown}>` +} + "#, + ); + } + + #[test] + fn const_block_does_not_cause_error() { + check_diagnostics( + r#" +fn bar<T>(_inner: fn() -> *const T) {} + +fn foo() { + bar(const { || 0 as *const i32 }) +} + "#, + ); + } + + #[test] + fn async_closure_does_not_trigger() { + check_diagnostics( + r#" +//- minicore: async_fn +struct Task<R>(R); +fn spawn_in<AsyncFn, R>(_f: AsyncFn) -> Task<R> +where + R: 'static, + AsyncFn: AsyncFnOnce(&()) -> R + 'static, +{ + loop {} +} + +fn foo() { + spawn_in(async move |cx| {}); +} + "#, + ); + } + + #[test] + fn regression_22263() { + check_diagnostics( + r#" +trait From<T> {} +impl<T> From<T> for T {} +#[rustc_reservation_impl = "blah blah"] +impl<T> From<!> for T {} + +fn any<T>() -> T { + loop {} +} +fn foo<T, U: From<T>>(_: T) -> U { + loop {} +} +fn bar() { + let _: () = foo(any()); +} + "#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index fd1674e2a4..e000d6388a 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -20,7 +20,10 @@ use syntax::AstNode; // Diagnostic: typed-hole // // This diagnostic is triggered when an underscore expression is used in an invalid position. -pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole<'_>) -> Diagnostic { +pub(crate) fn typed_hole<'db>( + ctx: &DiagnosticsContext<'_, 'db>, + d: &hir::TypedHole<'db>, +) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); let (message, fixes) = if d.expected.is_unknown() { ("`_` expressions may only appear on the left-hand side of an assignment".to_owned(), None) @@ -41,7 +44,7 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole<'_>) - .with_fixes(fixes) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole<'_>) -> Option<Vec<Assist>> { +fn fixes<'db>(ctx: &DiagnosticsContext<'_, 'db>, d: &hir::TypedHole<'db>) -> Option<Vec<Assist>> { let db = ctx.sema.db; let root = db.parse_or_expand(d.expr.file_id); let (original_range, _) = @@ -166,6 +169,8 @@ fn t<T>() -> T { loop {} } r#" fn main() { let _x = [(); _]; + // ^ error: type annotations needed + // | full type: `[(); _]` // FIXME: This should trigger error // let _y: [(); 10] = [(); _]; _ = 0; diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs index f81d34377d..7efc8a7136 100644 --- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -2,7 +2,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: undeclared-label pub(crate) fn undeclared_label( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UndeclaredLabel, ) -> Diagnostic { let name = &d.name; @@ -86,16 +86,18 @@ fn foo() { check_diagnostics( r#" //- minicore: option, try -fn foo() { +fn foo() -> Option<()> { None?; + None } "#, ); check_diagnostics( r#" //- minicore: option, try, future -async fn foo() { +async fn foo() -> Option<()> { None?; + None } "#, ); @@ -103,7 +105,7 @@ async fn foo() { r#" //- minicore: option, try, future, fn async fn foo() { - || None?; + || { None?; Some(()) }; } "#, ); diff --git a/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs b/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs index 5627393f31..b652456c09 100644 --- a/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs +++ b/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity}; // // This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer pub(crate) fn unimplemented_builtin_macro( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnimplementedBuiltinMacro, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs new file mode 100644 index 0000000000..d94ceef642 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs @@ -0,0 +1,72 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: unimplemented-trait +// +// This diagnostic is triggered when rust-analyzer cannot infer some type. +pub(crate) fn unimplemented_trait<'db>( + ctx: &DiagnosticsContext<'_, 'db>, + d: &hir::UnimplementedTrait<'db>, +) -> Diagnostic { + let message = match &d.root_trait_predicate { + Some(root_predicate) if *root_predicate != d.trait_predicate => format!( + "the trait bound `{}` is not satisfied\n\ + required by the bound `{}`\n", + d.trait_predicate.display(ctx.db(), ctx.display_target), + root_predicate.display(ctx.db(), ctx.display_target), + ), + _ => format!( + "the trait bound `{}` is not satisfied", + d.trait_predicate.display(ctx.db(), ctx.display_target), + ), + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0277"), + message, + d.span.map(Into::into), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn smoke_test() { + check_diagnostics( + r#" +trait Trait {} +impl<T: Trait, const N: usize> Trait for [T; N] {} +fn foo(_v: impl Trait) {} +fn bar() { + foo(1); + // ^^^ error: the trait bound `i32: Trait` is not satisfied + foo([1]); + // ^^^ error: the trait bound `i32: Trait` is not satisfied + // | required by the bound `[i32; 1]: Trait` +} + "#, + ); + } + + #[test] + fn async_closure_does_not_trigger() { + check_diagnostics( + r#" +//- minicore: async_fn +fn spawn_in<AsyncFn>(_f: AsyncFn) +where + AsyncFn: AsyncFnOnce(), +{ +} + +fn foo() { + spawn_in(async move || {}); +} + + "#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs b/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs new file mode 100644 index 0000000000..7f1b2da482 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs @@ -0,0 +1,42 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: union-expr-must-have-exactly-one-field +// +// A union expression does not have exactly one field. +pub(crate) fn union_expr_must_have_exactly_one_field( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnionExprMustHaveExactlyOneField, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0784"), + "union expressions should have exactly one field", + d.expr.map(|it| it.into()), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn union_expr_must_have_exactly_one_field() { + check_diagnostics( + r#" +union Bird { + pigeon: u8, + turtledove: u16, +} + +fn main() { + let bird = Bird { pigeon: 0 }; + let bird = Bird {}; + // ^^^^^^^ error: union expressions should have exactly one field + let bird = Bird { pigeon: 0, turtledove: 1 }; + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: union expressions should have exactly one field +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 570319c347..dc6ae6f08b 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -20,7 +20,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, fi // This diagnostic is shown for files that are not included in any crate, or files that are part of // crates rust-analyzer failed to discover. The file will not have IDE features available. pub(crate) fn unlinked_file( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, acc: &mut Vec<Diagnostic>, file_id: FileId, ) { @@ -73,7 +73,7 @@ pub(crate) fn unlinked_file( } fn fixes( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, file_id: FileId, trigger_range: TextRange, ) -> Option<Vec<Assist>> { diff --git a/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/crates/ide-diagnostics/src/handlers/unreachable_label.rs index 0c9e0d6ce4..52138b7cd5 100644 --- a/crates/ide-diagnostics/src/handlers/unreachable_label.rs +++ b/crates/ide-diagnostics/src/handlers/unreachable_label.rs @@ -2,7 +2,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: unreachable-label pub(crate) fn unreachable_label( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnreachableLabel, ) -> Diagnostic { let name = &d.name; diff --git a/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs b/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs index f181021bdc..7797c665fd 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if the referenced associated item does not exist. pub(crate) fn unresolved_assoc_item( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedAssocItem, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs b/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs index 7c3eacf7e3..2c1f1e7283 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate. pub(crate) fn unresolved_extern_crate( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedExternCrate, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 59ec259adf..78e13677cf 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -22,7 +22,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // // This diagnostic is triggered if a field does not exist on a given type. pub(crate) fn unresolved_field( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>, ) -> Diagnostic { let method_suffix = if d.method_with_same_name_exists { @@ -52,7 +52,7 @@ pub(crate) fn unresolved_field( .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Option<Vec<Assist>> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>) -> Option<Vec<Assist>> { let mut fixes = Vec::new(); if d.method_with_same_name_exists { fixes.extend(method_fix(ctx, &d.expr)); @@ -62,7 +62,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Option<V } // FIXME: Add Snippet Support -fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Option<Assist> { +fn field_fix(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>) -> Option<Assist> { // Get the FileRange of the invalid field access let root = ctx.sema.db.parse_or_expand(d.expr.file_id); let expr = d.expr.value.to_node(&root).left()?; @@ -101,7 +101,7 @@ fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Opti } fn add_variant_to_union( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, adt_union: Union, field_name: &str, suggested_type: Type, @@ -129,7 +129,7 @@ fn add_variant_to_union( } fn add_field_to_struct_fix( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, adt_struct: Struct, field_name: &str, suggested_type: Type, @@ -263,7 +263,7 @@ fn record_field_layout( // FIXME: We should fill out the call here, move the cursor and trigger signature help fn method_fix( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, expr_ptr: &InFile<AstPtr<Either<ast::Expr, ast::Pat>>>, ) -> Option<Assist> { let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); diff --git a/crates/ide-diagnostics/src/handlers/unresolved_ident.rs b/crates/ide-diagnostics/src/handlers/unresolved_ident.rs index 801023dabd..6ecf0be825 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_ident.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_ident.rs @@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if an expr-position ident is invalid. pub(crate) fn unresolved_ident( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedIdent, ) -> Diagnostic { let mut range = diff --git a/crates/ide-diagnostics/src/handlers/unresolved_import.rs b/crates/ide-diagnostics/src/handlers/unresolved_import.rs index 0da535d11b..f9a125de13 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_import.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_import.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is triggered if rust-analyzer is unable to resolve a path in // a `use` declaration. pub(crate) fn unresolved_import( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedImport, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( diff --git a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 030c82ca0b..9be7ef6fe7 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs @@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // This diagnostic is triggered if rust-analyzer is unable to resolve the path // to a macro in a macro invocation. pub(crate) fn unresolved_macro_call( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedMacroCall, ) -> Diagnostic { let display_range = ctx.sema.diagnostics_display_range_for_range(d.range); diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs index bd5d134348..93caf281f0 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -17,7 +17,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran // // This diagnostic is triggered if a method does not exist on a given type. pub(crate) fn unresolved_method( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedMethodCall<'_>, ) -> Diagnostic { let suffix = if d.field_with_same_name.is_some() { @@ -49,7 +49,10 @@ pub(crate) fn unresolved_method( .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall<'_>) -> Option<Vec<Assist>> { +fn fixes( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnresolvedMethodCall<'_>, +) -> Option<Vec<Assist>> { let field_fix = if let Some(ty) = &d.field_with_same_name { field_fix(ctx, d, ty) } else { @@ -71,7 +74,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall<'_>) -> Opt } fn field_fix( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedMethodCall<'_>, ty: &hir::Type<'_>, ) -> Option<Assist> { @@ -108,7 +111,7 @@ fn field_fix( } fn assoc_func_fix( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedMethodCall<'_>, ) -> Option<Assist> { if let Some(f) = d.assoc_func_with_same_name { diff --git a/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 1a409d7e76..1e0e9105d8 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_module.rs @@ -9,7 +9,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // // This diagnostic is triggered if rust-analyzer is unable to discover referred module. pub(crate) fn unresolved_module( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedModule, ) -> Diagnostic { Diagnostic::new_with_syntax_node_ptr( @@ -32,7 +32,7 @@ pub(crate) fn unresolved_module( .with_fixes(fixes(ctx, d)) } -fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> { let root = ctx.sema.db.parse_or_expand(d.decl.file_id); let unresolved_module = d.decl.value.to_node(&root); Some( diff --git a/crates/ide-diagnostics/src/handlers/unused_must_use.rs b/crates/ide-diagnostics/src/handlers/unused_must_use.rs new file mode 100644 index 0000000000..e8d0717c91 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/unused_must_use.rs @@ -0,0 +1,132 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: unused-must-use +// +// This diagnostic is triggered when a value with the `#[must_use]` attribute +// is dropped without being used. +pub(crate) fn unused_must_use<'db>( + ctx: &DiagnosticsContext<'_, 'db>, + d: &hir::UnusedMustUse<'db>, +) -> Diagnostic { + let message = match d.message { + Some(message) => format!("unused return value that must be used: {message}"), + None => "unused return value that must be used".to_owned(), + }; + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_must_use"), + message, + d.expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn unused_must_use_function_call() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + produces(); + //^^^^^^^^^^ warn: unused return value that must be used +} +"#, + ); + } + + #[test] + fn unused_must_use_method_call() { + check_diagnostics( + r#" +struct S; +impl S { + #[must_use] + fn produces(&self) -> i32 { 0 } +} +fn main() { + let s = S; + s.produces(); + //^^^^^^^^^^^^ warn: unused return value that must be used +} +"#, + ); + } + + #[test] + fn with_message() { + check_diagnostics( + r#" +struct S; +impl S { + #[must_use = "custom message"] + fn produces(&self) -> i32 { 0 } +} +fn main() { + let s = S; + s.produces(); + //^^^^^^^^^^^^ warn: unused return value that must be used: custom message +} +"#, + ); + } + + #[test] + fn unused_must_use_type() { + check_diagnostics( + r#" +#[must_use] +struct Important; +fn produces() -> Important { Important } +fn main() { + produces(); + //^^^^^^^^^^ warn: unused return value that must be used +} +"#, + ); + } + + #[test] + fn no_warning_when_value_used() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + let _x = produces(); +} +"#, + ); + } + + #[test] + fn no_warning_when_no_must_use_attribute() { + check_diagnostics( + r#" +fn ordinary() -> i32 { 0 } +fn main() { + ordinary(); +} +"#, + ); + } + + #[test] + fn no_warning_when_value_assigned() { + check_diagnostics( + r#" +#[must_use] +fn produces() -> i32 { 0 } +fn main() { + let x; + x = produces(); + let _ = x; +} +"#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs index 52a2f44fd0..afc74445f4 100644 --- a/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -14,7 +14,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered when a local variable is not used. pub(crate) fn unused_variables( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnusedVariable, ) -> Option<Diagnostic> { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 09c9f8eab0..49b3234a11 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -32,9 +32,13 @@ mod handlers { pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; + pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; + pub(crate) mod expected_array_or_slice_pat; pub(crate) mod expected_function; + pub(crate) mod functional_record_update_on_non_struct; pub(crate) mod generic_args_prohibited; + pub(crate) mod generic_default_refers_to_self; pub(crate) mod inactive_code; pub(crate) mod incoherent_impl; pub(crate) mod incorrect_case; @@ -42,9 +46,11 @@ mod handlers { pub(crate) mod incorrect_generics_order; pub(crate) mod invalid_cast; pub(crate) mod invalid_derive_target; + pub(crate) mod invalid_lhs_of_assignment; pub(crate) mod macro_error; pub(crate) mod malformed_derive; pub(crate) mod mismatched_arg_count; + pub(crate) mod mismatched_array_pat_len; pub(crate) mod missing_fields; pub(crate) mod missing_lifetime; pub(crate) mod missing_match_arms; @@ -53,7 +59,9 @@ mod handlers { pub(crate) mod mutability_errors; pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; + pub(crate) mod non_exhaustive_record_expr; pub(crate) mod parenthesized_generic_args_without_fn_trait; + pub(crate) mod pattern_arg_in_extern_fn; pub(crate) mod private_assoc_item; pub(crate) mod private_field; pub(crate) mod remove_trailing_return; @@ -64,9 +72,12 @@ mod handlers { pub(crate) mod trait_impl_orphan; pub(crate) mod trait_impl_redundant_assoc_item; pub(crate) mod type_mismatch; + pub(crate) mod type_must_be_known; pub(crate) mod typed_hole; pub(crate) mod undeclared_label; pub(crate) mod unimplemented_builtin_macro; + pub(crate) mod unimplemented_trait; + pub(crate) mod union_expr_must_have_exactly_one_field; pub(crate) mod unreachable_label; pub(crate) mod unresolved_assoc_item; pub(crate) mod unresolved_extern_crate; @@ -76,6 +87,7 @@ mod handlers { pub(crate) mod unresolved_macro_call; pub(crate) mod unresolved_method; pub(crate) mod unresolved_module; + pub(crate) mod unused_must_use; pub(crate) mod unused_variables; // The handlers below are unusual, the implement the diagnostics as well. @@ -91,7 +103,8 @@ mod tests; use std::sync::LazyLock; use hir::{ - Crate, DisplayTarget, InFile, Semantics, db::ExpandDatabase, diagnostics::AnyDiagnostic, + Crate, DisplayTarget, InFile, MacroCallIdExt, Semantics, db::ExpandDatabase, + diagnostics::AnyDiagnostic, }; use ide_db::{ FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, @@ -190,7 +203,7 @@ impl Diagnostic { } fn new_with_syntax_node_ptr( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, code: DiagnosticCode, message: impl Into<String>, node: InFile<SyntaxNodePtr>, @@ -218,6 +231,25 @@ impl Diagnostic { self.unused = unused; self } + + fn main_node(&self, sema: &Semantics<'_, RootDatabase>) -> Option<InFile<SyntaxNode>> { + self.main_node.map(|ptr| ptr.with_value(sema.to_node_syntax(ptr))).or_else(|| { + let token = sema + .parse_guess_edition(self.range.file_id) + .syntax() + .token_at_offset(self.range.range.start()) + .right_biased()?; + sema.descend_into_macros(token).into_iter().find_map(|token| { + let node = sema.ancestors_with_macros(token.parent().unwrap()).find(|node| { + let original_range = sema.original_range(node); + original_range.file_id.file_id(sema.db) == self.range.file_id + && original_range.range.contains_range(self.range.range) + })?; + let file = sema.hir_file_for(&node); + Some(InFile::new(file, node)) + }) + }) + } } #[derive(Debug, Clone)] @@ -276,17 +308,17 @@ impl DiagnosticsConfig { } } -struct DiagnosticsContext<'a> { +struct DiagnosticsContext<'a, 'db> { config: &'a DiagnosticsConfig, - sema: Semantics<'a, RootDatabase>, + sema: Semantics<'db, RootDatabase>, resolve: &'a AssistResolveStrategy, edition: Edition, display_target: DisplayTarget, is_nightly: bool, } -impl<'a> DiagnosticsContext<'a> { - fn db(&self) -> &'a RootDatabase { +impl<'db> DiagnosticsContext<'_, 'db> { + fn db(&self) -> &'db RootDatabase { self.sema.db } } @@ -395,7 +427,9 @@ pub fn semantic_diagnostics( let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), + AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), + AnyDiagnostic::FunctionalRecordUpdateOnNonStruct(d) => handlers::functional_record_update_on_non_struct::functional_record_update_on_non_struct(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { Some(it) => it, None => continue, @@ -419,6 +453,7 @@ pub fn semantic_diagnostics( }, AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), + AnyDiagnostic::MismatchedArrayPatLen(d) => handlers::mismatched_array_pat_len::mismatched_array_pat_len(&ctx, &d), AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), @@ -428,7 +463,11 @@ pub fn semantic_diagnostics( None => continue, }, AnyDiagnostic::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d), + AnyDiagnostic::NonExhaustiveRecordExpr(d) => { + handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d) + } AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), + AnyDiagnostic::DuplicateField(d) => handlers::duplicate_field::duplicate_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d), AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d), @@ -452,6 +491,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d), AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), + AnyDiagnostic::UnusedMustUse(d) => handlers::unused_must_use::unused_must_use(&ctx, &d), AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) { Some(it) => it, None => continue, @@ -477,6 +517,12 @@ pub fn semantic_diagnostics( AnyDiagnostic::IncorrectGenericsOrder(d) => handlers::incorrect_generics_order::incorrect_generics_order(&ctx, &d), AnyDiagnostic::MissingLifetime(d) => handlers::missing_lifetime::missing_lifetime(&ctx, &d), AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d), + AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), + AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), + AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), + AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), + AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), + AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d), }; res.push(d) } @@ -489,14 +535,7 @@ pub fn semantic_diagnostics( let mut lints = res .iter_mut() .filter(|it| matches!(it.code, DiagnosticCode::Clippy(_) | DiagnosticCode::RustcLint(_))) - .filter_map(|it| { - Some(( - it.main_node.map(|ptr| { - ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id))) - })?, - it, - )) - }) + .filter_map(|it| Some((it.main_node(&ctx.sema)?, it))) .collect::<Vec<_>>(); // The edition isn't accurate (each diagnostics may have its own edition due to macros), @@ -543,7 +582,7 @@ fn handle_diag_from_macros( let mut spans = span_map.spans_for_range(node.text_range()); if spans.any(|span| { span.ctx.outer_expn(sema.db).is_some_and(|expansion| { - let macro_call = sema.db.lookup_intern_macro_call(expansion.into()); + let macro_call = expansion.loc(sema.db); // We don't want to show diagnostics for non-local macros at all, but proc macros authors // seem to rely on being able to emit non-warning-free code, so we don't want to show warnings // for them even when the proc macro comes from the same workspace (in rustc that's not a @@ -766,7 +805,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist { } fn adjusted_display_range<N: AstNode>( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, diag_ptr: InFile<AstPtr<N>>, adj: &dyn Fn(N) -> Option<TextRange>, ) -> FileRange { diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index fc49542e3c..4b9535ca06 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -57,11 +57,11 @@ fn check_nth_fix( pub(crate) fn check_fix_with_disabled( #[rust_analyzer::rust_fixture] ra_fixture_before: &str, #[rust_analyzer::rust_fixture] ra_fixture_after: &str, - disabled: impl Iterator<Item = String>, + disabled: &[&str], ) { let mut config = DiagnosticsConfig::test_sample(); config.expr_fill_default = ExprFillDefaultMode::Default; - config.disabled.extend(disabled); + config.disabled.extend(disabled.iter().map(|&disabled| disabled.to_owned())); check_nth_fix_with_config(config, 0, ra_fixture_before, ra_fixture_after) } diff --git a/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs index 9883bcc84f..301613e920 100644 --- a/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs +++ b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs @@ -2722,6 +2722,13 @@ fn foo() { tracing::error!(); } "#, - &["E0432", "inactive-code", "unresolved-macro-call", "syntax-error", "macro-error"], + &[ + "E0432", + "E0282", + "inactive-code", + "unresolved-macro-call", + "syntax-error", + "macro-error", + ], ); } diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index fb885c2ad1..c2b3a3d8d6 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -6,7 +6,8 @@ use ide_db::{ }; use span::{SpanMap, TextRange, TextSize}; use stdx::format_to; -use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast, ted}; +use syntax::syntax_editor::SyntaxEditor; +use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast}; use crate::FilePosition; @@ -72,7 +73,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< SyntaxKind::MACRO_ITEMS, position.file_id, expansion, - &expansion_span_map, + expansion_span_map, krate, ); if let Some(err) = err { @@ -153,7 +154,6 @@ fn expand_macro_recur( .or_else(|| sema.expand_allowed_builtins(macro_call))?, item => sema.expand_attr_macro(item)?.map(|it| it.value), }; - let expanded = expanded.clone_for_update(); if let Some(err) = err { format_to!(error, "\n{}", err.render_to_string(sema.db)); } @@ -163,7 +163,7 @@ fn expand_macro_recur( result_span_map.merge( TextRange::at(offset_in_original_node, macro_call.syntax().text_range().len()), expanded.text_range().len(), - &expansion_span_map, + expansion_span_map, ); Some(expand(sema, expanded, error, result_span_map, u32::from(offset_in_original_node) as i32)) } @@ -175,6 +175,7 @@ fn expand( result_span_map: &mut SpanMap, mut offset_in_original_node: i32, ) -> SyntaxNode { + let (editor, expanded) = SyntaxEditor::new(expanded); let children = expanded.descendants().filter_map(ast::Item::cast); let mut replacements = Vec::new(); @@ -200,8 +201,8 @@ fn expand( } } - replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); - expanded + replacements.into_iter().rev().for_each(|(old, new)| editor.replace(old.syntax(), new)); + editor.finish().new_root().clone() } fn format( @@ -357,7 +358,7 @@ fn main() { "#, expect![[r#" bar! - for _ in 0..42{}"#]], + for _ in 0..42 {}"#]], ); } @@ -433,9 +434,9 @@ fn main() { expect![[r#" match_ast! { - if let Some(it) = ast::TraitDef::cast(container.clone()){} - else if let Some(it) = ast::ImplDef::cast(container.clone()){} - else { + if let Some(it) = ast::TraitDef::cast(container.clone()){ + }else if let Some(it) = ast::ImplDef::cast(container.clone()){ + }else { { continue } @@ -448,6 +449,7 @@ fn main() { fn macro_expand_match_ast_inside_let_statement() { check( r#" +//- minicore: try macro_rules! match_ast { (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; (match ($node:expr) {}) => {{}}; @@ -593,7 +595,7 @@ struct Foo {} "#, expect![[r#" proc_macros::DeriveIdentity - struct Foo{}"#]], + struct Foo {}"#]], ); } @@ -611,7 +613,7 @@ struct Foo {} expect![[r#" proc_macros::DeriveIdentity #[derive(proc_macros::DeriveIdentity)] - struct Foo{}"#]], + struct Foo {}"#]], ); } @@ -627,7 +629,7 @@ struct Foo {} "#, expect![[r#" proc_macros::DeriveIdentity - struct Foo{}"#]], + struct Foo {}"#]], ); check( r#" @@ -639,7 +641,7 @@ struct Foo {} "#, expect![[r#" proc_macros::DeriveIdentity - struct Foo{}"#]], + struct Foo {}"#]], ); } @@ -782,7 +784,6 @@ foo(); macro_rules! foo { () => { fn item(){} - }; } foo();"#]], diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 4cdf0eac75..4890badcbf 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -95,6 +95,11 @@ pub(crate) fn goto_definition( continue; } + if let Some(n) = find_definition_for_comparison_operators(sema, &token.value) { + navs.extend(n); + continue; + } + let parent = token.value.parent()?; if let Some(question_mark_conversion) = goto_question_mark_conversions(sema, &parent) { @@ -264,6 +269,62 @@ fn find_definition_for_known_blanket_dual_impls( Some(def_to_nav(sema, def)) } +// If the token is a comparison operator (!=, <, <=, >, >=) that resolves to a default trait method, navigate to the corresponding primary method (eq for ne, partial_cmp for the others). +fn find_definition_for_comparison_operators( + sema: &Semantics<'_, RootDatabase>, + original_token: &SyntaxToken, +) -> Option<Vec<NavigationTarget>> { + let bin_expr = ast::BinExpr::cast(original_token.parent()?)?; + + let f = sema.resolve_bin_expr(&bin_expr)?; + let assoc = f.as_assoc_item(sema.db)?; + + let lhs_type = sema.type_of_expr(&bin_expr.lhs()?)?.original; + let rhs_type = sema.type_of_expr(&bin_expr.rhs()?)?.original; + + let t = match assoc.container(sema.db) { + hir::AssocItemContainer::Trait(t) => t, + hir::AssocItemContainer::Impl(_) => return None, // Already implemented by the type + }; + + let fn_name = f.name(sema.db); + let fn_name_str = fn_name.as_str(); + + let trait_name = t.name(sema.db); + let trait_name_str = trait_name.as_str(); + + let (target_fn_name, expected_trait) = match fn_name_str { + "ne" => ("eq", "PartialEq"), + "lt" | "le" | "gt" | "ge" => ("partial_cmp", "PartialOrd"), + _ => return None, + }; + + if trait_name_str != expected_trait { + return None; + } + + let primary_f = t.items(sema.db).into_iter().find_map(|item| { + if let hir::AssocItem::Function(func) = item + && func.name(sema.db).as_str() == target_fn_name + { + return Some(func); + } + None + })?; + + // Chalk requires ALL trait substitutions, including `Self`! + // We must pass [Self, Rhs] + let resolved_f = sema.resolve_trait_impl_method( + lhs_type.clone(), + t, + primary_f, + [lhs_type.clone(), rhs_type.clone()], + )?; + + let def = Definition::from(resolved_f); + + Some(def_to_nav(sema, def)) +} fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, token: InFile<ast::String>, @@ -4099,4 +4160,24 @@ fn foo() -> Result<(), Bar> { "#, ); } + + #[test] + fn goto_definition_for_comparison_operators() { + check( + r#" +//- minicore: eq, ord +struct Foo; +impl PartialEq for Foo { + fn eq(&self, other: &Self) -> bool { true } + //^^ +} + +fn main() { + let a = Foo; + let b = Foo; + let _ = a !=$0 b; +} +"#, + ); + } } diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index e6ef7b8949..12ce457ea6 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -695,14 +695,14 @@ fn merge_map(res: &mut HighlightMap, new: Option<HighlightMap>) { /// Preorder walk all the expression's child expressions. /// For macro calls, the callback will be called on the expanded expressions after /// visiting the macro call itself. -struct WalkExpandedExprCtx<'a> { - sema: &'a Semantics<'a, RootDatabase>, +struct WalkExpandedExprCtx<'a, 'db> { + sema: &'a Semantics<'db, RootDatabase>, depth: usize, check_ctx: &'static dyn Fn(&ast::Expr) -> bool, } -impl<'a> WalkExpandedExprCtx<'a> { - fn new(sema: &'a Semantics<'a, RootDatabase>) -> Self { +impl<'a, 'db> WalkExpandedExprCtx<'a, 'db> { + fn new(sema: &'a Semantics<'db, RootDatabase>) -> Self { Self { sema, depth: 0, check_ctx: &is_closure_or_blk_with_modif } } diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 4d712bf0f0..e08bbc5c21 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -544,7 +544,7 @@ pub(super) fn definition( let mut body = source.value.body()?.syntax().clone(); if let Some(macro_file) = source.file_id.macro_file() { let span_map = db.expansion_span_map(macro_file); - body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into()); + body = prettify_macro_expansion(db, body, span_map, it.krate(db).into()); } if env::var_os("RA_DEV").is_some() { format!("{body}\n{}", render_const_eval_error(db, err, display_target)) @@ -576,7 +576,7 @@ pub(super) fn definition( let mut body = source.value.body()?.syntax().clone(); if let Some(macro_file) = source.file_id.macro_file() { let span_map = db.expansion_span_map(macro_file); - body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into()); + body = prettify_macro_expansion(db, body, span_map, it.krate(db).into()); } if env::var_os("RA_DEV").is_some() { format!("{body}\n{}", render_const_eval_error(db, err, display_target)) @@ -1136,12 +1136,12 @@ fn markup( } } -fn render_memory_layout( +fn render_memory_layout<'db>( config: Option<MemoryLayoutHoverConfig>, - layout: impl FnOnce() -> Result<Layout, LayoutError>, - offset: impl FnOnce(&Layout) -> Option<u64>, - padding: impl FnOnce(&Layout) -> Option<(&str, u64)>, - tag: impl FnOnce(&Layout) -> Option<usize>, + layout: impl FnOnce() -> Result<Layout<'db>, LayoutError>, + offset: impl FnOnce(&Layout<'db>) -> Option<u64>, + padding: impl for<'a> FnOnce(&'a Layout<'db>) -> Option<(&'a str, u64)>, + tag: impl FnOnce(&Layout<'db>) -> Option<usize>, ) -> Option<String> { let config = config?; let layout = layout().ok()?; diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 491471428f..bf5e0be374 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -3392,7 +3392,7 @@ fn main() { let foo_test = unsafe { fo$0o(1, 2, 3); } } ``` ```rust - pub unsafe fn foo(bar: i32, ...) -> i32 + pub unsafe extern "C" fn foo(bar: i32, ...) -> i32 ``` "#]], ); @@ -5972,9 +5972,8 @@ const FOO$0: f64 = 1.0f64; fn hover_const_eval_floating_point() { check( r#" -extern "rust-intrinsic" { - pub fn expf64(x: f64) -> f64; -} +#[rustc_intrinsic] +pub fn expf64(x: f64) -> f64; const FOO$0: f64 = expf64(1.2); "#, @@ -7152,6 +7151,7 @@ fn f() { let expr = [1, 2, $03$0, 4] } fn hover_range_functions() { check_hover_range( r#" +//- minicore: unsize, coerce_unsized fn f<T>(a: &[T]) { } fn b() { $0f$0(&[1, 2, 3, 4, 5]); } "#, @@ -9197,7 +9197,7 @@ extern "C" { ``` ```rust - unsafe fn fun() + unsafe extern "C" fn fun() ``` "#]], ); diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 0d2239c71f..a15366fea9 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -687,21 +687,21 @@ impl fmt::Debug for InlayHintLabelPart { } #[derive(Debug)] -struct InlayHintLabelBuilder<'a> { - sema: &'a Semantics<'a, RootDatabase>, +struct InlayHintLabelBuilder<'a, 'db> { + sema: &'a Semantics<'db, RootDatabase>, result: InlayHintLabel, last_part: String, resolve: bool, location: Option<LazyProperty<FileRange>>, } -impl fmt::Write for InlayHintLabelBuilder<'_> { +impl fmt::Write for InlayHintLabelBuilder<'_, '_> { fn write_str(&mut self, s: &str) -> fmt::Result { self.last_part.write_str(s) } } -impl HirWrite for InlayHintLabelBuilder<'_> { +impl HirWrite for InlayHintLabelBuilder<'_, '_> { fn start_location_link(&mut self, def: ModuleDefId) { never!(self.location.is_some(), "location link is already started"); self.make_new_part(); @@ -737,7 +737,7 @@ impl HirWrite for InlayHintLabelBuilder<'_> { } } -impl InlayHintLabelBuilder<'_> { +impl InlayHintLabelBuilder<'_, '_> { fn make_new_part(&mut self) { let text = take(&mut self.last_part); if !text.is_empty() { @@ -755,18 +755,18 @@ impl InlayHintLabelBuilder<'_> { } } -fn label_of_ty( - famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, +fn label_of_ty<'db>( + famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, 'db>, config: &InlayHintsConfig<'_>, - ty: &hir::Type<'_>, + ty: &hir::Type<'db>, display_target: DisplayTarget, ) -> Option<InlayHintLabel> { - fn rec( - sema: &Semantics<'_, RootDatabase>, - famous_defs: &FamousDefs<'_, '_>, + fn rec<'db>( + sema: &Semantics<'db, RootDatabase>, + famous_defs: &FamousDefs<'_, 'db>, mut max_length: Option<usize>, - ty: &hir::Type<'_>, - label_builder: &mut InlayHintLabelBuilder<'_>, + ty: &hir::Type<'db>, + label_builder: &mut InlayHintLabelBuilder<'_, '_>, config: &InlayHintsConfig<'_>, display_target: DisplayTarget, ) -> Result<(), HirDisplayError> { @@ -790,7 +790,7 @@ fn label_of_ty( ) }); - let module_def_location = |label_builder: &mut InlayHintLabelBuilder<'_>, + let module_def_location = |label_builder: &mut InlayHintLabelBuilder<'_, '_>, def: ModuleDef, name| { let def = def.try_into(); diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index f194bb183e..57b723cbd8 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -1336,7 +1336,7 @@ where { fn f(&self) { let x = self.field.foo(); - //^ impl Baz<<<T as Foo>::Assoc as Bar>::Target> + Bar + //^ impl Baz<<<… as Foo>::Assoc as Bar>::Target> + Bar } } "#, diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs index e8d305afb3..63a83ea335 100644 --- a/crates/ide/src/inlay_hints/binding_mode.rs +++ b/crates/ide/src/inlay_hints/binding_mode.rs @@ -169,13 +169,14 @@ fn __( } match &(0,) { (x,) | (x,) => (), - //^^^^^^^^^^^) - //^^^^^^^^^^^&( + //^^^^& //^ ref //^ ref + //^^^^& ((x,) | (x,)) => (), - //^^^^^^^^^^^^^& + //^^^^& //^ ref + //^^^^& //^ ref } match &mut (0,) { @@ -183,7 +184,8 @@ fn __( //^^^^ &mut //^ ref mut } -}"#, +} +"#, ); } @@ -217,8 +219,8 @@ fn main() { expect![[r#" fn main() { match &(0,) { - &(&((ref x,) | (ref x,))) => (), - &((ref x,) | (ref x,)) => (), + &(ref x,) | &(ref x,) => (), + (&(ref x,) | &(ref x,)) => (), } } "#]], diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs index f4ac9c42f4..df2c42a68c 100644 --- a/crates/ide/src/inlay_hints/closure_captures.rs +++ b/crates/ide/src/inlay_hints/closure_captures.rs @@ -2,7 +2,6 @@ //! //! Tests live in [`bind_pat`][super::bind_pat] module. use ide_db::famous_defs::FamousDefs; -use ide_db::text_edit::{TextRange, TextSize}; use span::Edition; use stdx::{TupleExt, never}; use syntax::ast::{self, AstNode}; @@ -29,14 +28,11 @@ pub(super) fn hints( return None; } - let (range, label) = match closure.move_token() { - Some(t) => (t.text_range(), InlayHintLabel::default()), + let (range, label, position, pad_right) = match closure.move_token() { + Some(t) => (t.text_range(), InlayHintLabel::default(), InlayHintPosition::After, false), None => { - let prev_token = closure.syntax().first_token()?.prev_token()?.text_range(); - ( - TextRange::new(prev_token.end() - TextSize::from(1), prev_token.end()), - InlayHintLabel::from("move"), - ) + let l_pipe = closure.param_list()?.pipe_token()?.text_range(); + (l_pipe, InlayHintLabel::from("move"), InlayHintPosition::Before, true) } }; let mut hint = InlayHint { @@ -44,9 +40,9 @@ pub(super) fn hints( kind: InlayKind::ClosureCapture, label, text_edit: None, - position: InlayHintPosition::After, + position, pad_left: false, - pad_right: true, + pad_right, resolve_parent: Some(closure.syntax().text_range()), }; hint.label.append_str("("); @@ -107,7 +103,7 @@ mod tests { check_with_config( InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, r#" -//- minicore: copy, derive +//- minicore: copy, derive, fn #[derive(Copy, Clone)] @@ -121,29 +117,75 @@ fn main() { let mut baz = NonCopy; let qux = &mut NonCopy; || { -// ^ move(&foo, bar, baz, qux) + // ^ move(&foo, bar, baz, qux) foo; bar; baz; qux; }; || { -// ^ move(&foo, &bar, &baz, &qux) + // ^ move(&foo, &bar, &baz, &qux) &foo; &bar; &baz; &qux; }; || { -// ^ move(&mut baz) + // ^ move(&mut baz) &mut baz; }; || { -// ^ move(&mut baz, &mut *qux) + // ^ move(&mut baz, &mut *qux) + baz = NonCopy; + *qux = NonCopy; + }; +} +"#, + ); + } + + #[test] + fn all_capture_kinds_async_closure() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, derive, fn, future, async_fn + +#[derive(Copy, Clone)] +struct Copy; + +struct NonCopy; + +fn main() { + let foo = Copy; + let bar = NonCopy; + let mut baz = NonCopy; + let qux = &mut NonCopy; + async || { + // ^ move(&foo, bar, baz, qux) + foo; + bar; + baz; + qux; + }; + async || { + // ^ move(&foo, &bar, &baz, &qux) + &foo; + &bar; + &baz; + &qux; + }; + async || { + // ^ move(&mut baz) + &mut baz; + }; + async || { + // ^ move(&mut baz, &mut *qux) baz = NonCopy; *qux = NonCopy; }; } + "#, ); } @@ -163,5 +205,18 @@ fn main() { } "#, ); + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, derive +fn main() { + let foo = u32; + async move || { + // ^^^^ (foo) + foo; + }; +} +"#, + ); } } diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs index 3af529e8c5..57aba51b4e 100644 --- a/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/crates/ide/src/inlay_hints/implicit_drop.rs @@ -37,7 +37,7 @@ pub(super) fn hints( let def = def.try_into().ok()?; let (hir, source_map) = hir::Body::with_source_map(sema.db, def); - let mir = sema.db.mir_body(def).ok()?; + let mir = sema.db.mir_body(def.into()).ok()?; let local_to_binding = mir.local_to_binding_map(); diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 0af2a1f820..e131e7bdd1 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -70,7 +70,7 @@ use ide_db::ra_fixture::RaFixtureAnalysis; use ide_db::{ FxHashMap, FxIndexSet, base_db::{ - CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath, + AbsPathBuf, CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath, salsa::{Cancelled, Database}, }, prime_caches, symbol_index, @@ -253,7 +253,7 @@ impl Analysis { // Creates an analysis instance for a single file, without any external // dependencies, stdlib support or ability to apply changes. See // `AnalysisHost` for creating a fully-featured analysis. - pub fn from_single_file(text: String) -> (Analysis, FileId) { + pub fn from_single_file(text: String, proc_macro_cwd: Arc<AbsPathBuf>) -> (Analysis, FileId) { let mut host = AnalysisHost::default(); let file_id = FileId::from_raw(0); let mut file_set = FileSet::default(); @@ -267,11 +267,6 @@ impl Analysis { // Default to enable test for single file. let mut cfg_options = CfgOptions::default(); - // FIXME: This is less than ideal - let proc_macro_cwd = Arc::new( - TryFrom::try_from(&*std::env::current_dir().unwrap().as_path().to_string_lossy()) - .unwrap(), - ); let crate_attrs = Vec::new(); cfg_options.insert_atom(sym::test); crate_graph.add_crate_root( diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 99f8634bcb..f70bb3353f 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -6,8 +6,7 @@ use arrayvec::ArrayVec; use either::Either; use hir::{ AssocItem, Crate, FieldSource, HasContainer, HasCrate, HasSource, HirDisplay, HirFileId, - InFile, LocalSource, ModuleSource, Name, Semantics, Symbol, db::ExpandDatabase, sym, - symbols::FileSymbol, + InFile, LocalSource, ModuleSource, Name, Semantics, Symbol, sym, symbols::FileSymbol, }; use ide_db::{ FileId, FileRange, RootDatabase, SymbolKind, @@ -580,7 +579,7 @@ impl TryToNav for hir::Field { |(FileRange { file_id, range: full_range }, focus_range)| { NavigationTarget::from_syntax( file_id, - Symbol::integer(self.index()), + sym::Integer::get(self.index()), focus_range, full_range, SymbolKind::Field, @@ -939,10 +938,9 @@ pub(crate) fn orig_range_with_focus_r( ) -> UpmappingResult<(FileRange, Option<TextRange>)> { let Some(name) = focus_range else { return orig_range_r(db, hir_file, value) }; - let call = || db.lookup_intern_macro_call(hir_file.macro_file().unwrap()); + let call = || hir_file.macro_file().unwrap().loc(db); - let def_range = - || db.lookup_intern_macro_call(hir_file.macro_file().unwrap()).def.definition_range(db); + let def_range = || hir_file.macro_file().unwrap().loc(db).def.definition_range(db); // FIXME: Also make use of the syntax context to determine which site we are at? let value_range = InFile::new(hir_file, value).original_node_file_range_opt(db); diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 4ed3d1c7d7..3eb7867a3a 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -580,6 +580,7 @@ pub fn also_calls_foo() { "#, false, false, + // FIXME: The ranges here are volatile when minicore changes, that's not good. expect![[r#" foo Function FileId(1) 0..15 7..10 @@ -599,7 +600,7 @@ fn main() { false, false, expect![[r#" - Some Variant FileId(1) 5999..6031 6024..6028 + Some Variant FileId(1) 6737..6769 6762..6766 FileId(0) 46..50 "#]], diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 900a885a64..b664187932 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -16,7 +16,7 @@ use std::fmt::Write; use stdx::{always, format_to, never}; use syntax::{ AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, - ast::{self, HasArgList, prec::ExprPrecedence}, + ast::{self, HasArgList, make, prec::ExprPrecedence}, }; use ide_db::text_edit::TextEdit; @@ -79,7 +79,10 @@ pub(crate) fn prepare_rename( let sema = Semantics::new(db); let source_file = sema.parse_guess_edition(position.file_id); let syntax = source_file.syntax(); - + if let Some(lifetime_token) = syntax.token_at_offset(position.offset).find(|t| t.text() == "'_") + { + return Ok(RangeInfo::new(lifetime_token.text_range(), ())); + } let res = find_definitions(&sema, syntax, position, &Name::new_symbol_root(sym::underscore))? .filter(|(_, _, def, _, _)| def.range_for_rename(&sema).is_some()) .map(|(frange, kind, _, _, _)| { @@ -133,6 +136,13 @@ pub(crate) fn rename( let edition = file_id.edition(db); let (new_name, kind) = IdentifierKind::classify(edition, new_name)?; + if kind == IdentifierKind::Lifetime + && let Some(lifetime_token) = + syntax.token_at_offset(position.offset).find(|t| t.text() == "'_") + { + let new_name_str = new_name.display(db, edition).to_string(); + return rename_elided_lifetime(position, lifetime_token, &new_name_str); + } let defs = find_definitions(&sema, syntax, position, &new_name)?; let alias_fallback = @@ -797,6 +807,30 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: String) -> O Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) } +fn rename_elided_lifetime( + position: FilePosition, + lifetime_token: syntax::SyntaxToken, + new_name: &str, +) -> RenameResult<SourceChange> { + let parent = lifetime_token.parent().unwrap(); + let root = parent.ancestors().last().unwrap(); + + let mut builder = SourceChangeBuilder::new(position.file_id); + + let editor = builder.make_editor(&root); + + editor.replace(lifetime_token, make::lifetime(new_name).syntax().clone()); + + if let Some(has_generic_params) = parent.ancestors().find_map(ast::AnyHasGenericParams::cast) { + let lifetime_param = make::lifetime_param(make::lifetime(new_name)); + editor.add_generic_param(&has_generic_params, lifetime_param.into()); + } + + builder.add_file_edits(position.file_id, editor); + + Ok(builder.finish()) +} + #[cfg(test)] mod tests { use expect_test::{Expect, expect}; @@ -1326,8 +1360,8 @@ impl Foo { struct Foo { foo$0: i32 } impl Foo { - fn new(foo: i32) -> Self { - Self { foo } + fn foo(foo: i32) { + Self { foo }; } } "#, @@ -1335,8 +1369,8 @@ impl Foo { struct Foo { field: i32 } impl Foo { - fn new(foo: i32) -> Self { - Self { field: foo } + fn foo(foo: i32) { + Self { field: foo }; } } "#, @@ -3930,4 +3964,198 @@ fn bar() { "#, ); } + + #[test] + fn rename_constructor_locals() { + check( + "field", + r#" +struct Struct { + struct_field$0: String, +} + +impl Struct { + fn new(struct_field: String) -> Self { + if false { + return Self { struct_field }; + } + Self { struct_field } + } +} + +mod foo { + macro_rules! m { + ($it:expr) => { return $it }; + } + + impl crate::Struct { + fn with_foo(struct_field: String) -> crate::Struct { + m!(crate::Struct { struct_field }); + } + } +} + "#, + r#" +struct Struct { + field: String, +} + +impl Struct { + fn new(field: String) -> Self { + if false { + return Self { field }; + } + Self { field } + } +} + +mod foo { + macro_rules! m { + ($it:expr) => { return $it }; + } + + impl crate::Struct { + fn with_foo(field: String) -> crate::Struct { + m!(crate::Struct { field }); + } + } +} + "#, + ); + } + + #[test] + fn test_rename_elided_lifetime_fn_no_generics() { + check( + "'a", + r#" +fn foo(x: &'_$0 str) {} +"#, + r#" +fn foo<'a>(x: &'a str) {} +"#, + ); + } + + #[test] + fn test_rename_elided_lifetime_fn_with_generics() { + check( + "'a", + r#" +fn foo<T>(x: &'_$0 str, y: T) {} +"#, + r#" +fn foo<'a, T>(x: &'a str, y: T) {} +"#, + ); + } + + #[test] + fn test_rename_elided_lifetime_impl_no_generics() { + check( + "'a", + r#" +struct Foo<'a>(&'a str); +impl Foo<'_$0> {} +"#, + r#" +struct Foo<'a>(&'a str); +impl<'a> Foo<'a> {} +"#, + ); + } + + #[test] + fn test_rename_elided_lifetime_impl_with_generics() { + check( + "'a", + r#" +struct Foo<'a, T>(&'a str, T); +impl<T> Foo<'_$0, T> {} +"#, + r#" +struct Foo<'a, T>(&'a str, T); +impl<'a, T> Foo<'a, T> {} +"#, + ); + } + + #[test] + fn test_rename_mut_pattern_with_macro() { + check( + "new", + r#" +//- minicore: option +macro_rules! pat_macro { + ($pat:pat) => { + $pat + }; +} + +pub fn main() { + match None { + pat_macro!(Some(mut old$0)) => { + old += 1, + } + None => {} + } +} +"#, + r#" +macro_rules! pat_macro { + ($pat:pat) => { + $pat + }; +} + +pub fn main() { + match None { + pat_macro!(Some(mut new)) => { + new += 1, + } + None => {} + } +} +"#, + ); + } + #[test] + fn test_rename_ref_pattern_with_macro() { + check( + "new", + r#" +//- minicore: option +macro_rules! pat_macro { + ($pat:pat) => { + $pat + }; +} + +pub fn main() { + match None { + pat_macro!(Some(ref old$0)) => { + old += 1, + } + None => {} + } +} +"#, + r#" +macro_rules! pat_macro { + ($pat:pat) => { + $pat + }; +} + +pub fn main() { + match None { + pat_macro!(Some(ref new)) => { + new += 1, + } + None => {} + } +} +"#, + ); + } } diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index cf796b2715..edcf0dc22b 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -381,8 +381,7 @@ fn signature_help_for_generics( } } GenericParam::ConstParam(param) => { - if let Some(expr) = param.default(db, display_target).and_then(|konst| konst.expr()) - { + if let Some(expr) = param.default(db, display_target) { format_to!(buf, " = {}", expr); } } diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 6afe5681a9..76bb06328b 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -9,6 +9,7 @@ use syntax::{ SyntaxNode, TextRange, TextSize, ast::{self, IsString}, }; +use triomphe::Arc; use crate::{ Analysis, HlMod, HlRange, HlTag, RootDatabase, @@ -92,6 +93,7 @@ pub(super) fn doc_comment( Some(it) => it, None => return, }; + let vfs_file_id = src_file_id.file_id(sema.db); let src_file_id: HirFileId = src_file_id.into(); let Some(docs) = attributes.hir_docs(sema.db) else { return }; @@ -171,7 +173,16 @@ pub(super) fn doc_comment( inj.add_unmapped("\n}"); - let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text()); + let proc_macro_cwd = { + match sema.first_crate(vfs_file_id) { + Some(krate) => krate.base().data(sema.db).proc_macro_cwd.clone(), + None => { + // Arbitrarily pick /, since from_single_file treats this file as as /main.rs anyway. + Arc::new(ide_db::base_db::AbsPathBuf::try_from("/").unwrap()) + } + } + }; + let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text(), proc_macro_cwd); if let Ok(ranges) = analysis.with_db(|db| { super::highlight( diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index fa7f7b1cba..6b63edf563 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -54,7 +54,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="comment">// This is another normal comment</span> <span class="attribute_bracket attribute macro proc_macro">#</span><span class="attribute_bracket attribute macro proc_macro">[</span><span class="attribute attribute default_library library macro proc_macro">derive</span><span class="parenthesis attribute macro proc_macro">(</span><span class="derive attribute default_library library macro">Copy</span><span class="comma attribute macro proc_macro">,</span> <span class="unresolved_reference attribute macro">Unresolved</span><span class="parenthesis attribute macro proc_macro">)</span><span class="attribute_bracket attribute macro proc_macro">]</span> <span class="comment">// The reason for these being here is to test AttrIds</span> +<span class="attribute_bracket attribute macro proc_macro">#</span><span class="attribute_bracket attribute macro proc_macro">[</span><span class="derive_helper attribute default_library library macro proc_macro">default</span><span class="attribute_bracket attribute macro proc_macro">]</span> <span class="keyword macro proc_macro">enum</span> <span class="enum declaration macro proc_macro">Foo</span> <span class="brace macro proc_macro">{</span> <span class="attribute_bracket attribute macro proc_macro">#</span><span class="attribute_bracket attribute macro proc_macro">[</span><span class="derive_helper attribute default_library library macro proc_macro">default</span><span class="attribute_bracket attribute macro proc_macro">]</span> - <span class="enum_variant declaration macro proc_macro">Bar</span> -<span class="brace macro proc_macro">}</span></code></pre>
\ No newline at end of file + <span class="enum_variant declaration macro proc_macro">Bar</span> <span class="brace macro proc_macro">{</span> + <span class="attribute_bracket attribute macro proc_macro">#</span><span class="attribute_bracket attribute macro proc_macro">[</span><span class="derive_helper attribute default_library library macro proc_macro">default</span><span class="attribute_bracket attribute macro proc_macro">]</span> + <span class="field declaration macro proc_macro">field</span><span class="colon macro proc_macro">:</span> <span class="builtin_type macro proc_macro">i32</span> + <span class="brace macro proc_macro">}</span> +<span class="brace macro proc_macro">}</span> + +<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library macro">Default</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> +<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="derive_helper attribute default_library library">default</span><span class="attribute_bracket attribute">]</span> +<span class="keyword">struct</span> <span class="struct declaration">Bar</span><span class="parenthesis">(</span><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="derive_helper attribute default_library library">default</span><span class="attribute_bracket attribute">]</span> <span class="builtin_type">i32</span><span class="parenthesis">)</span><span class="semicolon">;</span></code></pre>
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index d687cb40a9..ab69578ed9 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -39,10 +39,18 @@ fn attributes() { // This is another normal comment #[derive(Copy, Unresolved)] // The reason for these being here is to test AttrIds +#[default] enum Foo { #[default] - Bar + Bar { + #[default] + field: i32 + } } + +#[derive(Default)] +#[default] +struct Bar(#[default] i32); "#, expect_file!["./test_data/highlight_attributes.html"], false, @@ -1348,7 +1356,7 @@ fn benchmark_syntax_highlighting_parser() { }) .count() }; - assert_eq!(hash, 1631); + assert_eq!(hash, 1644); } #[test] diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs index 47ca616f31..1b9df9722b 100644 --- a/crates/ide/src/view_memory_layout.rs +++ b/crates/ide/src/view_memory_layout.rs @@ -108,7 +108,7 @@ pub(crate) fn view_memory_layout( nodes: &mut Vec<MemoryLayoutNode>, db: &RootDatabase, ty: &Type<'_>, - layout: &Layout, + layout: &Layout<'_>, parent_idx: usize, display_target: DisplayTarget, ) { diff --git a/crates/intern/Cargo.toml b/crates/intern/Cargo.toml index 39320ebd1c..1e6e8ff32f 100644 --- a/crates/intern/Cargo.toml +++ b/crates/intern/Cargo.toml @@ -19,6 +19,7 @@ hashbrown.workspace = true rustc-hash.workspace = true triomphe.workspace = true rayon.workspace = true +arrayvec.workspace = true [lints] workspace = true diff --git a/crates/intern/src/symbol.rs b/crates/intern/src/symbol.rs index 8b2d6e8717..72d32d1017 100644 --- a/crates/intern/src/symbol.rs +++ b/crates/intern/src/symbol.rs @@ -73,10 +73,9 @@ impl TaggedArcPtr { /// /// You can only drop the `Arc` if the instance is dropped. #[inline] - pub(crate) unsafe fn try_as_arc_owned(self) -> Option<ManuallyDrop<Arc<Box<str>>>> { + unsafe fn try_as_arc_owned(self) -> Option<ManuallyDrop<Arc<Box<str>>>> { // Unpack the tag from the alignment niche - let tag = self.packed.as_ptr().addr() & Self::BOOL_BITS; - if tag != 0 { + if self.is_arc() { // Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc` Some(ManuallyDrop::new(unsafe { Arc::from_raw(self.pointer().as_ptr().cast::<Box<str>>()) @@ -87,6 +86,11 @@ impl TaggedArcPtr { } #[inline] + fn is_arc(&self) -> bool { + (self.packed.as_ptr().addr() & Self::BOOL_BITS) != 0 + } + + #[inline] fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> { let packed_tag = true as usize; @@ -161,28 +165,6 @@ impl Symbol { unsafe { bucket.as_ref().0.clone() } } - pub fn integer(i: usize) -> Self { - match i { - 0 => symbols::INTEGER_0, - 1 => symbols::INTEGER_1, - 2 => symbols::INTEGER_2, - 3 => symbols::INTEGER_3, - 4 => symbols::INTEGER_4, - 5 => symbols::INTEGER_5, - 6 => symbols::INTEGER_6, - 7 => symbols::INTEGER_7, - 8 => symbols::INTEGER_8, - 9 => symbols::INTEGER_9, - 10 => symbols::INTEGER_10, - 11 => symbols::INTEGER_11, - 12 => symbols::INTEGER_12, - 13 => symbols::INTEGER_13, - 14 => symbols::INTEGER_14, - 15 => symbols::INTEGER_15, - i => Symbol::intern(&format!("{i}")), - } - } - pub fn empty() -> Self { symbols::__empty } diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index 614411598b..ac6daaf006 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -1,15 +1,78 @@ //! Module defining all known symbols required by the rest of rust-analyzer. #![allow(non_upper_case_globals)] -use std::hash::{BuildHasher, BuildHasherDefault}; +use std::{ + hash::{BuildHasher, BuildHasherDefault}, + ptr::NonNull, +}; +use arrayvec::ArrayString; use dashmap::{DashMap, SharedValue}; use rustc_hash::FxHasher; use crate::{Symbol, symbol::TaggedArcPtr}; +macro_rules! last { + ( $($elems:literal,)+ ) => { + *[ $($elems,)* ].last().unwrap() + }; +} + +impl Integer { + #[inline] + pub fn as_uint(sym: &Symbol) -> Option<usize> { + if !sym.repr.is_arc() { + let elem_ref = sym.repr.pointer(); + // SAFETY: The types have the same layout. + let elem_ref = unsafe { std::mem::transmute::<NonNull<*const str>, &&str>(elem_ref) }; + Self::LIST.element_offset(elem_ref) + } else { + Self::as_uint_cold(sym) + } + } + + #[cold] + fn as_uint_cold(sym: &Symbol) -> Option<usize> { + sym.as_str().parse().ok() + } +} + macro_rules! define_symbols { - (@WITH_NAME: $($alias:ident = $value:literal,)* @PLAIN: $($name:ident,)*) => { + ( + @LISTS: $($list_type_name:ident = $list_prefix:literal + [ $($list_idx:literal,)+ ],)* + @WITH_NAME: $($alias:ident = $value:literal,)* + @PLAIN: $($name:ident,)* + ) => { + $( + pub enum $list_type_name {} + impl $list_type_name { + // Ensure we covered all numbers. + const LIST: &[&str; last!($($list_idx,)+) + 1] = { + static LIST: [&str; last!($($list_idx,)+) + 1] = [ $( concat!($list_prefix, $list_idx), )* ]; + &LIST + }; + + #[cold] + #[inline(never)] + fn create(idx: usize) -> Symbol { + use std::fmt::Write; + const MAX_LEN: usize = $list_prefix.len() + u64::MAX.ilog10() as usize + 1; + let mut s = ArrayString::<MAX_LEN>::new(); + s.push_str($list_prefix); + _ = write!(s, "{idx}"); + Symbol::intern(&s) + } + + #[inline] + pub fn get(idx: usize) -> Symbol { + match Self::LIST.get(idx) { + Option::Some(s) => Symbol { repr: TaggedArcPtr::non_arc(s) }, + Option::None => Self::create(idx), + } + } + } + )* + // The strings should be in `static`s so that symbol equality holds. $( pub const $name: Symbol = { @@ -32,6 +95,14 @@ macro_rules! define_symbols { let hash_one = |it_: &str| hasher_.hash_one(it_); { $( + for s in $list_type_name::LIST { + let hash_ = hash_one(s); + let shard_idx_ = dashmap_.determine_shard(hash_ as usize); + let symbol = Symbol { repr: TaggedArcPtr::non_arc(s) }; + dashmap_.shards_mut()[shard_idx_].get_mut().insert(hash_, (symbol, SharedValue::new(())), |(x, _)| hash_one(x.as_str())); + } + )* + $( let s = stringify!($name); let hash_ = hash_one(s); let shard_idx_ = dashmap_.determine_shard(hash_ as usize); @@ -49,25 +120,37 @@ macro_rules! define_symbols { }; } define_symbols! { + @LISTS: + Integer = "" + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, + ], + RaGeneratedName = "<ra@gennew>" + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, + ], + @WITH_NAME: dotdotdot = "...", - INTEGER_0 = "0", - INTEGER_1 = "1", - INTEGER_2 = "2", - INTEGER_3 = "3", - INTEGER_4 = "4", - INTEGER_5 = "5", - INTEGER_6 = "6", - INTEGER_7 = "7", - INTEGER_8 = "8", - INTEGER_9 = "9", - INTEGER_10 = "10", - INTEGER_11 = "11", - INTEGER_12 = "12", - INTEGER_13 = "13", - INTEGER_14 = "14", - INTEGER_15 = "15", __empty = "", unsafe_ = "unsafe", in_ = "in", @@ -128,6 +211,9 @@ define_symbols! { as_str, asm, assert, + async_iter, + async_iterator, + AsyncIterator, attr, attributes, begin_panic, @@ -194,6 +280,7 @@ define_symbols! { Default, deprecated, deref_mut, + deref_pure, deref_target, deref, derive_const, @@ -303,7 +390,6 @@ define_symbols! { Iterator, iterator, fused_iterator, - async_iterator, keyword, lang, lang_items, @@ -329,6 +415,7 @@ define_symbols! { module_path, mul_assign, mul, + must_use, naked_asm, ne, neg, @@ -578,4 +665,9 @@ define_symbols! { field, field_base, field_type, + ref_pat_eat_one_layer_2024, + ref_pat_eat_one_layer_2024_structural, + deref_patterns, + mut_ref, + type_changing_struct_update, } diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 839df18159..801eaeaea9 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -657,7 +657,7 @@ impl ProcMacroExpander for Expander { let mut current_ctx = span.ctx; while let Some(macro_call_id) = current_ctx.outer_expn(db) { - let macro_call_loc = db.lookup_intern_macro_call(macro_call_id.into()); + let macro_call_loc = hir_expand::MacroCallId::from(macro_call_id).loc(db); let call_site_file = macro_call_loc.kind.file_id(); @@ -701,7 +701,7 @@ impl ProcMacroExpander for Expander { }; if let Some(macro_call_id) = span.ctx.outer_expn(db) { - let macro_call_loc = db.lookup_intern_macro_call(macro_call_id.into()); + let macro_call_loc = hir_expand::MacroCallId::from(macro_call_id).loc(db); let call_site_file = macro_call_loc.kind.file_id(); let call_site_ast_id = macro_call_loc.kind.erased_ast_id(); diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index de8c3f2e55..9088efeca4 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -5,7 +5,7 @@ use syn::parse_quote; use synstructure::decl_derive; decl_derive!( - [TypeFoldable, attributes(type_foldable)] => + [TypeFoldable, attributes(type_foldable, type_visitable)] => /// Derives `TypeFoldable` for the annotated `struct` or `enum` (`union` is not supported). /// /// The fold will produce a value of the same struct or enum variant as the input, with @@ -102,8 +102,10 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke vi.construct(|_, index| { let bind = &bindings[index]; - // retain value of fields with #[type_foldable(identity)] - if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { + // retain value of fields with #[type_foldable(identity)] or #[type_visitable(ignore)] + if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") + || has_ignore_attr(&bind.ast().attrs, "type_visitable", "ignore") + { bind.to_token_stream() } else { quote! { @@ -118,8 +120,10 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke vi.construct(|_, index| { let bind = &bindings[index]; - // retain value of fields with #[type_foldable(identity)] - if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { + // retain value of fields with #[type_foldable(identity)] or #[type_visitable(ignore)] + if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") + || has_ignore_attr(&bind.ast().attrs, "type_visitable", "ignore") + { bind.to_token_stream() } else { quote! { diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index e8e7928c26..e135291d89 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs @@ -222,7 +222,7 @@ fn expand_subtree( let index = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx); builder.push(tt::Leaf::Literal(tt::Literal { - text_and_suffix: Symbol::integer(index), + text_and_suffix: sym::Integer::get(index), span: ctx.call_site, kind: tt::LitKind::Integer, suffix_len: 0, @@ -234,7 +234,7 @@ fn expand_subtree( 0 }); builder.push(tt::Leaf::Literal(tt::Literal { - text_and_suffix: Symbol::integer(length), + text_and_suffix: sym::Integer::get(length), span: ctx.call_site, kind: tt::LitKind::Integer, suffix_len: 0, @@ -278,7 +278,7 @@ fn expand_subtree( let res = count(binding, 0, depth.unwrap_or(0)); builder.push(tt::Leaf::Literal(tt::Literal { - text_and_suffix: Symbol::integer(res), + text_and_suffix: sym::Integer::get(res), span: ctx.call_site, suffix_len: 0, kind: tt::LitKind::Integer, diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 936cf178f0..76fdac097f 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -374,6 +374,13 @@ impl<T, E> ValueResult<T, E> { ValueResult { value: f(self.value), err: self.err } } + pub fn as_ref(&self) -> ValueResult<&T, E> + where + E: Clone, + { + ValueResult { value: &self.value, err: self.err.clone() } + } + pub fn map_err<E2>(self, f: impl FnOnce(E) -> E2) -> ValueResult<T, E2> { ValueResult { value: self.value, err: self.err.map(f) } } diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 4a1af31656..271dfc877b 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs @@ -237,7 +237,7 @@ fn expr_2021() { PUNCH ; [alone] 0:Root[0000, 0]@39..40#ROOT2024 _; - (const { + (const { 1 });"#]], ); diff --git a/crates/parser/src/event.rs b/crates/parser/src/event.rs index 5be9cb2a24..001adfb780 100644 --- a/crates/parser/src/event.rs +++ b/crates/parser/src/event.rs @@ -2,7 +2,7 @@ //! It is intended to be completely decoupled from the //! parser, so as to allow to evolve the tree representation //! and the parser algorithm independently. -use std::mem; +use std::{mem, num::NonZeroU32}; use crate::{ SyntaxKind::{self, *}, @@ -12,7 +12,13 @@ use crate::{ /// `Parser` produces a flat list of `Event`s. /// They are converted to a tree-structure in /// a separate pass, via `TreeBuilder`. -#[derive(Debug, PartialEq)] +/// +/// Kept to 8 bytes: error messages live in a side table on the `Parser` +/// (the `errors` vec) and `Event::Error` only stores an index into it. +/// `forward_parent` uses `NonZeroU32` so `Option` is niche-optimised away +/// (the offset is always ≥ 1 because the forward parent sits later in the +/// event stream). +#[derive(Debug, PartialEq, Clone, Copy)] pub(crate) enum Event { /// This event signifies the start of the node. /// It should be either abandoned (in which case the @@ -53,10 +59,7 @@ pub(crate) enum Event { /// ``` /// /// See also `CompletedMarker::precede`. - Start { - kind: SyntaxKind, - forward_parent: Option<u32>, - }, + Start { kind: SyntaxKind, forward_parent: Option<NonZeroU32> }, /// Complete the previous `Start` event Finish, @@ -65,20 +68,14 @@ pub(crate) enum Event { /// `n_raw_tokens` is used to glue complex contextual tokens. /// For example, lexer tokenizes `>>` as `>`, `>`, and /// `n_raw_tokens = 2` is used to produced a single `>>`. - Token { - kind: SyntaxKind, - n_raw_tokens: u8, - }, + Token { kind: SyntaxKind, n_raw_tokens: u8 }, /// When we parse `foo.0.0` or `foo. 0. 0` the lexer will hand us a float literal /// instead of an integer literal followed by a dot as the lexer has no contextual knowledge. /// This event instructs whatever consumes the events to split the float literal into /// the corresponding parts. - FloatSplitHack { - ends_in_dot: bool, - }, - Error { - msg: String, - }, + FloatSplitHack { ends_in_dot: bool }, + /// Index into the parser's side `errors` vec. + Error { err: u32 }, } impl Event { @@ -87,13 +84,16 @@ impl Event { } } -/// Generate the syntax tree with the control of events. -pub(super) fn process(mut events: Vec<Event>) -> Output { - let mut res = Output::default(); +/// Generate the syntax tree with the control of events. `errors` is the +/// side table of error messages built up alongside the `events` stream. +pub(super) fn process(mut events: Vec<Event>, mut errors: Vec<String>) -> Output { + // Each event becomes roughly one u32 in Output, so preallocate to avoid + // the amortized grow-one churn we used to see in Output::enter_node. + let mut res = Output::with_event_capacity(events.len()); let mut forward_parents = Vec::new(); for i in 0..events.len() { - match mem::replace(&mut events[i], Event::tombstone()) { + match events[i] { Event::Start { kind, forward_parent } => { // For events[A, B, C], B is A's forward_parent, C is B's forward_parent, // in the normal control flow, the parent-child relation: `A -> B -> C`, @@ -104,7 +104,7 @@ pub(super) fn process(mut events: Vec<Event>) -> Output { let mut idx = i; let mut fp = forward_parent; while let Some(fwd) = fp { - idx += fwd as usize; + idx += fwd.get() as usize; // append `A`'s forward_parent `B` fp = match mem::replace(&mut events[idx], Event::tombstone()) { Event::Start { kind, forward_parent } => { @@ -131,7 +131,13 @@ pub(super) fn process(mut events: Vec<Event>) -> Output { let ev = mem::replace(&mut events[i + 1], Event::tombstone()); assert!(matches!(ev, Event::Finish), "{ev:?}"); } - Event::Error { msg } => res.error(msg), + Event::Error { err } => { + // Move the string out of the side table; each index is visited + // exactly once, so swapping with an empty String is cheap and + // avoids any clone. + let msg = mem::take(&mut errors[err as usize]); + res.error(msg); + } } } diff --git a/crates/parser/src/frontmatter.rs b/crates/parser/src/frontmatter.rs index 2747db4327..1edcad64cf 100644 --- a/crates/parser/src/frontmatter.rs +++ b/crates/parser/src/frontmatter.rs @@ -263,7 +263,7 @@ pub fn strip_ws_lines(input: &str) -> Option<usize> { /// True if `c` is considered a whitespace according to Rust language definition. /// See [Rust language reference](https://doc.rust-lang.org/reference/whitespace.html) /// for definitions of these classes. -fn is_whitespace(c: char) -> bool { +pub(crate) fn is_whitespace(c: char) -> bool { // This is Pattern_White_Space. // // Note that this set is stable (ie, it doesn't change with different diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index 76d26c1ecd..3f341c2ab8 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs @@ -1,7 +1,5 @@ mod atom; -use crate::grammar::attributes::ATTRIBUTE_FIRST; - use super::*; pub(super) use atom::{EXPR_RECOVERY_SET, LITERAL_FIRST, literal, parse_asm_expr}; @@ -324,7 +322,7 @@ fn expr_bp( } const LHS_FIRST: TokenSet = - atom::ATOM_EXPR_FIRST.union(TokenSet::new(&[T![&], T![*], T![!], T![.], T![-], T![_]])); + atom::ATOM_EXPR_FIRST.union(TokenSet::new(&[T![&], T![*], T![!], T![.], T![-], T![_], T![#]])); fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> { let m; @@ -653,7 +651,7 @@ fn arg_list(p: &mut Parser<'_>) { T![')'], T![,], || "expected expression".into(), - EXPR_FIRST.union(ATTRIBUTE_FIRST), + EXPR_FIRST, |p| expr(p).is_some(), ); m.complete(p, ARG_LIST); diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 3214fd90f2..cabdd74b09 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -216,16 +216,19 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { let mut saw_comma = false; let mut saw_expr = false; - // test_err tuple_expr_leading_comma - // fn foo() { - // (,); - // } - if p.eat(T![,]) { - p.error("expected expression"); - saw_comma = true; - } - while !p.at(EOF) && !p.at(T![')']) { + // test_err tuple_expr_empty_expr + // fn foo() { + // (,); + // (a, , b); + // } + if p.current() == T![,] { + p.error("expected expression"); + p.bump(T![,]); + saw_comma = true; + continue; + } + saw_expr = true; // test tuple_attrs @@ -888,6 +891,10 @@ fn return_expr(p: &mut Parser<'_>) -> CompletedMarker { let m = p.start(); p.bump(T![return]); if p.at_ts(EXPR_FIRST) { + // test return_attr + // fn foo() { + // return #[attr] 1; + // } expr(p); } m.complete(p, RETURN_EXPR) diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs index c5c6e04dd4..88a4397232 100644 --- a/crates/parser/src/grammar/items.rs +++ b/crates/parser/src/grammar/items.rs @@ -119,6 +119,25 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res let mut has_mods = false; let mut has_extern = false; + if p.at(T![impl]) + && p.nth(1) == T!['('] + && ((matches!(p.nth(2), T![crate] | T![super] | T![self]) && p.nth(3) == T![')']) + || p.nth(2) == T![in]) + { + // test impl_restrictions + // pub impl(crate) unsafe trait Foo {} + // impl(in super::bar) trait Bar {} + // impl () {} + // impl (i32) {} + let m = p.start(); + p.bump(T![impl]); + if !opt_visibility_inner(p, false) { + p.error("expected an impl restriction"); + } + m.complete(p, IMPL_RESTRICTION); + has_mods = true; + } + // modifiers if p.at(T![const]) && p.nth(1) != T!['{'] { p.eat(T![const]); @@ -167,25 +186,6 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res has_mods = true; } - if p.at(T![impl]) - && p.nth(1) == T!['('] - && ((matches!(p.nth(2), T![crate] | T![super] | T![self]) && p.nth(3) == T![')']) - || p.nth(2) == T![in]) - { - // test impl_restrictions - // pub unsafe impl(crate) trait Foo {} - // impl(in super::bar) trait Bar {} - // impl () {} - // impl (i32) {} - let m = p.start(); - p.bump(T![impl]); - if !opt_visibility_inner(p, false) { - p.error("expected an impl restriction"); - } - m.complete(p, IMPL_RESTRICTION); - has_mods = true; - } - // test default_item // default impl T for Foo {} if p.at_contextual_kw(T![default]) { diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index 4dd44c030f..5726f085a0 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs @@ -199,6 +199,23 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) { } } +fn builtin_pat(p: &mut Parser<'_>) -> Option<CompletedMarker> { + let m = p.start(); + p.bump_remap(T![builtin]); + p.bump(T![#]); + if p.eat_contextual_kw(T![deref]) { + // test deref_pat + // fn foo() { let builtin # deref(_) = 1; } + p.expect(T!['(']); + pattern(p); + p.expect(T![')']); + Some(m.complete(p, DEREF_PAT)) + } else { + m.abandon(p); + None + } +} + const PAT_RECOVERY_SET: TokenSet = TokenSet::new(&[ T![let], T![if], @@ -214,6 +231,10 @@ const PAT_RECOVERY_SET: TokenSet = TokenSet::new(&[ ]); fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option<CompletedMarker> { + if p.at_contextual_kw(T![builtin]) && p.nth_at(1, T![#]) { + return builtin_pat(p); + } + let m = match p.current() { T![box] => box_pat(p), T![ref] | T![mut] => ident_pat(p, true), diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 4478bf4e37..5900d7cfed 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -56,6 +56,13 @@ pub use crate::{ syntax_kind::SyntaxKind, }; +/// True if `c` is whitespace in Rust source (Unicode Pattern_White_Space as in the language reference). +/// +/// See <https://doc.rust-lang.org/reference/whitespace.html>. +pub fn is_rust_whitespace(c: char) -> bool { + frontmatter::is_whitespace(c) +} + /// Parse the whole of the input as a given syntactic construct. /// /// This covers two main use-cases: @@ -104,8 +111,8 @@ impl TopEntryPoint { }; let mut p = parser::Parser::new(input); entry_point(&mut p); - let events = p.finish(); - let res = event::process(events); + let (events, errors) = p.finish(); + let res = event::process(events, errors); if cfg!(debug_assertions) { let mut depth = 0; @@ -169,8 +176,8 @@ impl PrefixEntryPoint { }; let mut p = parser::Parser::new(input); entry_point(&mut p); - let events = p.finish(); - event::process(events) + let (events, errors) = p.finish(); + event::process(events, errors) } } @@ -195,7 +202,7 @@ impl Reparser { let Reparser(r) = self; let mut p = parser::Parser::new(tokens); r(&mut p); - let events = p.finish(); - event::process(events) + let (events, errors) = p.finish(); + event::process(events, errors) } } diff --git a/crates/parser/src/output.rs b/crates/parser/src/output.rs index 0ea15a656c..2f09b11218 100644 --- a/crates/parser/src/output.rs +++ b/crates/parser/src/output.rs @@ -33,6 +33,14 @@ pub enum Step<'a> { } impl Output { + /// Preallocate the event buffer. Each `Event` in the input stream maps to + /// roughly one `u32` in the output, so passing the event count as the + /// initial capacity avoids most of the amortized `Vec::grow` allocations + /// during `event::process`. + pub(crate) fn with_event_capacity(cap: usize) -> Self { + Output { event: Vec::with_capacity(cap), error: Vec::new() } + } + const EVENT_MASK: u32 = 0b1; const TAG_MASK: u32 = 0x0000_00F0; const N_INPUT_TOKEN_MASK: u32 = 0x0000_FF00; diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs index 4557078de9..57c501eda2 100644 --- a/crates/parser/src/parser.rs +++ b/crates/parser/src/parser.rs @@ -1,6 +1,6 @@ //! See [`Parser`]. -use std::cell::Cell; +use std::{cell::Cell, num::NonZeroU32}; use drop_bomb::DropBomb; @@ -12,6 +12,14 @@ use crate::{ input::Input, }; +/// Build a forward-parent offset. The offset is always ≥ 1 because the +/// forward-parent event is created *after* the event it forwards to, so +/// `NonZeroU32` is always valid here. Panics only on a parser bug. +#[inline] +fn fwd_parent(offset: u32) -> NonZeroU32 { + NonZeroU32::new(offset).expect("forward-parent offset must be non-zero") +} + /// `Parser` struct provides the low-level API for /// navigating through the stream of tokens and /// constructing the parse tree. The actual parsing @@ -25,6 +33,9 @@ pub(crate) struct Parser<'t> { inp: &'t Input, pos: usize, events: Vec<Event>, + /// Side table of error messages. `Event::Error { err }` carries an index + /// into this vec, keeping `Event` itself a flat 8-byte enum. + errors: Vec<String>, steps: Cell<u32>, } @@ -32,11 +43,17 @@ const PARSER_STEP_LIMIT: usize = if cfg!(debug_assertions) { 150_000 } else { 15 impl<'t> Parser<'t> { pub(super) fn new(inp: &'t Input) -> Parser<'t> { - Parser { inp, pos: 0, events: Vec::with_capacity(2 * inp.len()), steps: Cell::new(0) } + Parser { + inp, + pos: 0, + events: Vec::with_capacity(2 * inp.len()), + errors: Vec::new(), + steps: Cell::new(0), + } } - pub(crate) fn finish(self) -> Vec<Event> { - self.events + pub(crate) fn finish(self) -> (Vec<Event>, Vec<String>) { + (self.events, self.errors) } /// Returns the kind of the current token. @@ -206,7 +223,7 @@ impl<'t> Parser<'t> { match &mut self.events[idx] { Event::Start { forward_parent, kind } => { *kind = SyntaxKind::FIELD_EXPR; - *forward_parent = Some(new_marker.pos - marker.pos); + *forward_parent = Some(fwd_parent(new_marker.pos - marker.pos)); } _ => unreachable!(), } @@ -237,8 +254,9 @@ impl<'t> Parser<'t> { /// structured errors with spans and notes, like rustc /// does. pub(crate) fn error<T: Into<String>>(&mut self, message: T) { - let msg = message.into(); - self.push_event(Event::Error { msg }); + let err = self.errors.len() as u32; + self.errors.push(message.into()); + self.push_event(Event::Error { err }); } /// Consume the next token if it is `kind` or emit an error @@ -366,7 +384,7 @@ impl CompletedMarker { let idx = self.start_pos as usize; match &mut p.events[idx] { Event::Start { forward_parent, .. } => { - *forward_parent = Some(new_pos.pos - self.start_pos); + *forward_parent = Some(fwd_parent(new_pos.pos - self.start_pos)); } _ => unreachable!(), } @@ -379,7 +397,7 @@ impl CompletedMarker { let idx = m.pos as usize; match &mut p.events[idx] { Event::Start { forward_parent, .. } => { - *forward_parent = Some(self.start_pos - m.pos); + *forward_parent = Some(fwd_parent(self.start_pos - m.pos)); } _ => unreachable!(), } diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index 59fa3ee773..b1867275ce 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs @@ -120,6 +120,7 @@ pub enum SyntaxKind { CFG_KW, CLOBBER_ABI_KW, DEFAULT_KW, + DEREF_KW, DYN_KW, FORMAT_ARGS_KW, GEN_KW, @@ -198,6 +199,7 @@ pub enum SyntaxKind { CONST_BLOCK_PAT, CONST_PARAM, CONTINUE_EXPR, + DEREF_PAT, DYN_TRAIT_TYPE, ENUM, EXPR_STMT, @@ -382,6 +384,7 @@ impl SyntaxKind { | CONST_BLOCK_PAT | CONST_PARAM | CONTINUE_EXPR + | DEREF_PAT | DYN_TRAIT_TYPE | ENUM | EXPR_STMT @@ -627,6 +630,7 @@ impl SyntaxKind { CFG_ATTR_KW => "cfg_attr", CLOBBER_ABI_KW => "clobber_abi", DEFAULT_KW => "default", + DEREF_KW => "deref", DYN_KW => "dyn", FORMAT_ARGS_KW => "format_args", GLOBAL_ASM_KW => "global_asm", @@ -732,6 +736,7 @@ impl SyntaxKind { CFG_ATTR_KW => true, CLOBBER_ABI_KW => true, DEFAULT_KW => true, + DEREF_KW => true, DYN_KW if edition < Edition::Edition2018 => true, FORMAT_ARGS_KW => true, GLOBAL_ASM_KW => true, @@ -825,6 +830,7 @@ impl SyntaxKind { CFG_ATTR_KW => true, CLOBBER_ABI_KW => true, DEFAULT_KW => true, + DEREF_KW => true, DYN_KW if edition < Edition::Edition2018 => true, FORMAT_ARGS_KW => true, GLOBAL_ASM_KW => true, @@ -981,6 +987,7 @@ impl SyntaxKind { "cfg_attr" => CFG_ATTR_KW, "clobber_abi" => CLOBBER_ABI_KW, "default" => DEFAULT_KW, + "deref" => DEREF_KW, "dyn" if edition < Edition::Edition2018 => DYN_KW, "format_args" => FORMAT_ARGS_KW, "global_asm" => GLOBAL_ASM_KW, @@ -1155,6 +1162,7 @@ macro_rules ! T_ { [cfg_attr] => { $ crate :: SyntaxKind :: CFG_ATTR_KW }; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW }; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW }; + [deref] => { $ crate :: SyntaxKind :: DEREF_KW }; [dyn] => { $ crate :: SyntaxKind :: DYN_KW }; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW }; [global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW }; diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index 6dfb78b128..7aaf270a77 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs @@ -195,6 +195,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/default_unsafe_item.rs"); } #[test] + fn deref_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/deref_pat.rs"); } + #[test] fn destructuring_assignment_struct_rest_pattern() { run_and_expect_no_errors( "test_data/parser/inline/ok/destructuring_assignment_struct_rest_pattern.rs", @@ -584,6 +586,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/reference_type.rs"); } #[test] + fn return_attr() { run_and_expect_no_errors("test_data/parser/inline/ok/return_attr.rs"); } + #[test] fn return_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/return_expr.rs"); } #[test] fn return_type_syntax_in_path() { @@ -956,8 +960,8 @@ mod err { #[test] fn top_level_let() { run_and_expect_errors("test_data/parser/inline/err/top_level_let.rs"); } #[test] - fn tuple_expr_leading_comma() { - run_and_expect_errors("test_data/parser/inline/err/tuple_expr_leading_comma.rs"); + fn tuple_expr_empty_expr() { + run_and_expect_errors("test_data/parser/inline/err/tuple_expr_empty_expr.rs"); } #[test] fn tuple_field_list_recovery() { diff --git a/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast b/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast new file mode 100644 index 0000000000..2ccd04c321 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast @@ -0,0 +1,45 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + TUPLE_EXPR + L_PAREN "(" + COMMA "," + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + TUPLE_EXPR + L_PAREN "(" + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + COMMA "," + WHITESPACE " " + COMMA "," + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 16: expected expression +error 27: expected expression diff --git a/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs b/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rs index 12fab59a77..37f756ffa6 100644 --- a/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs +++ b/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rs @@ -1,3 +1,4 @@ fn foo() { (,); + (a, , b); } diff --git a/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast b/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast deleted file mode 100644 index 3fbc0da400..0000000000 --- a/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast +++ /dev/null @@ -1,24 +0,0 @@ -SOURCE_FILE - FN - FN_KW "fn" - WHITESPACE " " - NAME - IDENT "foo" - PARAM_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE "\n " - EXPR_STMT - TUPLE_EXPR - L_PAREN "(" - COMMA "," - R_PAREN ")" - SEMICOLON ";" - WHITESPACE "\n" - R_CURLY "}" - WHITESPACE "\n" -error 17: expected expression diff --git a/crates/parser/test_data/parser/inline/ok/deref_pat.rast b/crates/parser/test_data/parser/inline/ok/deref_pat.rast new file mode 100644 index 0000000000..ab4073c081 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/deref_pat.rast @@ -0,0 +1,36 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LET_STMT + LET_KW "let" + WHITESPACE " " + DEREF_PAT + BUILTIN_KW "builtin" + WHITESPACE " " + POUND "#" + WHITESPACE " " + DEREF_KW "deref" + L_PAREN "(" + WILDCARD_PAT + UNDERSCORE "_" + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "1" + SEMICOLON ";" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/deref_pat.rs b/crates/parser/test_data/parser/inline/ok/deref_pat.rs new file mode 100644 index 0000000000..c5c9d9f059 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/deref_pat.rs @@ -0,0 +1 @@ +fn foo() { let builtin # deref(_) = 1; } diff --git a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast index 5f2680cbaa..bf7b5c5a34 100644 --- a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast +++ b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast @@ -3,8 +3,6 @@ SOURCE_FILE VISIBILITY PUB_KW "pub" WHITESPACE " " - UNSAFE_KW "unsafe" - WHITESPACE " " IMPL_RESTRICTION IMPL_KW "impl" VISIBILITY_INNER @@ -15,6 +13,8 @@ SOURCE_FILE CRATE_KW "crate" R_PAREN ")" WHITESPACE " " + UNSAFE_KW "unsafe" + WHITESPACE " " TRAIT_KW "trait" WHITESPACE " " NAME diff --git a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs index 0a46b158af..429ac93ad5 100644 --- a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs +++ b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs @@ -1,4 +1,4 @@ -pub unsafe impl(crate) trait Foo {} +pub impl(crate) unsafe trait Foo {} impl(in super::bar) trait Bar {} impl () {} impl (i32) {} diff --git a/crates/parser/test_data/parser/inline/ok/return_attr.rast b/crates/parser/test_data/parser/inline/ok/return_attr.rast new file mode 100644 index 0000000000..d5dcdf0447 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/return_attr.rast @@ -0,0 +1,34 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RETURN_EXPR + RETURN_KW "return" + WHITESPACE " " + LITERAL + ATTR + POUND "#" + L_BRACK "[" + PATH_META + PATH + PATH_SEGMENT + NAME_REF + IDENT "attr" + R_BRACK "]" + WHITESPACE " " + INT_NUMBER "1" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/return_attr.rs b/crates/parser/test_data/parser/inline/ok/return_attr.rs new file mode 100644 index 0000000000..dc8bd1a797 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/return_attr.rs @@ -0,0 +1,3 @@ +fn foo() { + return #[attr] 1; +} diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 8377e94c8d..048d3776ca 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -23,7 +23,7 @@ perf-event = "=0.4.8" libc.workspace = true [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.60", features = [ +windows-sys = { version = "0.61", features = [ "Win32_System_Threading", "Win32_System_ProcessStatus", ] } diff --git a/crates/project-model/src/build_dependencies.rs b/crates/project-model/src/build_dependencies.rs index aff5391697..e44af96f36 100644 --- a/crates/project-model/src/build_dependencies.rs +++ b/crates/project-model/src/build_dependencies.rs @@ -382,7 +382,6 @@ impl WorkspaceBuildScripts { } Message::CompilerArtifact(message) => { with_output_for(&message.package_id, &mut |name, data| { - progress(format!("proc-macro {name} built")); if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt { data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro; } @@ -392,6 +391,7 @@ impl WorkspaceBuildScripts { .kind .contains(&cargo_metadata::TargetKind::ProcMacro) { + progress(format!("proc-macro {name} built")); data.proc_macro_dylib_path = match message.filenames.iter().find(|file| is_dylib(file)) { Some(filename) => { diff --git a/crates/project-model/src/cargo_config_file.rs b/crates/project-model/src/cargo_config_file.rs index 9ce88484f7..ae36deb71f 100644 --- a/crates/project-model/src/cargo_config_file.rs +++ b/crates/project-model/src/cargo_config_file.rs @@ -158,15 +158,14 @@ pub(crate) fn make_lockfile_copy( build: semver::BuildMetadata::EMPTY, }; - // TODO: turn this into a const and remove pre once 1.95 is stable - #[allow(non_snake_case)] - let MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV: semver::Version = semver::Version { - major: 1, - minor: 95, - patch: 0, - pre: semver::Prerelease::new("beta").unwrap(), - build: semver::BuildMetadata::EMPTY, - }; + const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV: semver::Version = + semver::Version { + major: 1, + minor: 95, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, + }; let usage = if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV { LockfileUsage::WithEnvVar diff --git a/crates/query-group-macro/src/lib.rs b/crates/query-group-macro/src/lib.rs index 277cc0b269..6e89e0875e 100644 --- a/crates/query-group-macro/src/lib.rs +++ b/crates/query-group-macro/src/lib.rs @@ -6,18 +6,15 @@ use std::vec; use proc_macro::TokenStream; use proc_macro2::Span; use queries::{ - GeneratedInputStruct, InputQuery, InputSetter, InputSetterWithDurability, Intern, Lookup, - Queries, SetterKind, TrackedQuery, Transparent, + GeneratedInputStruct, InputQuery, InputSetter, InputSetterWithDurability, Queries, SetterKind, + TrackedQuery, Transparent, }; use quote::{ToTokens, format_ident, quote}; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::visit_mut::VisitMut; -use syn::{ - Attribute, FnArg, ItemTrait, Path, Token, TraitItem, TraitItemFn, parse_quote, - parse_quote_spanned, -}; +use syn::{Attribute, FnArg, ItemTrait, Path, Token, TraitItem, parse_quote, parse_quote_spanned}; mod queries; @@ -106,7 +103,6 @@ enum QueryKind { Tracked, TrackedWithSalsaStruct, Transparent, - Interned, } #[derive(Default, Debug, Clone)] @@ -190,7 +186,6 @@ pub(crate) fn query_group_impl( let mut trait_methods = vec![]; let mut setter_trait_methods = vec![]; let mut lookup_signatures = vec![]; - let mut lookup_methods = vec![]; for item in &mut item_trait.items { if let syn::TraitItem::Fn(method) = item { @@ -202,7 +197,6 @@ pub(crate) fn query_group_impl( let mut query_kind = QueryKind::TrackedWithSalsaStruct; let mut invoke = None; let mut cycle = None; - let mut interned_struct_path = None; let mut lru = None; let params: Vec<FnArg> = signature.inputs.clone().into_iter().collect(); @@ -230,22 +224,6 @@ pub(crate) fn query_group_impl( } query_kind = QueryKind::Input; } - "interned" => { - let syn::ReturnType::Type(_, ty) = &signature.output else { - return Err(syn::Error::new( - span, - "interned queries must have return type", - )); - }; - let syn::Type::Path(path) = &**ty else { - return Err(syn::Error::new( - span, - "interned queries must have return type", - )); - }; - interned_struct_path = Some(path.path.clone()); - query_kind = QueryKind::Interned; - } "invoke_interned" => { let path = syn::parse::<Parenthesized<Path>>(tts)?; invoke = Some(path.0.clone()); @@ -317,28 +295,6 @@ pub(crate) fn query_group_impl( }; setter_trait_methods.push(SetterKind::WithDurability(setter)); } - (QueryKind::Interned, None) => { - let interned_struct_path = interned_struct_path.unwrap(); - let method = Intern { - signature: signature.clone(), - pat_and_tys: pat_and_tys.clone(), - interned_struct_path: interned_struct_path.clone(), - }; - - trait_methods.push(Queries::Intern(method)); - - let mut method = Lookup { - signature: signature.clone(), - pat_and_tys: pat_and_tys.clone(), - return_ty: *return_ty, - interned_struct_path, - }; - method.prepare_signature(); - - lookup_signatures - .push(TraitItem::Fn(make_trait_method(method.signature.clone()))); - lookup_methods.push(method); - } // tracked function. it might have an invoke, or might not. (QueryKind::Tracked, invoke) => { let method = TrackedQuery { @@ -380,13 +336,6 @@ pub(crate) fn query_group_impl( }; trait_methods.push(Queries::Transparent(method)); } - // error/invalid constructions - (QueryKind::Interned, Some(path)) => { - return Err(syn::Error::new( - path.span(), - "Interned queries cannot be used with an `#[invoke]`".to_string(), - )); - } (QueryKind::Input, Some(path)) => { return Err(syn::Error::new( path.span(), @@ -451,8 +400,6 @@ pub(crate) fn query_group_impl( #(#trait_methods)* #(#setter_methods)* - - #(#lookup_methods)* } }; RemoveAttrsFromTraitMethods.visit_item_trait_mut(&mut item_trait); @@ -485,15 +432,6 @@ where } } -fn make_trait_method(sig: syn::Signature) -> TraitItemFn { - TraitItemFn { - attrs: vec![], - sig: sig.clone(), - semi_token: Some(syn::Token)), - default: None, - } -} - struct RemoveAttrsFromTraitMethods; impl VisitMut for RemoveAttrsFromTraitMethods { diff --git a/crates/query-group-macro/src/queries.rs b/crates/query-group-macro/src/queries.rs index 83ce8902d0..4221068828 100644 --- a/crates/query-group-macro/src/queries.rs +++ b/crates/query-group-macro/src/queries.rs @@ -1,7 +1,7 @@ //! The IR of the `#[query_group]` macro. use quote::{ToTokens, format_ident, quote, quote_spanned}; -use syn::{FnArg, Ident, PatType, Path, Receiver, ReturnType, Type, parse_quote, spanned::Spanned}; +use syn::{FnArg, Ident, PatType, Path, Receiver, ReturnType, parse_quote, spanned::Spanned}; use crate::Cycle; @@ -266,80 +266,11 @@ impl ToTokens for Transparent { method.to_tokens(tokens); } } -pub(crate) struct Intern { - pub(crate) signature: syn::Signature, - pub(crate) pat_and_tys: Vec<PatType>, - pub(crate) interned_struct_path: Path, -} - -impl ToTokens for Intern { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let sig = &self.signature; - - let ty = self.pat_and_tys.to_vec(); - - let interned_pat = ty.first().expect("at least one pat; this is a bug"); - let interned_pat = &interned_pat.pat; - - let wrapper_struct = self.interned_struct_path.to_token_stream(); - - let method = quote! { - #sig { - #wrapper_struct::new(self, #interned_pat) - } - }; - - method.to_tokens(tokens); - } -} - -pub(crate) struct Lookup { - pub(crate) signature: syn::Signature, - pub(crate) pat_and_tys: Vec<PatType>, - pub(crate) return_ty: Type, - pub(crate) interned_struct_path: Path, -} - -impl Lookup { - pub(crate) fn prepare_signature(&mut self) { - let sig = &self.signature; - - let ident = format_ident!("lookup_{}", sig.ident); - - let ty = self.pat_and_tys.to_vec(); - - let interned_key = &self.return_ty; - - let interned_pat = ty.first().expect("at least one pat; this is a bug"); - let interned_return_ty = &interned_pat.ty; - - self.signature = parse_quote!( - fn #ident(&self, id: #interned_key) -> #interned_return_ty - ); - } -} - -impl ToTokens for Lookup { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let sig = &self.signature; - - let wrapper_struct = self.interned_struct_path.to_token_stream(); - let method = quote! { - #sig { - let zalsa = self.zalsa(); - #wrapper_struct::ingredient(zalsa).data(zalsa, id.as_id()).0.clone() - } - }; - - method.to_tokens(tokens); - } -} #[allow(clippy::large_enum_variant)] pub(crate) enum Queries { TrackedQuery(TrackedQuery), InputQuery(InputQuery), - Intern(Intern), Transparent(Transparent), } @@ -349,7 +280,6 @@ impl ToTokens for Queries { Queries::TrackedQuery(tracked_query) => tracked_query.to_tokens(tokens), Queries::InputQuery(input_query) => input_query.to_tokens(tokens), Queries::Transparent(transparent) => transparent.to_tokens(tokens), - Queries::Intern(intern) => intern.to_tokens(tokens), } } } diff --git a/crates/query-group-macro/tests/interned.rs b/crates/query-group-macro/tests/interned.rs deleted file mode 100644 index f738185b1f..0000000000 --- a/crates/query-group-macro/tests/interned.rs +++ /dev/null @@ -1,50 +0,0 @@ -use query_group_macro::query_group; - -use expect_test::expect; -use salsa::plumbing::AsId; - -mod logger_db; -use logger_db::LoggerDb; - -#[salsa_macros::interned(no_lifetime)] -pub struct InternedString { - data: String, -} - -#[query_group] -pub trait InternedDB: salsa::Database { - #[salsa::interned] - fn intern_string(&self, data: String) -> InternedString; - - fn interned_len(&self, id: InternedString) -> usize; -} - -fn interned_len(db: &dyn InternedDB, id: InternedString) -> usize { - db.lookup_intern_string(id).len() -} - -#[test] -fn intern_round_trip() { - let db = LoggerDb::default(); - - let id = db.intern_string(String::from("Hello, world!")); - let s = db.lookup_intern_string(id); - - assert_eq!(s.len(), 13); - db.assert_logs(expect![[r#"[]"#]]); -} - -#[test] -fn intern_with_query() { - let db = LoggerDb::default(); - - let id = db.intern_string(String::from("Hello, world!")); - let len = db.interned_len(id); - - assert_eq!(len, 13); - db.assert_logs(expect![[r#" - [ - "salsa_event(WillCheckCancellation)", - "salsa_event(WillExecute { database_key: interned_len_shim(Id(0)) })", - ]"#]]); -} diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index da2ec74019..27d9576e29 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -78,7 +78,7 @@ paths.workspace = true ra-ap-rustc_type_ir.workspace = true [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.60", features = [ +windows-sys = { version = "0.61", features = [ "Win32_System_Diagnostics_Debug", "Win32_System_Threading", ] } diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index bf9a66bf3f..61e0b1e611 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -2,6 +2,7 @@ //! errors. use std::{ + cell::LazyCell, env, fmt, ops::AddAssign, panic::{AssertUnwindSafe, catch_unwind}, @@ -11,7 +12,7 @@ use std::{ use cfg::{CfgAtom, CfgDiff}; use hir::{ Adt, AssocItem, Crate, DefWithBody, FindPathConfig, GenericDef, HasCrate, HasSource, - HirDisplay, ModuleDef, Name, Variant, VariantId, crate_lang_items, + HirDisplay, ModuleDef, Name, Variant, crate_lang_items, db::{DefDatabase, ExpandDatabase, HirDatabase}, next_solver::{DbInterner, GenericArgs}, }; @@ -588,8 +589,8 @@ impl flags::AnalysisStats { continue; }; - fn trim(s: &str) -> String { - s.chars().filter(|c| !c.is_whitespace()).collect() + fn drop_whitespace(s: &str) -> String { + s.chars().filter(|c| !parser::is_rust_whitespace(*c)).collect() } let todo = syntax::ast::make::ext::expr_todo().to_string(); @@ -609,7 +610,8 @@ impl flags::AnalysisStats { display_target, ) .unwrap(); - syntax_hit_found |= trim(&original_text) == trim(&generated); + syntax_hit_found |= + drop_whitespace(&original_text) == drop_whitespace(&generated); // Validate if type-checks let mut txt = file_txt.text(db).to_string(); @@ -663,7 +665,7 @@ impl flags::AnalysisStats { let msg = move || { format!( "processing: {:<50}", - trim(&original_text).chars().take(50).collect::<String>() + drop_whitespace(&original_text).chars().take(50).collect::<String>() ) }; if verbosity.is_spammy() { @@ -744,10 +746,8 @@ impl flags::AnalysisStats { } all += 1; - let Ok(body_id) = body.try_into() else { - continue; - }; - let Err(e) = db.mir_body(body_id) else { + #[expect(deprecated)] + let Err(e) = body.run_mir_body(db) else { continue; }; if verbosity.is_spammy() { @@ -778,7 +778,7 @@ impl flags::AnalysisStats { vfs: &Vfs, bodies: &[DefWithBody], signatures: &[GenericDef], - variants: &[Variant], + _variants: &[Variant], verbosity: Verbosity, ) { let mut bar = match verbosity { @@ -799,23 +799,24 @@ impl flags::AnalysisStats { InferenceResult::of(snap, body); }) .count(); - let signatures = signatures + let _signatures = signatures .iter() .filter_map(|&signatures| signatures.try_into().ok()) .collect::<Vec<GenericDefId>>(); - signatures - .par_iter() - .map_with(db.clone(), |snap, &signatures| { - InferenceResult::of(snap, signatures); - }) - .count(); - let variants = variants.iter().copied().map(Into::into).collect::<Vec<VariantId>>(); - variants - .par_iter() - .map_with(db.clone(), |snap, &variants| { - InferenceResult::of(snap, variants); - }) - .count(); + // FIXME: We don't have access to the necessary types here (nor we should have). + // signatures + // .par_iter() + // .map_with(db.clone(), |snap, &signatures| { + // InferenceResult::of(snap, signatures); + // }) + // .count(); + // let variants = variants.iter().copied().map(Into::into).collect::<Vec<VariantId>>(); + // variants + // .par_iter() + // .map_with(db.clone(), |snap, &variants| { + // InferenceResult::of(snap, variants); + // }) + // .count(); eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed()); } @@ -907,6 +908,18 @@ impl flags::AnalysisStats { // region:expressions let (previous_exprs, previous_unknown, previous_partially_unknown) = (num_exprs, num_exprs_unknown, num_exprs_partially_unknown); + let type_mismatch_for_node = LazyCell::new(|| { + inference_result + .diagnostics() + .iter() + .filter_map(|diag| match diag { + hir_ty::InferenceDiagnostic::TypeMismatch { node, expected, found } => { + Some((*node, (expected.as_ref(), found.as_ref()))) + } + _ => None, + }) + .collect::<FxHashMap<_, _>>() + }); for (expr_id, _) in body.exprs() { let ty = inference_result.expr_ty(expr_id); num_exprs += 1; @@ -963,9 +976,10 @@ impl flags::AnalysisStats { ty.display(db, display_target) ); } - if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) { + if inference_result.expr_has_type_mismatch(expr_id) { num_expr_type_mismatches += 1; if verbosity.is_verbose() { + let (expected, actual) = type_mismatch_for_node[&expr_id.into()]; if let Some((path, start, end)) = expr_syntax_range(db, vfs, sm(), expr_id) { bar.println(format!( @@ -975,24 +989,25 @@ impl flags::AnalysisStats { start.col, end.line + 1, end.col, - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) )); } else { bar.println(format!( "{}: Expected {}, got {}", name.display(db, Edition::LATEST), - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) )); } } if self.output == Some(OutputFormat::Csv) { + let (expected, actual) = type_mismatch_for_node[&expr_id.into()]; println!( r#"{},mismatch,"{}","{}""#, location_csv_expr(db, vfs, sm(), expr_id), - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) ); } } @@ -1066,9 +1081,10 @@ impl flags::AnalysisStats { ty.display(db, display_target) ); } - if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) { + if inference_result.pat_has_type_mismatch(pat_id) { num_pat_type_mismatches += 1; if verbosity.is_verbose() { + let (expected, actual) = type_mismatch_for_node[&pat_id.into()]; if let Some((path, start, end)) = pat_syntax_range(db, vfs, sm(), pat_id) { bar.println(format!( "{} {}:{}-{}:{}: Expected {}, got {}", @@ -1077,24 +1093,25 @@ impl flags::AnalysisStats { start.col, end.line + 1, end.col, - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) )); } else { bar.println(format!( "{}: Expected {}, got {}", name.display(db, Edition::LATEST), - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) )); } } if self.output == Some(OutputFormat::Csv) { + let (expected, actual) = type_mismatch_for_node[&pat_id.into()]; println!( r#"{},mismatch,"{}","{}""#, location_csv_pat(db, vfs, sm(), pat_id), - mismatch.expected.as_ref().display(db, display_target), - mismatch.actual.as_ref().display(db, display_target) + expected.display(db, display_target), + actual.display(db, display_target) ); } } @@ -1647,5 +1664,5 @@ impl fmt::Display for PrettyItemStats { // fn syntax_len(node: SyntaxNode) -> usize { // // Macro expanded code doesn't contain whitespace, so erase *all* whitespace // // to make macro and non-macro code comparable. -// node.to_string().replace(|it: char| it.is_ascii_whitespace(), "").len() +// drop_whitespace(&node.to_string()).len() // } diff --git a/crates/rust-analyzer/src/cli/highlight.rs b/crates/rust-analyzer/src/cli/highlight.rs index 84607b9fd5..5aba532f57 100644 --- a/crates/rust-analyzer/src/cli/highlight.rs +++ b/crates/rust-analyzer/src/cli/highlight.rs @@ -1,12 +1,15 @@ //! Read Rust code on stdin, print HTML highlighted version to stdout. use ide::Analysis; +use ide_db::base_db::AbsPathBuf; +use triomphe::Arc; use crate::cli::{flags, read_stdin}; impl flags::Highlight { pub fn run(self) -> anyhow::Result<()> { - let (analysis, file_id) = Analysis::from_single_file(read_stdin()?); + let cwd = AbsPathBuf::assert_utf8(std::env::current_dir()?); + let (analysis, file_id) = Analysis::from_single_file(read_stdin()?, Arc::new(cwd)); let html = analysis.highlight_as_html(file_id, self.rainbow).unwrap(); println!("{html}"); Ok(()) diff --git a/crates/rust-analyzer/src/cli/symbols.rs b/crates/rust-analyzer/src/cli/symbols.rs index d7af56d3e1..3d7d712326 100644 --- a/crates/rust-analyzer/src/cli/symbols.rs +++ b/crates/rust-analyzer/src/cli/symbols.rs @@ -1,12 +1,15 @@ //! Read Rust code on stdin, print syntax tree on stdout. use ide::{Analysis, FileStructureConfig}; +use ide_db::base_db::AbsPathBuf; +use triomphe::Arc; use crate::cli::{flags, read_stdin}; impl flags::Symbols { pub fn run(self) -> anyhow::Result<()> { let text = read_stdin()?; - let (analysis, file_id) = Analysis::from_single_file(text); + let cwd = AbsPathBuf::assert_utf8(std::env::current_dir()?); + let (analysis, file_id) = Analysis::from_single_file(text, Arc::new(cwd)); let structure = analysis // The default setting in config.rs (document_symbol_search_excludeLocals) is to exclude // locals because it is unlikely that users want document search to return the names of diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 3a88a8fe84..f4c3f24ce6 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -627,6 +627,11 @@ config_data! { /// Term search fuel in "units of work" for assists (Defaults to 1800). assist_termSearch_fuel: usize = 1800, + /// Automatically add `::` when completing the module. + /// + /// Will not be completed in `use`. + completion_addColonsToModule: bool = true, + /// Automatically add a semicolon when completing unit-returning functions. /// /// In `match` arms it completes a comma instead. @@ -1901,6 +1906,7 @@ impl Config { CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses), CallableCompletionDef::None => None, }, + add_colons_to_module: *self.completion_addColonsToModule(source_root), add_semicolon_to_unit: *self.completion_addSemicolonToUnit(source_root), snippet_cap: SnippetCap::new(self.completion_snippet()), insert_use: self.insert_use_config(source_root), diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 0c1c067ffa..ca6a2e70b0 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1049,7 +1049,6 @@ pub(crate) fn handle_runnables( all_targets = if all_targets { " --all-targets" } else { "" } ), location: None, - kind: lsp_ext::RunnableKind::Cargo, args: lsp_ext::RunnableArgs::Cargo(lsp_ext::CargoRunnableArgs { workspace_root: Some(spec.workspace_root.clone().into()), cwd: cwd.into(), @@ -1076,7 +1075,6 @@ pub(crate) fn handle_runnables( res.push(lsp_ext::Runnable { label: "cargo check --workspace".to_owned(), location: None, - kind: lsp_ext::RunnableKind::Cargo, args: lsp_ext::RunnableArgs::Cargo(lsp_ext::CargoRunnableArgs { workspace_root: None, cwd: path.as_path().unwrap().to_path_buf().into(), diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index af449c473a..bd3574ae0a 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -162,36 +162,7 @@ fn integrated_completion_benchmark() { { let _span = profile::cpu_span(); let analysis = host.analysis(); - let config = CompletionConfig { - enable_postfix_completions: true, - enable_imports_on_the_fly: true, - enable_self_on_the_fly: true, - enable_private_editable: true, - enable_term_search: true, - term_search_fuel: 200, - full_function_signatures: false, - callable: Some(CallableSnippets::FillArguments), - snippet_cap: SnippetCap::new(true), - insert_use: InsertUseConfig { - granularity: ImportGranularity::Crate, - prefix_kind: hir::PrefixKind::ByCrate, - enforce_granularity: true, - group: true, - skip_glob_imports: true, - }, - prefer_no_std: false, - prefer_prelude: true, - prefer_absolute: false, - snippets: Vec::new(), - limit: None, - add_semicolon_to_unit: true, - fields_to_resolve: CompletionFieldsToResolve::empty(), - exclude_flyimport: vec![], - exclude_traits: &[], - enable_auto_await: true, - enable_auto_iter: true, - ra_fixture: RaFixtureConfig::default(), - }; + let config = completion_config(); let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; analysis.completions(&config, position, None).unwrap(); @@ -217,36 +188,7 @@ fn integrated_completion_benchmark() { let _p = tracing::info_span!("unqualified path completion").entered(); let _span = profile::cpu_span(); let analysis = host.analysis(); - let config = CompletionConfig { - enable_postfix_completions: true, - enable_imports_on_the_fly: true, - enable_self_on_the_fly: true, - enable_private_editable: true, - enable_term_search: true, - term_search_fuel: 200, - full_function_signatures: false, - callable: Some(CallableSnippets::FillArguments), - snippet_cap: SnippetCap::new(true), - insert_use: InsertUseConfig { - granularity: ImportGranularity::Crate, - prefix_kind: hir::PrefixKind::ByCrate, - enforce_granularity: true, - group: true, - skip_glob_imports: true, - }, - prefer_no_std: false, - prefer_prelude: true, - prefer_absolute: false, - snippets: Vec::new(), - limit: None, - add_semicolon_to_unit: true, - fields_to_resolve: CompletionFieldsToResolve::empty(), - exclude_flyimport: vec![], - exclude_traits: &[], - enable_auto_await: true, - enable_auto_iter: true, - ra_fixture: RaFixtureConfig::default(), - }; + let config = completion_config(); let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; analysis.completions(&config, position, None).unwrap(); @@ -270,36 +212,7 @@ fn integrated_completion_benchmark() { let _p = tracing::info_span!("dot completion").entered(); let _span = profile::cpu_span(); let analysis = host.analysis(); - let config = CompletionConfig { - enable_postfix_completions: true, - enable_imports_on_the_fly: true, - enable_self_on_the_fly: true, - enable_private_editable: true, - enable_term_search: true, - term_search_fuel: 200, - full_function_signatures: false, - callable: Some(CallableSnippets::FillArguments), - snippet_cap: SnippetCap::new(true), - insert_use: InsertUseConfig { - granularity: ImportGranularity::Crate, - prefix_kind: hir::PrefixKind::ByCrate, - enforce_granularity: true, - group: true, - skip_glob_imports: true, - }, - prefer_no_std: false, - prefer_prelude: true, - prefer_absolute: false, - snippets: Vec::new(), - limit: None, - add_semicolon_to_unit: true, - fields_to_resolve: CompletionFieldsToResolve::empty(), - exclude_flyimport: vec![], - exclude_traits: &[], - enable_auto_await: true, - enable_auto_iter: true, - ra_fixture: RaFixtureConfig::default(), - }; + let config = completion_config(); let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; analysis.completions(&config, position, None).unwrap(); @@ -400,3 +313,37 @@ fn patch(what: &mut String, from: &str, to: &str) -> usize { *what = what.replacen(from, to, 1); idx } + +fn completion_config() -> CompletionConfig<'static> { + CompletionConfig { + enable_postfix_completions: true, + enable_imports_on_the_fly: true, + enable_self_on_the_fly: true, + enable_private_editable: true, + enable_term_search: true, + term_search_fuel: 200, + full_function_signatures: false, + callable: Some(CallableSnippets::FillArguments), + snippet_cap: SnippetCap::new(true), + insert_use: InsertUseConfig { + granularity: ImportGranularity::Crate, + prefix_kind: hir::PrefixKind::ByCrate, + enforce_granularity: true, + group: true, + skip_glob_imports: true, + }, + prefer_no_std: false, + prefer_prelude: true, + prefer_absolute: false, + snippets: Vec::new(), + limit: None, + add_colons_to_module: true, + add_semicolon_to_unit: true, + fields_to_resolve: CompletionFieldsToResolve::empty(), + exclude_flyimport: vec![], + exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, + ra_fixture: RaFixtureConfig::default(), + } +} diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs index 5d0d9209de..754d6e65fe 100644 --- a/crates/rust-analyzer/src/lsp/ext.rs +++ b/crates/rust-analyzer/src/lsp/ext.rs @@ -451,25 +451,17 @@ pub struct Runnable { pub label: String, #[serde(skip_serializing_if = "Option::is_none")] pub location: Option<lsp_types::LocationLink>, - pub kind: RunnableKind, + #[serde(flatten)] pub args: RunnableArgs, } #[derive(Deserialize, Serialize, Debug, Clone)] -#[serde(rename_all = "camelCase")] -#[serde(untagged)] +#[serde(tag = "kind", content = "args", rename_all = "lowercase")] pub enum RunnableArgs { Cargo(CargoRunnableArgs), Shell(ShellRunnableArgs), } -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(rename_all = "lowercase")] -pub enum RunnableKind { - Cargo, - Shell, -} - #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct CargoRunnableArgs { @@ -858,6 +850,7 @@ pub struct InlayHintResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct CompletionImport { pub full_import_path: String, + #[serde(default)] pub as_underscore: bool, } @@ -880,3 +873,94 @@ impl Request for GetFailedObligations { type Result = String; const METHOD: &'static str = "rust-analyzer/getFailedObligations"; } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn cargo_runnable_round_trips() { + let runnable = Runnable { + label: "cargo test -p my-crate".to_owned(), + location: None, + args: RunnableArgs::Cargo(CargoRunnableArgs { + environment: [("RUSTC_TOOLCHAIN".to_owned(), "/toolchain".to_owned())] + .into_iter() + .collect(), + cwd: "/project".into(), + override_cargo: None, + workspace_root: Some("/project".into()), + cargo_args: vec![ + "test".into(), + "--package".into(), + "my-crate".into(), + "--lib".into(), + ], + executable_args: vec!["my_test".into(), "--exact".into()], + }), + }; + let expected = json!({ + "label": "cargo test -p my-crate", + "kind": "cargo", + "args": { + "environment": {"RUSTC_TOOLCHAIN": "/toolchain"}, + "cwd": "/project", + "overrideCargo": null, + "workspaceRoot": "/project", + "cargoArgs": ["test", "--package", "my-crate", "--lib"], + "executableArgs": ["my_test", "--exact"], + } + }); + + let serialized = serde_json::to_value(&runnable).expect("serialized runnable"); + assert_eq!(serialized, expected); + + let deserialized: Runnable = + serde_json::from_value(expected).expect("cargo runnable should deserialize"); + let RunnableArgs::Cargo(cargo) = &deserialized.args else { + panic!("expected Cargo variant, got {:?}", deserialized.args); + }; + assert_eq!(cargo.cargo_args, vec!["test", "--package", "my-crate", "--lib"]); + assert_eq!(cargo.executable_args, vec!["my_test", "--exact"]); + } + + #[test] + fn shell_runnable_round_trips() { + let runnable = Runnable { + label: "nextest test_one".to_owned(), + location: None, + args: RunnableArgs::Shell(ShellRunnableArgs { + environment: [("RUSTC_TOOLCHAIN".to_owned(), "/toolchain".to_owned())] + .into_iter() + .collect(), + cwd: "/project".into(), + program: "cargo".into(), + args: vec!["nextest".into(), "run".into(), "--package".into(), "my-crate".into()], + }), + }; + let expected = json!({ + "label": "nextest test_one", + "kind": "shell", + "args": { + "environment": {"RUSTC_TOOLCHAIN": "/toolchain"}, + "cwd": "/project", + "program": "cargo", + "args": ["nextest", "run", "--package", "my-crate"], + } + }); + + let serialized = serde_json::to_value(&runnable).expect("serialized runnable"); + assert_eq!(serialized, expected); + + // Every shell runnable is a structurally valid cargo runnable if the `kind` tag isn't + // used. This test ensures that the `kind` tag is used. + let deserialized: Runnable = + serde_json::from_value(expected).expect("shell runnable should deserialize"); + let RunnableArgs::Shell(shell) = &deserialized.args else { + panic!("expected Shell variant, got {:?}", deserialized.args); + }; + assert_eq!(shell.program, "cargo"); + assert_eq!(shell.args, vec!["nextest", "run", "--package", "my-crate"]); + } +} diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index d857f23b67..eff5477969 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -1608,7 +1608,6 @@ pub(crate) fn runnable( Some((program, args)) => Some(lsp_ext::Runnable { label, location: Some(location), - kind: lsp_ext::RunnableKind::Shell, args: lsp_ext::RunnableArgs::Shell(lsp_ext::ShellRunnableArgs { environment, cwd: cwd.into(), @@ -1621,7 +1620,6 @@ pub(crate) fn runnable( None => Some(lsp_ext::Runnable { label, location: Some(location), - kind: lsp_ext::RunnableKind::Cargo, args: lsp_ext::RunnableArgs::Cargo(lsp_ext::CargoRunnableArgs { workspace_root: Some(workspace_root.into()), override_cargo: config.override_cargo, @@ -1648,7 +1646,6 @@ pub(crate) fn runnable( Ok(Some(lsp_ext::Runnable { label, location: Some(location), - kind: lsp_ext::RunnableKind::Shell, args: lsp_ext::RunnableArgs::Shell(runnable_args), })) } @@ -1668,7 +1665,6 @@ pub(crate) fn runnable( Ok(Some(lsp_ext::Runnable { label, location: Some(location), - kind: lsp_ext::RunnableKind::Cargo, args: lsp_ext::RunnableArgs::Cargo(lsp_ext::CargoRunnableArgs { workspace_root: None, override_cargo: config.override_cargo, @@ -2028,6 +2024,7 @@ pub(crate) fn rename_error(err: RenameError) -> LspError { mod tests { use expect_test::{Expect, expect}; use ide::{Analysis, FilePosition}; + use ide_db::base_db::AbsPathBuf; use ide_db::source_change::Snippet; use test_utils::extract_offset; use triomphe::Arc; @@ -2048,7 +2045,10 @@ fn main() { } }"#; - let (analysis, file_id) = Analysis::from_single_file(text.to_owned()); + let (analysis, file_id) = Analysis::from_single_file( + text.to_owned(), + Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())), + ); let folds = analysis.folding_ranges(file_id, true).unwrap(); assert_eq!(folds.len(), 5); @@ -2084,7 +2084,10 @@ fn bar(_: usize) {} "#; let (offset, text) = extract_offset(text); - let (analysis, file_id) = Analysis::from_single_file(text); + let (analysis, file_id) = Analysis::from_single_file( + text, + Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())), + ); let help = signature_help( analysis.signature_help(FilePosition { file_id, offset }).unwrap().unwrap(), CallInfoConfig { params_only: false, docs: true }, diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index f5b3658ea9..a9ce6f728b 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -660,8 +660,20 @@ impl GlobalState { let subscriptions = subscriptions.clone(); // Do not fetch semantic diagnostics (and populate query results) if we haven't even // loaded the initial workspace yet. - let fetch_semantic = - self.vfs_done && self.fetch_workspaces_queue.last_op_result().is_some(); + // + // Only fetch semantic diagnostics when + // - we have fully populated the VFS + // - have a workspace + // - have finished fetching the build data once + // - and have finished loading the proc-macros once + let fetch_semantic = self.vfs_done + && self.fetch_workspaces_queue.last_op_result().is_some() + && (!self.config.run_build_scripts(None) + || (self.fetch_build_data_queue.last_op_result().is_none() + && !self.fetch_build_data_queue.op_in_progress())) + && (!self.config.expand_proc_macros() + || (self.fetch_proc_macros_queue.last_op_result().is_none() + && !self.fetch_proc_macros_queue.op_in_progress())); move |sender| { // We aren't observing the semantics token cache here let snapshot = AssertUnwindSafe(&snapshot); diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 1832275eb3..74fd0e6533 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -440,7 +440,7 @@ impl GlobalState { let expansion_res = match client { Some(Ok(client)) => match res { Ok((crate_name, path)) => { - progress(format!("loading proc-macros: {path}")); + progress(path.to_string()); let ignored_proc_macros = ignored_proc_macros .iter() .find_map(|(name, macros)| { diff --git a/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/crates/rust-analyzer/tests/slow-tests/flycheck.rs index e5d4d7c88e..386edb82f4 100644 --- a/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -76,6 +76,7 @@ fn main() { } #[test] +#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostic_with_override_command() { if skip_slow_tests() { return; diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs index 0a81cef52e..f475de93e0 100644 --- a/crates/span/src/hygiene.rs +++ b/crates/span/src/hygiene.rs @@ -156,6 +156,8 @@ const _: () = { impl zalsa_::SalsaStructInDb for SyntaxContext { type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex; + const LEAF_TYPE_IDS: &[salsa::plumbing::ConstTypeId] = + &[salsa::plumbing::ConstTypeId::of::<SyntaxContext>()]; fn lookup_ingredient_index(aux: &zalsa_::Zalsa) -> salsa::plumbing::IngredientIndices { aux.lookup_jar_by_type::<zalsa_struct_::JarImpl<SyntaxContext>>().into() diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index 2c19f00f08..a9d19a1347 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -26,7 +26,7 @@ libc.workspace = true [target.'cfg(windows)'.dependencies] miow = "0.6.0" -windows-sys = { version = "0.60", features = ["Win32_Foundation"] } +windows-sys = { version = "0.61", features = ["Win32_Foundation"] } [features] # Uncomment to enable for the whole crate graph diff --git a/crates/syntax-bridge/Cargo.toml b/crates/syntax-bridge/Cargo.toml index b0fd40ff59..c928f23e02 100644 --- a/crates/syntax-bridge/Cargo.toml +++ b/crates/syntax-bridge/Cargo.toml @@ -25,6 +25,7 @@ span = { path = "../span", version = "0.0.0", default-features = false} intern.workspace = true [dev-dependencies] +expect-test = "1.5.1" test-utils.workspace = true [features] diff --git a/crates/syntax-bridge/src/prettify_macro_expansion.rs b/crates/syntax-bridge/src/prettify_macro_expansion.rs index 2f932e0458..648119ed70 100644 --- a/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -3,8 +3,7 @@ use syntax::{ NodeOrToken, SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, T, WalkEvent, - ast::make, - ted::{self, Position}, + syntax_editor::{Position, SyntaxEditor}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -29,7 +28,7 @@ pub fn prettify_macro_expansion( let mut last: Option<SyntaxKind> = None; let mut mods = Vec::new(); let mut dollar_crate_replacements = Vec::new(); - let syn = syn.clone_subtree().clone_for_update(); + let (editor, syn) = SyntaxEditor::new(syn); let before = Position::before; let after = Position::after; @@ -45,17 +44,21 @@ pub fn prettify_macro_expansion( for event in syn.preorder_with_tokens() { let token = match event { WalkEvent::Enter(NodeOrToken::Token(token)) => token, - WalkEvent::Leave(NodeOrToken::Node(node)) - if matches!( - node.kind(), - ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL | MACRO_RULES - ) => - { - if indent > 0 { + WalkEvent::Leave(NodeOrToken::Node(node)) => { + let is_last_child = + node.parent().is_some_and(|parent| parent.last_child().as_ref() == Some(&node)); + let is_always_newline = matches!(node.kind(), ATTR); + let is_non_last_newline = match node.kind() { + MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL | MACRO_RULES | EXTERN_BLOCK + | EXTERN_CRATE | MODULE => true, + EXPR_STMT if Some(R_CURLY) == node.last_token().map(|it| it.kind()) => true, + _ => false, + }; + if (!is_last_child && is_non_last_newline) || is_always_newline { mods.push((Position::after(node.clone()), PrettifyWsKind::Indent(indent))); - } - if node.parent().is_some() { - mods.push((Position::after(node), PrettifyWsKind::Newline)); + if node.parent().is_some() { + mods.push((Position::after(node), PrettifyWsKind::Newline)); + } } continue; } @@ -77,55 +80,69 @@ pub fn prettify_macro_expansion( match tok.kind() { k if is_text(k) - && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#]), false) => + && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#] | L_CURLY), false) => { mods.push(do_ws(after, tok)); } L_CURLY if is_next(|it| it != R_CURLY, true) => { indent += 1; - if is_last(is_text, false) { - mods.push(do_ws(before, tok)); - } - mods.push(do_indent(after, tok, indent)); mods.push(do_nl(after, tok)); } R_CURLY if is_last(|it| it != L_CURLY, true) => { indent = indent.saturating_sub(1); - if indent > 0 { - mods.push(do_indent(before, tok, indent)); - } + mods.push(do_indent(before, tok, indent)); mods.push(do_nl(before, tok)); } - R_CURLY => { - if indent > 0 { - mods.push(do_indent(after, tok, indent)); - } - mods.push(do_nl(after, tok)); + R_CURLY if is_next(|it| it == T![else], false) => { + mods.push(do_indent(before, tok, indent)); + mods.push(do_nl(before, tok)); } LIFETIME_IDENT if is_next(is_text, true) => { mods.push(do_ws(after, tok)); } - AS_KW | DYN_KW | IMPL_KW | CONST_KW | MUT_KW => { + AS_KW | DYN_KW | IMPL_KW | CONST_KW | MUT_KW | LET_KW | MATCH_KW => { mods.push(do_ws(after, tok)); } T![;] if is_next(|it| it != R_CURLY, true) => { - if indent > 0 { - mods.push(do_indent(after, tok, indent)); + mods.push(do_indent(after, tok, indent)); + if tok.text_range().end() != syn.text_range().end() { + mods.push(do_nl(after, tok)); } - mods.push(do_nl(after, tok)); } - T![=] if is_next(|it| it == T![>], false) => { + T![=] if let Some((last, next)) = last.zip(tok.next_token()) => { // FIXME: this branch is for `=>` in macro_rules!, which is currently parsed as // two separate symbols. - mods.push(do_ws(before, tok)); - mods.push(do_ws(after, &tok.next_token().unwrap())); + match (last, next.kind()) { + (T![=], _) | (_, T![=]) => (), + // catch ..= += etc + #[rustfmt::skip] + ( + T![!] | T![%] | T![&] | T![*] | T![+] | T![-] | + T![/] | T![<] | T![>] | T![^] | T![|] | T![.], + _, + ) => (), + (_, T![>]) => { + mods.push(do_ws(before, tok)); + mods.push(do_ws(after, &next)); + } + _ => { + mods.push(do_ws(before, tok)); + mods.push(do_ws(after, tok)); + } + } } - T![->] | T![=] | T![=>] => { + T![->] | T![=>] => { mods.push(do_ws(before, tok)); mods.push(do_ws(after, tok)); } + T![:] if is_next(|it| it != T![:], false) && is_last(|it| it != T![:], false) => { + // XXX: Why input included WHITESPACE? + if is_next(|it| it != SyntaxKind::WHITESPACE, false) { + mods.push(do_ws(after, tok)); + } + } T![!] if is_last(|it| it == MACRO_RULES_KW, false) && is_next(is_text, false) => { mods.push(do_ws(after, tok)); } @@ -137,27 +154,319 @@ pub fn prettify_macro_expansion( inspect_mods(&mods); for (pos, insert) in mods { - ted::insert_raw( + editor.insert( pos, match insert { - PrettifyWsKind::Space => make::tokens::single_space(), - PrettifyWsKind::Indent(indent) => make::tokens::whitespace(&" ".repeat(4 * indent)), - PrettifyWsKind::Newline => make::tokens::single_newline(), + PrettifyWsKind::Space => editor.make().whitespace(" "), + PrettifyWsKind::Indent(0) => continue, + PrettifyWsKind::Indent(indent) => editor.make().whitespace(&" ".repeat(4 * indent)), + PrettifyWsKind::Newline => editor.make().whitespace("\n"), }, ); } for (old, new) in dollar_crate_replacements { - ted::replace(old, new); + editor.replace(old, new); } if let Some(it) = syn.last_token().filter(|it| it.kind() == SyntaxKind::WHITESPACE) { - ted::remove(it); + editor.delete(it); } - syn + editor.finish().new_root().clone() } fn is_text(k: SyntaxKind) -> bool { // Consider all keywords in all editions. k.is_any_identifier() || k.is_literal() || k == UNDERSCORE } + +#[cfg(test)] +mod tests { + use super::*; + use expect_test::{Expect, expect}; + + #[expect(deprecated)] + fn check_pretty(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let ra_fixture = stdx::trim_indent(ra_fixture); + let source_file = syntax::ast::SourceFile::parse(&ra_fixture, span::Edition::CURRENT); + let syn = remove_whitespaces(&source_file.syntax_node()); + + let pretty = prettify_macro_expansion(syn, &mut |_| None, |_| ()); + let mut pretty = pretty.to_string(); + if pretty.contains('\n') { + pretty.push('\n'); + } + expect.assert_eq(&pretty); + + fn remove_whitespaces(node: &SyntaxNode) -> SyntaxNode { + let (editor, node) = SyntaxEditor::new(node.clone()); + node.preorder_with_tokens().for_each(|it| match it { + WalkEvent::Enter(NodeOrToken::Token(tok)) if tok.kind().is_trivia() => { + editor.delete(tok); + } + _ => (), + }); + editor.finish().new_root().clone() + } + } + + #[test] + fn test_in_macro() { + check_pretty( + r#" + const X: i32 = x::y::z; + macro_rules! foo { + () => { + $crate::foo::bar!(); + (1..2, 1..=2); + (a==b, a!=b, a<=b, a>=b, x+=2, x<<=2); + }; + } + "#, + expect![[r#" + const X: i32 = x::y::z; + macro_rules! foo { + () => { + $crate::foo::bar!(); + (1..2,1..=2); + (a==b,a!=b,a<=b,a>=b,x+=2,x<<=2); + }; + } + "#]], + ); + } + + #[test] + fn test_curly_indent() { + check_pretty( + r#" + const _: () = { + { + 2; + 3 + } + }; + "#, + expect![[r#" + const _: () = { + { + 2; + 3 + } + }; + "#]], + ); + } + + #[test] + fn test_pats() { + check_pretty( + r#" + const _: () = { + let x = 2; + let mut y = 3; + let ref mut z @ 0..5 = 4; + let ref mut t @ 0..=5 = 4; + let (x, ref y) = (5, 6); + let (Foo { x, y }, Bar(z, t)); + let (&mut x, (y | y)); + match () {} + }; + "#, + expect![[r#" + const _: () = { + let x = 2; + let mut y = 3; + let ref mut [email protected] = 4; + let ref mut t@0..=5 = 4; + let (x,ref y) = (5,6); + let (Foo { + x,y + },Bar(z,t)); + let (&mut x,(y|y)); + match (){} + }; + "#]], + ); + } + + #[test] + fn test_attrs() { + check_pretty( + r#" + #[attr1] + #[attr2] + const _: () = {}; + #[attr1] + const _: () = { + #[attr2] + {} + }; + "#, + expect![[r#" + #[attr1] + #[attr2] + const _: () = {}; + #[attr1] + const _: () = { + #[attr2] + {} + }; + "#]], + ); + } + + #[test] + fn test_items() { + check_pretty( + r#" + fn foo() {} + struct Foo {} + struct Foo; + enum Foo {} + impl Foo {} + const _: () = {}; + static S: () = {}; + extern {} + mod x {} + mod x; + type X = 2; + use a; + use b::{c, d}; + macro_rules! foo { () => {}; } + "#, + expect![[r#" + fn foo(){} + struct Foo {} + struct Foo; + + enum Foo {} + impl Foo {} + const _: () = {}; + static S: () = {}; + extern {} + mod x {} + mod x; + + type X = 2; + use a; + use b::{ + c,d + }; + macro_rules! foo { + () => {}; + } + "#]], + ); + } + + #[test] + fn test_exprs() { + check_pretty( + r#" + const _: () = { + let _ = 1+2; + let _ = !true && false; + let _ = foo() + !bar() + dbg!(2) + *x; + let _ = async move || {}; + let _ = async move {}; + let _ = x.await; + let _ = (1..2, 1..=2); + 'lab: for _ in 0..5 { + loop { } + break 'lab expr; + if let pat = expr { + foo() + } else if true { + bar() + } else {} + if true {} else if true {} else {} + fun() + } + }; + "#, + expect![[r#" + const _: () = { + let _ = 1+2; + let _ = !true&&false; + let _ = foo()+!bar()+dbg!(2)+*x; + let _ = async move||{}; + let _ = async move {}; + let _ = x.await; + let _ = (1..2,1..=2); + 'lab: for _ in 0..5 { + loop {} + break 'lab expr; + if let pat = expr { + foo() + }else if true { + bar() + }else {} + if true { + }else if true { + }else {} + fun() + } + }; + "#]], + ); + } + + #[test] + fn test_match_arm() { + check_pretty( + r#" + const _: () = { + match 2 { + tmp => foo!(), + }; + }; + "#, + expect![[r#" + const _: () = { + match 2 { + tmp => foo!(), + }; + }; + "#]], + ); + + check_pretty( + r#" + const _: () = { + match 2 { + tmp => {} + }; + }; + "#, + expect![[r#" + const _: () = { + match 2 { + tmp => {} + }; + }; + "#]], + ); + + check_pretty( + r#" + const _: () = { + match 2 { + 1 => {} + 2 => foo(), + _ => {}, + }; + }; + "#, + expect![[r#" + const _: () = { + match 2 { + 1 => {} + 2 => foo(), + _ => {}, + }; + }; + "#]], + ); + } +} diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 768cf2013d..6bcf8ba743 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -748,6 +748,10 @@ Pat = | TuplePat | TupleStructPat | ConstBlockPat +| DerefPat + +DerefPat = + 'builtin' '#' 'deref' '(' Pat ')' LiteralPat = '-'? Literal diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index b20aa90d06..2e3a4016ee 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -2,15 +2,18 @@ //! immutable, all function here return a fresh copy of the tree, instead of //! doing an in-place modification. use parser::T; -use std::{fmt, iter, ops}; +use std::{ + fmt, + iter::{self, once}, + ops, +}; use crate::{ AstToken, NodeOrToken, SyntaxElement, - SyntaxKind::WHITESPACE, + SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, ast::{self, AstNode, HasName, make}, syntax_editor::{Position, SyntaxEditor, SyntaxMappingBuilder}, - ted, }; use super::syntax_factory::SyntaxFactory; @@ -84,29 +87,6 @@ impl IndentLevel { IndentLevel(0) } - /// XXX: this intentionally doesn't change the indent of the very first token. - /// For example, in something like: - /// ``` - /// fn foo() -> i32 { - /// 92 - /// } - /// ``` - /// if you indent the block, the `{` token would stay put. - pub(super) fn increase_indent(self, node: &SyntaxNode) { - let tokens = node.preorder_with_tokens().filter_map(|event| match event { - rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), - _ => None, - }); - for token in tokens { - if let Some(ws) = ast::Whitespace::cast(token) - && ws.text().contains('\n') - { - let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax())); - ted::replace(ws.syntax(), &new_ws); - } - } - } - pub(super) fn clone_increase_indent(self, node: &SyntaxNode) -> SyntaxNode { let (editor, node) = SyntaxEditor::new(node.clone()); let tokens = node @@ -124,23 +104,6 @@ impl IndentLevel { editor.finish().new_root().clone() } - pub(super) fn decrease_indent(self, node: &SyntaxNode) { - let tokens = node.preorder_with_tokens().filter_map(|event| match event { - rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), - _ => None, - }); - for token in tokens { - if let Some(ws) = ast::Whitespace::cast(token) - && ws.text().contains('\n') - { - let new_ws = make::tokens::whitespace( - &ws.syntax().text().replace(&format!("\n{self}"), "\n"), - ); - ted::replace(ws.syntax(), &new_ws); - } - } - } - pub(super) fn clone_decrease_indent(self, node: &SyntaxNode) -> SyntaxNode { let (editor, node) = SyntaxEditor::new(node.clone()); let tokens = node @@ -197,6 +160,28 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { impl<N: AstNode + Clone> AstNodeEdit for N {} +pub trait AttrsOwnerEdit: ast::HasAttrs { + fn remove_attrs_and_docs(&self, editor: &SyntaxEditor) { + let mut remove_next_ws = false; + for child in self.syntax().children_with_tokens() { + match child.kind() { + ATTR | COMMENT => { + remove_next_ws = true; + editor.delete(child); + continue; + } + WHITESPACE if remove_next_ws => { + editor.delete(child); + } + _ => (), + } + remove_next_ws = false; + } + } +} + +impl<T: ast::HasAttrs> AttrsOwnerEdit for T {} + impl ast::IdentPat { pub fn set_pat(&self, pat: Option<ast::Pat>, editor: &SyntaxEditor) -> ast::IdentPat { let make = editor.make(); @@ -252,6 +237,32 @@ impl ast::IdentPat { } } +impl ast::UseTree { + pub fn wrap_in_tree_list_with_editor(&self) -> Option<ast::UseTree> { + if self.use_tree_list().is_some() + && self.path().is_none() + && self.star_token().is_none() + && self.rename().is_none() + { + return None; + } + + let (editor, use_tree) = SyntaxEditor::with_ast_node(self); + let make = editor.make(); + let first_child = use_tree.syntax().first_child_or_token()?; + let last_child = use_tree.syntax().last_child_or_token()?; + let use_tree_list = make.use_tree_list(once(self.clone())); + editor.replace_all(first_child..=last_child, vec![use_tree_list.syntax().clone().into()]); + + let edit = editor.finish(); + ast::UseTree::cast(edit.new_root().clone()) + } +} + +pub fn indent(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode { + level.clone_increase_indent(node) +} + #[test] fn test_increase_indent() { let arm_list = { diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 46ea4daba8..4a8c9d450c 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -2,120 +2,31 @@ use std::iter::{empty, once, successors}; -use parser::{SyntaxKind, T}; +use parser::T; use crate::{ - AstNode, AstToken, Direction, SyntaxElement, - SyntaxKind::{ATTR, COMMENT, WHITESPACE}, - SyntaxNode, SyntaxToken, + AstNode, AstToken, Direction, algo::{self, neighbor}, - ast::{self, edit::IndentLevel, make}, + ast::{self, make, syntax_factory::SyntaxFactory}, + syntax_editor::SyntaxEditor, ted, }; -use super::{GenericParam, HasName}; - -pub trait AttrsOwnerEdit: ast::HasAttrs { - fn remove_attrs_and_docs(&self) { - remove_attrs_and_docs(self.syntax()); - - fn remove_attrs_and_docs(node: &SyntaxNode) { - let mut remove_next_ws = false; - for child in node.children_with_tokens() { - match child.kind() { - ATTR | COMMENT => { - remove_next_ws = true; - child.detach(); - continue; - } - WHITESPACE if remove_next_ws => { - child.detach(); - } - _ => (), - } - remove_next_ws = false; - } - } - } -} - -impl<T: ast::HasAttrs> AttrsOwnerEdit for T {} +use super::HasName; impl ast::GenericParamList { - pub fn add_generic_param(&self, generic_param: ast::GenericParam) { - match self.generic_params().last() { - Some(last_param) => { - let position = ted::Position::after(last_param.syntax()); - let elements = vec![ - make::token(T![,]).into(), - make::tokens::single_space().into(), - generic_param.syntax().clone().into(), - ]; - ted::insert_all(position, elements); - } - None => { - let after_l_angle = ted::Position::after(self.l_angle_token().unwrap()); - ted::insert(after_l_angle, generic_param.syntax()); - } - } - } - - /// Removes the existing generic param - pub fn remove_generic_param(&self, generic_param: ast::GenericParam) { - if let Some(previous) = generic_param.syntax().prev_sibling() { - if let Some(next_token) = previous.next_sibling_or_token() { - ted::remove_all(next_token..=generic_param.syntax().clone().into()); - } - } else if let Some(next) = generic_param.syntax().next_sibling() { - if let Some(next_token) = next.prev_sibling_or_token() { - ted::remove_all(generic_param.syntax().clone().into()..=next_token); - } - } else { - ted::remove(generic_param.syntax()); - } - } - - /// Find the params corresponded to generic arg - pub fn find_generic_arg(&self, generic_arg: &ast::GenericArg) -> Option<GenericParam> { - self.generic_params().find_map(move |param| match (¶m, &generic_arg) { - (ast::GenericParam::LifetimeParam(a), ast::GenericArg::LifetimeArg(b)) => { - (a.lifetime()?.lifetime_ident_token()?.text() - == b.lifetime()?.lifetime_ident_token()?.text()) - .then_some(param) - } - (ast::GenericParam::TypeParam(a), ast::GenericArg::TypeArg(b)) => { - debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token()); - (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param) - } - (ast::GenericParam::ConstParam(a), ast::GenericArg::TypeArg(b)) => { - debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token()); - (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param) - } - _ => None, - }) - } - - /// Removes the corresponding generic arg - pub fn remove_generic_arg(&self, generic_arg: &ast::GenericArg) { - let param_to_remove = self.find_generic_arg(generic_arg); - - if let Some(param) = ¶m_to_remove { - self.remove_generic_param(param.clone()); - } - } - /// Constructs a matching [`ast::GenericArgList`] - pub fn to_generic_args(&self) -> ast::GenericArgList { + pub fn to_generic_args(&self, make: &SyntaxFactory) -> ast::GenericArgList { let args = self.generic_params().filter_map(|param| match param { ast::GenericParam::LifetimeParam(it) => { - Some(ast::GenericArg::LifetimeArg(make::lifetime_arg(it.lifetime()?))) + Some(ast::GenericArg::LifetimeArg(make.lifetime_arg(it.lifetime()?))) } ast::GenericParam::TypeParam(it) => { - Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?)))) + Some(ast::GenericArg::TypeArg(make.type_arg(make.ty_name(it.name()?)))) } ast::GenericParam::ConstParam(it) => { // Name-only const params get parsed as `TypeArg`s - Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?)))) + Some(ast::GenericArg::TypeArg(make.type_arg(make.ty_name(it.name()?)))) } }); @@ -123,44 +34,10 @@ impl ast::GenericParamList { } } -impl ast::WhereClause { - pub fn add_predicate(&self, predicate: ast::WherePred) { - if let Some(pred) = self.predicates().last() - && !pred.syntax().siblings_with_tokens(Direction::Next).any(|it| it.kind() == T![,]) - { - ted::append_child_raw(self.syntax(), make::token(T![,])); - } - ted::append_child(self.syntax(), predicate.syntax()); - } - - pub fn remove_predicate(&self, predicate: ast::WherePred) { - if let Some(previous) = predicate.syntax().prev_sibling() { - if let Some(next_token) = previous.next_sibling_or_token() { - ted::remove_all(next_token..=predicate.syntax().clone().into()); - } - } else if let Some(next) = predicate.syntax().next_sibling() { - if let Some(next_token) = next.prev_sibling_or_token() { - ted::remove_all(predicate.syntax().clone().into()..=next_token); - } - } else { - ted::remove(predicate.syntax()); - } - } -} - pub trait Removable: AstNode { fn remove(&self); } -impl Removable for ast::TypeBoundList { - fn remove(&self) { - match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) { - Some(colon) => ted::remove_all(colon..=self.syntax().clone().into()), - None => ted::remove(self.syntax()), - } - } -} - impl Removable for ast::UseTree { fn remove(&self) { for dir in [Direction::Next, Direction::Prev] { @@ -179,24 +56,41 @@ impl Removable for ast::UseTree { } impl ast::UseTree { + /// Editor variant of UseTree remove + fn remove_with_editor(&self, editor: &SyntaxEditor) { + for dir in [Direction::Next, Direction::Prev] { + if let Some(next_use_tree) = neighbor(self, dir) { + let separators = self + .syntax() + .siblings_with_tokens(dir) + .skip(1) + .take_while(|it| it.as_node() != Some(next_use_tree.syntax())); + for separator in separators { + editor.delete(separator); + } + break; + } + } + editor.delete(self.syntax()); + } + /// Deletes the usetree node represented by the input. Recursively removes parents, including use nodes that become empty. - pub fn remove_recursive(self) { + pub fn remove_recursive(self, editor: &SyntaxEditor) { let parent = self.syntax().parent(); - self.remove(); - if let Some(u) = parent.clone().and_then(ast::Use::cast) { - if u.use_tree().is_none() { - u.remove(); - } + u.remove(editor); } else if let Some(u) = parent.and_then(ast::UseTreeList::cast) { - if u.use_trees().next().is_none() { - let parent = u.syntax().parent().and_then(ast::UseTree::cast); - if let Some(u) = parent { - u.remove_recursive(); - } + if u.use_trees().nth(1).is_none() + || u.use_trees().all(|use_tree| { + use_tree.syntax() == self.syntax() || editor.deleted(use_tree.syntax()) + }) + { + u.parent_use_tree().remove_recursive(editor); + return; } - u.remove_unnecessary_braces(); + self.remove_with_editor(editor); + u.remove_unnecessary_braces(editor); } } @@ -270,6 +164,35 @@ impl ast::UseTree { } } + /// Editor variant of `split_prefix` + pub fn split_prefix_with_editor(&self, editor: &SyntaxEditor, prefix: &ast::Path) { + debug_assert_eq!(self.path(), Some(prefix.top_path())); + + let make = editor.make(); + let path = self.path().unwrap(); + let suffix = if path == *prefix && self.use_tree_list().is_none() { + if self.star_token().is_some() { + make.use_tree_glob() + } else { + let self_path = make.path_unqualified(make.path_segment_self()); + make.use_tree(self_path, None, None, false) + } + } else { + let suffix_segments = path.segments().skip(prefix.segments().count()); + let suffix_path = make.path_from_segments(suffix_segments, false); + make.use_tree( + suffix_path, + self.use_tree_list(), + self.rename(), + self.star_token().is_some(), + ) + }; + let use_tree_list = make.use_tree_list(once(suffix)); + let new_use_tree = make.use_tree(prefix.clone(), Some(use_tree_list), None, false); + + editor.replace(self.syntax(), new_use_tree.syntax()); + } + /// Wraps the use tree in use tree list with no top level path (if it isn't already). /// /// # Examples @@ -318,8 +241,9 @@ impl ast::UseTreeList { } } -impl Removable for ast::Use { - fn remove(&self) { +impl ast::Use { + fn remove(&self, editor: &SyntaxEditor) { + let make = editor.make(); let next_ws = self .syntax() .next_sibling_or_token() @@ -328,10 +252,17 @@ impl Removable for ast::Use { if let Some(next_ws) = next_ws { let ws_text = next_ws.syntax().text(); if let Some(rest) = ws_text.strip_prefix('\n') { - if rest.is_empty() { - ted::remove(next_ws.syntax()); + let next_use_removed = next_ws + .syntax() + .next_sibling_or_token() + .and_then(|it| it.into_node()) + .and_then(ast::Use::cast) + .and_then(|use_| use_.use_tree()) + .is_some_and(|use_tree| editor.deleted(use_tree.syntax())); + if rest.is_empty() || next_use_removed { + editor.delete(next_ws.syntax()); } else { - ted::replace(next_ws.syntax(), make::tokens::whitespace(rest)); + editor.replace(next_ws.syntax(), make.whitespace(rest)); } } } @@ -345,13 +276,13 @@ impl Removable for ast::Use { let prev_newline = ws_text.rfind('\n').map(|x| x + 1).unwrap_or(0); let rest = &ws_text[0..prev_newline]; if rest.is_empty() { - ted::remove(prev_ws.syntax()); + editor.delete(prev_ws.syntax()); } else { - ted::replace(prev_ws.syntax(), make::tokens::whitespace(rest)); + editor.replace(prev_ws.syntax(), make.whitespace(rest)); } } - ted::remove(self.syntax()); + editor.delete(self.syntax()); } } @@ -365,216 +296,23 @@ impl ast::Impl { } } -impl ast::AssocItemList { - /// Adds a new associated item after all of the existing associated items. - /// - /// Attention! This function does align the first line of `item` with respect to `self`, - /// but it does _not_ change indentation of other lines (if any). - pub fn add_item(&self, item: ast::AssocItem) { - let (indent, position, whitespace) = match self.assoc_items().last() { - Some(last_item) => ( - IndentLevel::from_node(last_item.syntax()), - ted::Position::after(last_item.syntax()), - "\n\n", - ), - None => match self.l_curly_token() { - Some(l_curly) => { - normalize_ws_between_braces(self.syntax()); - (IndentLevel::from_token(&l_curly) + 1, ted::Position::after(&l_curly), "\n") - } - None => (IndentLevel::zero(), ted::Position::last_child_of(self.syntax()), "\n"), - }, - }; - let elements: Vec<SyntaxElement> = vec![ - make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), - item.syntax().clone().into(), - ]; - ted::insert_all(position, elements); - } -} - -impl ast::RecordExprFieldList { - pub fn add_field(&self, field: ast::RecordExprField) { - let is_multiline = self.syntax().text().contains_char('\n'); - let whitespace = if is_multiline { - let indent = IndentLevel::from_node(self.syntax()) + 1; - make::tokens::whitespace(&format!("\n{indent}")) - } else { - make::tokens::single_space() - }; - - if is_multiline { - normalize_ws_between_braces(self.syntax()); - } - - let position = match self.fields().last() { - Some(last_field) => { - let comma = get_or_insert_comma_after(last_field.syntax()); - ted::Position::after(comma) - } - None => match self.l_curly_token() { - Some(it) => ted::Position::after(it), - None => ted::Position::last_child_of(self.syntax()), - }, - }; - - ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); - if is_multiline { - ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,])); - } - } -} - impl ast::RecordExprField { /// This will either replace the initializer, or in the case that this is a shorthand convert /// the initializer into the name ref and insert the expr as the new initializer. - pub fn replace_expr(&self, expr: ast::Expr) { + pub fn replace_expr(&self, editor: &SyntaxEditor, expr: ast::Expr) { if self.name_ref().is_some() { - match self.expr() { - Some(prev) => ted::replace(prev.syntax(), expr.syntax()), - None => ted::append_child(self.syntax(), expr.syntax()), + if let Some(prev) = self.expr() { + editor.replace(prev.syntax(), expr.syntax()); } - return; - } - // this is a shorthand - if let Some(ast::Expr::PathExpr(path_expr)) = self.expr() + } else if let Some(ast::Expr::PathExpr(path_expr)) = self.expr() && let Some(path) = path_expr.path() && let Some(name_ref) = path.as_single_name_ref() { - path_expr.syntax().detach(); - let children = vec![ - name_ref.syntax().clone().into(), - ast::make::token(T![:]).into(), - ast::make::tokens::single_space().into(), - expr.syntax().clone().into(), - ]; - ted::insert_all_raw(ted::Position::last_child_of(self.syntax()), children); - } - } -} - -impl ast::RecordPatFieldList { - pub fn add_field(&self, field: ast::RecordPatField) { - let is_multiline = self.syntax().text().contains_char('\n'); - let whitespace = if is_multiline { - let indent = IndentLevel::from_node(self.syntax()) + 1; - make::tokens::whitespace(&format!("\n{indent}")) - } else { - make::tokens::single_space() - }; - - if is_multiline { - normalize_ws_between_braces(self.syntax()); + // shorthand `{ x }` → expand to `{ x: expr }` + let new_field = editor + .make() + .record_expr_field(editor.make().name_ref(&name_ref.text()), Some(expr)); + editor.replace(self.syntax(), new_field.syntax()); } - - let position = match self.fields().last() { - Some(last_field) => { - let syntax = last_field.syntax(); - let comma = get_or_insert_comma_after(syntax); - ted::Position::after(comma) - } - None => match self.l_curly_token() { - Some(it) => ted::Position::after(it), - None => ted::Position::last_child_of(self.syntax()), - }, - }; - - ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); - if is_multiline { - ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,])); - } - } -} - -fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken { - match syntax - .siblings_with_tokens(Direction::Next) - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T![,]) - { - Some(it) => it, - None => { - let comma = ast::make::token(T![,]); - ted::insert(ted::Position::after(syntax), &comma); - comma - } - } -} - -fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { - let l = node - .children_with_tokens() - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T!['{'])?; - let r = node - .children_with_tokens() - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T!['}'])?; - - let indent = IndentLevel::from_node(node); - - match l.next_sibling_or_token() { - Some(ws) - if ws.kind() == SyntaxKind::WHITESPACE - && ws.next_sibling_or_token()?.into_token()? == r => - { - ted::replace(ws, make::tokens::whitespace(&format!("\n{indent}"))); - } - Some(ws) if ws.kind() == T!['}'] => { - ted::insert(ted::Position::after(l), make::tokens::whitespace(&format!("\n{indent}"))); - } - _ => (), - } - Some(()) -} - -pub trait Indent: AstNode + Clone + Sized { - fn indent_level(&self) -> IndentLevel { - IndentLevel::from_node(self.syntax()) - } - fn indent(&self, by: IndentLevel) { - by.increase_indent(self.syntax()); - } - fn dedent(&self, by: IndentLevel) { - by.decrease_indent(self.syntax()); - } - fn reindent_to(&self, target_level: IndentLevel) { - let current_level = IndentLevel::from_node(self.syntax()); - self.dedent(current_level); - self.indent(target_level); - } -} - -impl<N: AstNode + Clone> Indent for N {} - -#[cfg(test)] -mod tests { - use parser::Edition; - - use crate::SourceFile; - - use super::*; - - fn ast_mut_from_text<N: AstNode>(text: &str) -> N { - let parse = SourceFile::parse(text, Edition::CURRENT); - parse.tree().syntax().descendants().find_map(N::cast).unwrap().clone_for_update() - } - - #[test] - fn test_increase_indent() { - let arm_list = ast_mut_from_text::<ast::Fn>( - "fn foo() { - ; - ; -}", - ); - arm_list.indent(IndentLevel(2)); - assert_eq!( - arm_list.to_string(), - "fn foo() { - ; - ; - }", - ); } } diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 9a2bba9ebf..e3e5c499d4 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -532,6 +532,23 @@ impl ContinueExpr { support::token(&self.syntax, T![continue]) } } +pub struct DerefPat { + pub(crate) syntax: SyntaxNode, +} +impl DerefPat { + #[inline] + pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) } + #[inline] + pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) } + #[inline] + pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } + #[inline] + pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) } + #[inline] + pub fn deref_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![deref]) } +} pub struct DynTraitType { pub(crate) syntax: SyntaxNode, } @@ -2254,6 +2271,7 @@ pub enum Meta { pub enum Pat { BoxPat(BoxPat), ConstBlockPat(ConstBlockPat), + DerefPat(DerefPat), IdentPat(IdentPat), LiteralPat(LiteralPat), MacroPat(MacroPat), @@ -3585,6 +3603,38 @@ impl fmt::Debug for ContinueExpr { f.debug_struct("ContinueExpr").field("syntax", &self.syntax).finish() } } +impl AstNode for DerefPat { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + DEREF_PAT + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == DEREF_PAT } + #[inline] + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for DerefPat { + fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for DerefPat {} +impl PartialEq for DerefPat { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for DerefPat { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for DerefPat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DerefPat").field("syntax", &self.syntax).finish() + } +} impl AstNode for DynTraitType { #[inline] fn kind() -> SyntaxKind @@ -8515,6 +8565,10 @@ impl From<ConstBlockPat> for Pat { #[inline] fn from(node: ConstBlockPat) -> Pat { Pat::ConstBlockPat(node) } } +impl From<DerefPat> for Pat { + #[inline] + fn from(node: DerefPat) -> Pat { Pat::DerefPat(node) } +} impl From<IdentPat> for Pat { #[inline] fn from(node: IdentPat) -> Pat { Pat::IdentPat(node) } @@ -8578,6 +8632,7 @@ impl AstNode for Pat { kind, BOX_PAT | CONST_BLOCK_PAT + | DEREF_PAT | IDENT_PAT | LITERAL_PAT | MACRO_PAT @@ -8599,6 +8654,7 @@ impl AstNode for Pat { let res = match syntax.kind() { BOX_PAT => Pat::BoxPat(BoxPat { syntax }), CONST_BLOCK_PAT => Pat::ConstBlockPat(ConstBlockPat { syntax }), + DEREF_PAT => Pat::DerefPat(DerefPat { syntax }), IDENT_PAT => Pat::IdentPat(IdentPat { syntax }), LITERAL_PAT => Pat::LiteralPat(LiteralPat { syntax }), MACRO_PAT => Pat::MacroPat(MacroPat { syntax }), @@ -8622,6 +8678,7 @@ impl AstNode for Pat { match self { Pat::BoxPat(it) => &it.syntax, Pat::ConstBlockPat(it) => &it.syntax, + Pat::DerefPat(it) => &it.syntax, Pat::IdentPat(it) => &it.syntax, Pat::LiteralPat(it) => &it.syntax, Pat::MacroPat(it) => &it.syntax, @@ -10121,6 +10178,11 @@ impl std::fmt::Display for ContinueExpr { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for DerefPat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for DynTraitType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index ac02cc9e43..95ff3aebd8 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -294,12 +294,7 @@ fn merge_where_clause( (None, None) => None, (None, Some(bs)) => Some(bs), (Some(ps), None) => Some(ps), - (Some(ps), Some(bs)) => { - let preds = where_clause(std::iter::empty()).clone_for_update(); - ps.predicates().for_each(|p| preds.add_predicate(p)); - bs.predicates().for_each(|p| preds.add_predicate(p)); - Some(preds) - } + (Some(ps), Some(bs)) => Some(where_clause(ps.predicates().chain(bs.predicates()))), } } @@ -541,9 +536,10 @@ pub fn block_expr( quote! { BlockExpr { StmtList { - ['{'] "\n" - #(" " #stmts "\n")* - #(" " #tail_expr "\n")* + ['{'] + #("\n " #stmts)* + #("\n " #tail_expr)* + "\n" ['}'] } } @@ -877,6 +873,10 @@ pub fn box_pat(pat: ast::Pat) -> ast::BoxPat { ast_from_text(&format!("fn f(box {pat}: ())")) } +pub fn deref_pat(pat: ast::Pat) -> ast::Pat { + ast_from_text(&format!("fn f(deref!({pat}): ())")) +} + pub fn paren_pat(pat: ast::Pat) -> ast::ParenPat { ast_from_text(&format!("fn f(({pat}): ())")) } diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 751f8d7e1c..1eb658f4b8 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -16,7 +16,7 @@ use crate::{ self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, SyntaxNode, support, }, - ted, + syntax_editor::SyntaxEditor, }; use super::{GenericParam, RangeItem, RangeOp}; @@ -454,11 +454,12 @@ impl ast::UseTreeList { } /// Remove the unnecessary braces in current `UseTreeList` - pub fn remove_unnecessary_braces(mut self) { + pub fn remove_unnecessary_braces(mut self, editor: &SyntaxEditor) { // Returns true iff there is a single subtree and it is not the self keyword. The braces in // `use x::{self};` are necessary and so we should not remove them. let has_single_subtree_that_is_not_self = |u: &ast::UseTreeList| { - if let Some((single_subtree,)) = u.use_trees().collect_tuple() { + let use_trees = u.use_trees().filter(|use_tree| !editor.deleted(use_tree.syntax())); + if let Some((single_subtree,)) = use_trees.collect_tuple() { // We have a single subtree, check whether it is self. let is_self = single_subtree.path().as_ref().is_some_and(|path| { @@ -476,12 +477,12 @@ impl ast::UseTreeList { let remove_brace_in_use_tree_list = |u: &ast::UseTreeList| { if has_single_subtree_that_is_not_self(u) { if let Some(a) = u.l_curly_token() { - ted::remove(a) + editor.delete(a) } if let Some(a) = u.r_curly_token() { - ted::remove(a) + editor.delete(a) } - u.comma().for_each(ted::remove); + u.comma().for_each(|u| editor.delete(u)); } }; diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 0f3b3d301c..1070af65e7 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -2,7 +2,7 @@ use either::Either; use crate::{ - AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, + AstNode, Edition, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, ast::{ self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, HasTypeBounds, HasVisibility, Lifetime, Param, RangeItem, make, @@ -33,6 +33,10 @@ impl SyntaxFactory { make::ext::expr_self().clone_for_update() } + pub fn expr_const_value(&self, text: &str) -> ast::ConstArg { + make::expr_const_value(text).clone_for_update() + } + pub fn lifetime(&self, text: &str) -> ast::Lifetime { make::lifetime(text).clone_for_update() } @@ -63,6 +67,14 @@ impl SyntaxFactory { ast } + pub fn ty_path_from_segments( + &self, + segments: impl IntoIterator<Item = ast::PathSegment>, + is_abs: bool, + ) -> ast::Type { + ast::Type::PathType(self.ty_path(self.path_from_segments(segments, is_abs))) + } + pub fn type_bound(&self, bound: ast::Type) -> ast::TypeBound { make::type_bound(bound).clone_for_update() } @@ -131,6 +143,10 @@ impl SyntaxFactory { make::path_from_text(text).clone_for_update() } + pub fn path_from_text_with_edition(&self, text: &str, edition: Edition) -> ast::Path { + make::path_from_text_with_edition(text, edition).clone_for_update() + } + pub fn path_concat(&self, first: ast::Path, second: ast::Path) -> ast::Path { make::path_concat(first, second).clone_for_update() } @@ -491,6 +507,18 @@ impl SyntaxFactory { ast } + pub fn path_segment_self(&self) -> ast::PathSegment { + make::path_segment_self().clone_for_update() + } + + pub fn path_segment_super(&self) -> ast::PathSegment { + make::path_segment_super().clone_for_update() + } + + pub fn path_segment_crate(&self) -> ast::PathSegment { + make::path_segment_crate().clone_for_update() + } + pub fn generic_ty_path_segment( &self, name_ref: ast::NameRef, @@ -551,6 +579,25 @@ impl SyntaxFactory { make::ty_placeholder().clone_for_update() } + pub fn ty_unit(&self) -> ast::Type { + make::ty_unit().clone_for_update() + } + + pub fn ty_tuple(&self, types: impl IntoIterator<Item = ast::Type>) -> ast::Type { + let (types, input) = iterator_input(types); + let ast = make::ty_tuple(types).clone_for_update(); + + if let Some(mut mapping) = self.mappings() + && let ast::Type::TupleType(tuple_ty) = &ast + { + let mut builder = SyntaxMappingBuilder::new(tuple_ty.syntax().clone()); + builder.map_children(input, tuple_ty.fields().map(|ty| ty.syntax().clone())); + builder.finish(&mut mapping); + } + + ast + } + pub fn path_segment_generics( &self, name_ref: ast::NameRef, @@ -628,6 +675,10 @@ impl SyntaxFactory { ast } + pub fn use_tree_glob(&self) -> ast::UseTree { + make::use_tree_glob().clone_for_update() + } + pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path { let ast = make::path_unqualified(segment.clone()).clone_for_update(); @@ -640,6 +691,23 @@ impl SyntaxFactory { ast } + pub fn path_qualified(&self, qual: ast::Path, segment: ast::PathSegment) -> ast::Path { + let ast = make::path_qualified(qual.clone(), segment.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + if let Some(out_qual) = ast.qualifier() { + builder.map_node(qual.syntax().clone(), out_qual.syntax().clone()); + } + if let Some(out_segment) = ast.segment() { + builder.map_node(segment.syntax().clone(), out_segment.syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast + } + pub fn path_from_segments( &self, segments: impl IntoIterator<Item = ast::PathSegment>, @@ -676,6 +744,18 @@ impl SyntaxFactory { ast } + pub fn simple_ident_pat(&self, name: ast::Name) -> ast::IdentPat { + let ast = make::ext::simple_ident_pat(name.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn wildcard_pat(&self) -> ast::WildcardPat { make::wildcard_pat().clone_for_update() } @@ -851,6 +931,10 @@ impl SyntaxFactory { ast } + pub fn deref_pat(&self, pat: ast::Pat) -> ast::Pat { + make::deref_pat(pat.clone()).clone_for_update() + } + pub fn paren_pat(&self, pat: ast::Pat) -> ast::ParenPat { let ast = make::paren_pat(pat.clone()).clone_for_update(); @@ -925,6 +1009,37 @@ impl SyntaxFactory { ast } + pub fn async_move_block_expr( + &self, + statements: impl IntoIterator<Item = ast::Stmt>, + tail_expr: Option<ast::Expr>, + ) -> ast::BlockExpr { + let (statements, mut input) = iterator_input(statements); + + let ast = make::async_move_block_expr(statements, tail_expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let stmt_list = ast.stmt_list().unwrap(); + let mut builder = SyntaxMappingBuilder::new(stmt_list.syntax().clone()); + + if let Some(input) = tail_expr { + builder.map_node( + input.syntax().clone(), + stmt_list.tail_expr().unwrap().syntax().clone(), + ); + } else if let Some(ast_tail) = stmt_list.tail_expr() { + let last_stmt = input.pop().unwrap(); + builder.map_node(last_stmt, ast_tail.syntax().clone()); + } + + builder.map_children(input, stmt_list.statements().map(|it| it.syntax().clone())); + + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_empty_block(&self) -> ast::BlockExpr { make::expr_empty_block().clone_for_update() } @@ -1075,6 +1190,27 @@ impl SyntaxFactory { ast.into() } + pub fn expr_reborrow(&self, expr: ast::Expr) -> ast::Expr { + let ast::Expr::RefExpr(ast) = make::expr_reborrow(expr.clone()).clone_for_update() else { + unreachable!() + }; + + if let Some(mut mapping) = self.mappings() { + // Layout: RefExpr(&mut, PrefixExpr(*, expr)). Map `expr` to the + // inner expr inside the synthesized PrefixExpr. + let prefix = match ast.expr() { + Some(ast::Expr::PrefixExpr(p)) => p, + _ => unreachable!("expr_reborrow always produces `&mut *expr`"), + }; + let inner = prefix.expr().unwrap(); + let mut builder = SyntaxMappingBuilder::new(prefix.syntax().clone()); + builder.map_node(expr.syntax().clone(), inner.syntax().clone()); + builder.finish(&mut mapping); + } + + ast.into() + } + pub fn expr_raw_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr { let ast::Expr::RefExpr(ast) = make::expr_raw_ref(expr.clone(), exclusive).clone_for_update() @@ -2113,6 +2249,21 @@ impl SyntaxFactory { make::ext::field_from_idents(parts) } + pub fn ty_name(&self, name: ast::Name) -> ast::Type { + let ast = make::ext::ty_name(name.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() + && let ast::Type::PathType(path_ty) = &ast + && let Some(name_ref) = path_ty.path().and_then(|path| path.segment()?.name_ref()) + { + let mut builder = SyntaxMappingBuilder::new(name_ref.syntax().parent().unwrap()); + builder.map_node(name.syntax().clone(), name_ref.syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_await(&self, expr: ast::Expr) -> ast::AwaitExpr { let ast::Expr::AwaitExpr(ast) = make::expr_await(expr.clone()).clone_for_update() else { unreachable!() @@ -2127,6 +2278,53 @@ impl SyntaxFactory { ast } + pub fn expr_try(&self, expr: ast::Expr) -> ast::Expr { + let ast = make::expr_try(expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + if let ast::Expr::TryExpr(try_expr) = &ast + && let Some(inner) = try_expr.expr() + { + builder.map_node(expr.syntax().clone(), inner.syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast + } + + pub fn hacky_block_expr( + &self, + elements: impl IntoIterator<Item = SyntaxElement>, + tail_expr: Option<ast::Expr>, + ) -> ast::BlockExpr { + let elements = elements.into_iter().collect::<Vec<_>>(); + let ast = + make::hacky_block_expr(elements.iter().cloned(), tail_expr.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() + && let Some(stmt_list) = ast.stmt_list() + { + let mut builder = SyntaxMappingBuilder::new(stmt_list.syntax().clone()); + builder.map_children( + elements.into_iter().filter_map(|node_or_token| match node_or_token { + NodeOrToken::Node(node) => Some(node), + NodeOrToken::Token(_) => None, + }), + stmt_list.syntax().children(), + ); + if let Some(tail_expr) = tail_expr + && let Some(output_tail_expr) = stmt_list.tail_expr() + { + builder.map_node(tail_expr.syntax().clone(), output_tail_expr.syntax().clone()); + } + builder.finish(&mut mapping); + } + + ast + } + pub fn expr_break(&self, label: Option<Lifetime>, expr: Option<ast::Expr>) -> ast::BreakExpr { let ast::Expr::BreakExpr(ast) = make::expr_break(label.clone(), expr.clone()).clone_for_update() diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index c510b2831e..cda3e69b7c 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs @@ -10,8 +10,8 @@ //! the [Swift] one. //! //! The most interesting modules here are `syntax_node` (which defines concrete -//! syntax tree) and `ast` (which defines abstract syntax tree on top of the -//! CST). The actual parser live in a separate `parser` crate, though the +//! syntax tree) and [`ast`] (which defines abstract syntax tree on top of the +//! CST). The actual parser live in a separate [`parser`] crate, though the //! lexer lives in this crate. //! //! See `api_walkthrough` test in this file for a quick API tour! diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs index c4979b8e3a..ed8a68fb8a 100644 --- a/crates/syntax/src/ptr.rs +++ b/crates/syntax/src/ptr.rs @@ -21,7 +21,7 @@ use crate::{AstNode, SyntaxNode, syntax_node::RustLanguage}; /// A "pointer" to a [`SyntaxNode`], via location in the source code. pub type SyntaxNodePtr = rowan::ast::SyntaxNodePtr<RustLanguage>; -/// Like `SyntaxNodePtr`, but remembers the type of node. +/// Like [`SyntaxNodePtr`], but remembers the type of node. pub struct AstPtr<N: AstNode> { raw: SyntaxNodePtr, _ty: PhantomData<fn() -> N>, @@ -90,7 +90,7 @@ impl<N: AstNode> AstPtr<N> { AstPtr { raw: self.raw, _ty: PhantomData } } - /// Like `SyntaxNodePtr::cast` but the trait bounds work out. + /// Like [`SyntaxNodePtr::cast`] but the trait bounds work out. pub fn try_from_raw(raw: SyntaxNodePtr) -> Option<AstPtr<N>> { N::can_cast(raw.kind()).then_some(AstPtr { raw, _ty: PhantomData }) } diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index edd063ffd4..7d15195c6f 100644 --- a/crates/syntax/src/syntax_editor.rs +++ b/crates/syntax/src/syntax_editor.rs @@ -133,7 +133,19 @@ impl SyntaxEditor { !matches!(&element, SyntaxElement::Node(node) if node == &self.root), "should not delete root node" ); - self.changes.borrow_mut().push(Change::Replace(element.syntax_element(), None)); + let mut changes = self.changes.borrow_mut(); + for change in changes.iter_mut() { + if let Change::Replace(existing, replacement) = change + && *existing == element + { + if replacement.is_none() { + return; + } + *replacement = None; + return; + } + } + changes.push(Change::Replace(element, None)); } pub fn delete_all(&self, range: RangeInclusive<SyntaxElement>) { @@ -149,9 +161,23 @@ impl SyntaxEditor { pub fn replace(&self, old: impl Element, new: impl Element) { let old = old.syntax_element(); debug_assert!(is_ancestor_or_self_of_element(&old, &self.root)); - self.changes - .borrow_mut() - .push(Change::Replace(old.syntax_element(), Some(new.syntax_element()))); + let new = new.syntax_element(); + let mut changes = self.changes.borrow_mut(); + for change in changes.iter_mut() { + if let Change::Replace(existing, replacement) = change + && *existing == old + { + match replacement { + None => return, + Some(existing_new) if *existing_new == new => return, + Some(existing_new) => { + *existing_new = new; + return; + } + } + } + } + changes.push(Change::Replace(old, Some(new))); } pub fn replace_with_many(&self, old: impl Element, new: Vec<SyntaxElement>) { @@ -177,6 +203,14 @@ impl SyntaxEditor { pub fn finish(self) -> SyntaxEdit { edit_algo::apply_edits(self) } + + pub fn deleted(&self, element: impl Element) -> bool { + let element = element.syntax_element(); + self.changes + .borrow() + .iter() + .any(|change| matches!(change, Change::Replace(existing, None) if *existing == element)) + } } /// Represents a completed [`SyntaxEditor`] operation. @@ -216,6 +250,17 @@ impl SyntaxEdit { pub fn find_annotation(&self, annotation: SyntaxAnnotation) -> &[SyntaxElement] { self.annotations.get(&annotation).as_ref().map_or(&[], |it| it.as_slice()) } + + pub fn find_element(&self, old_node: &SyntaxNode) -> Option<SyntaxNode> { + let old_root_start = self.old_root.text_range().start(); + let old_start = old_node.text_range().start() - old_root_start; + let new_root_start = self.new_root.text_range().start(); + let kind = old_node.kind(); + + self.new_root + .descendants() + .find(|it| it.kind() == kind && it.text_range().start() - new_root_start == old_start) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -702,6 +747,62 @@ mod tests { } #[test] + fn test_dependent_change_prefers_nearest_changed_ancestor() { + let root = make::block_expr( + [], + Some( + make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ) + .into(), + ), + ); + + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); + + let inner_block = + root.syntax().descendants().flat_map(ast::BlockExpr::cast).nth(1).unwrap(); + + let outer_replacement = make.block_expr([], Some(ast::Expr::BlockExpr(root.clone()))); + let inner_replacement = + make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); + + let first_let = make.let_stmt( + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ); + + editor.insert( + Position::first_child_of(inner_block.stmt_list().unwrap().syntax()), + first_let.syntax(), + ); + editor.replace(inner_block.syntax(), inner_replacement.syntax()); + editor.replace(root.syntax(), outer_replacement.syntax()); + + let edit = editor.finish(); + + let expect = expect![[r#" + { + { + { + let first = 1;{ + let second = 2; + } + } + } + }"#]]; + expect.assert_eq(&edit.new_root.to_string()); + } + + #[test] fn test_replace_root_with_dependent() { let root = make::block_expr( [make::let_stmt( diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs index 27ea03ec09..36f50e3918 100644 --- a/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/crates/syntax/src/syntax_editor/edit_algo.rs @@ -111,8 +111,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // Check if this change is dependent on another change (i.e. it's contained within another range) if let Some(index) = changed_ancestors .iter() - .rev() - .position(|ancestor| ancestor.affected_range().contains_range(change.target_range())) + .rposition(|ancestor| ancestor.affected_range().contains_range(change.target_range())) { // Pop off any ancestors that aren't applicable changed_ancestors.drain((index + 1)..); @@ -284,7 +283,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { } } - for DependentChange { parent, child } in dependent_changes.into_iter() { + for DependentChange { parent, child } in dependent_changes.into_iter().rev() { let (input_ancestor, output_ancestor) = match &changes[parent as usize] { // No change will depend on an insert since changes can only depend on nodes in the root tree Change::Insert(_, _) | Change::InsertAll(_, _) => unreachable!(), diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs index 28e8ceed70..0338d976b0 100644 --- a/crates/syntax/src/syntax_editor/edits.rs +++ b/crates/syntax/src/syntax_editor/edits.rs @@ -3,7 +3,7 @@ use crate::{ AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, algo::neighbor, - ast::{self, AstNode, Fn, GenericParam, HasGenericParams, HasName, edit::IndentLevel, make}, + ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel, make}, syntax_editor::{Position, SyntaxEditor}, }; @@ -109,64 +109,79 @@ impl GetOrCreateWhereClause for ast::Enum { } impl SyntaxEditor { - /// Adds a new generic param to the function using `SyntaxEditor` - pub fn add_generic_param(&self, function: &Fn, new_param: GenericParam) { - match function.generic_param_list() { - Some(generic_param_list) => match generic_param_list.generic_params().last() { - Some(last_param) => { - // There exists a generic param list and it's not empty - let position = generic_param_list.r_angle_token().map_or_else( - || Position::last_child_of(function.syntax()), - Position::before, - ); - - if last_param - .syntax() - .next_sibling_or_token() - .is_some_and(|it| it.kind() == SyntaxKind::COMMA) - { - self.insert( - Position::after(last_param.syntax()), - new_param.syntax().clone(), - ); - self.insert( - Position::after(last_param.syntax()), - make::token(SyntaxKind::WHITESPACE), - ); - self.insert( - Position::after(last_param.syntax()), - make::token(SyntaxKind::COMMA), - ); + /// Adds a new generic param to the node using `SyntaxEditor` + pub fn add_generic_param( + &self, + node: &impl ast::HasGenericParams, + new_param: ast::GenericParam, + ) { + let make = self.make(); + match node.generic_param_list() { + Some(generic_param_list) => { + let is_lifetime = matches!(new_param, ast::GenericParam::LifetimeParam(_)); + + if let Some(first_param) = generic_param_list.generic_params().next() { + let last_lifetime = generic_param_list + .generic_params() + .filter(|p| matches!(p, ast::GenericParam::LifetimeParam(_))) + .last(); + + if is_lifetime { + if let Some(last_lt) = last_lifetime { + let elements = vec![ + make.token(SyntaxKind::COMMA).into(), + make.token(SyntaxKind::WHITESPACE).into(), + new_param.syntax().clone().into(), + ]; + self.insert_all(Position::after(last_lt.syntax()), elements); + } else { + // Insert before the first parameter + let elements = vec![ + new_param.syntax().clone().into(), + make.token(SyntaxKind::COMMA).into(), + make.token(SyntaxKind::WHITESPACE).into(), + ]; + self.insert_all(Position::before(first_param.syntax()), elements); + } } else { + let last_param = generic_param_list.generic_params().last().unwrap(); let elements = vec![ - make::token(SyntaxKind::COMMA).into(), - make::token(SyntaxKind::WHITESPACE).into(), + make.token(SyntaxKind::COMMA).into(), + make.token(SyntaxKind::WHITESPACE).into(), new_param.syntax().clone().into(), ]; - self.insert_all(position, elements); + self.insert_all(Position::after(last_param.syntax()), elements); + } + } else { + if let Some(l_angle) = generic_param_list.l_angle_token() { + self.insert(Position::after(l_angle), new_param.syntax().clone()); } } - None => { - // There exists a generic param list but it's empty - let position = Position::after(generic_param_list.l_angle_token().unwrap()); - self.insert(position, new_param.syntax()); - } - }, + } None => { - // There was no generic param list - let position = if let Some(name) = function.name() { - Position::after(name.syntax) - } else if let Some(fn_token) = function.fn_token() { - Position::after(fn_token) - } else if let Some(param_list) = function.param_list() { - Position::before(param_list.syntax) - } else { - Position::last_child_of(function.syntax()) - }; + let position = + if let Some(name) = node.syntax().children().find_map(ast::Name::cast) { + Position::after(name.syntax()) + } else if let Some(impl_node) = ast::Impl::cast(node.syntax().clone()) { + impl_node + .impl_token() + .map_or_else(|| Position::last_child_of(node.syntax()), Position::after) + } else if let Some(fn_node) = ast::Fn::cast(node.syntax().clone()) { + if let Some(fn_token) = fn_node.fn_token() { + Position::after(fn_token) + } else if let Some(param_list) = fn_node.param_list() { + Position::before(param_list.syntax()) + } else { + Position::last_child_of(node.syntax()) + } + } else { + Position::last_child_of(node.syntax()) + }; + let elements = vec![ - make::token(SyntaxKind::L_ANGLE).into(), + make.token(SyntaxKind::L_ANGLE).into(), new_param.syntax().clone().into(), - make::token(SyntaxKind::R_ANGLE).into(), + make.token(SyntaxKind::R_ANGLE).into(), ]; self.insert_all(position, elements); } @@ -176,11 +191,7 @@ impl SyntaxEditor { fn get_or_insert_comma_after(editor: &SyntaxEditor, syntax: &SyntaxNode) -> SyntaxToken { let make = editor.make(); - match syntax - .siblings_with_tokens(Direction::Next) - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T![,]) - { + match comma_after(syntax) { Some(it) => it, None => { let comma = make.token(T![,]); @@ -226,6 +237,113 @@ impl ast::AssocItemList { } } +impl ast::RecordExprFieldList { + pub fn add_fields( + &self, + editor: &SyntaxEditor, + fields: impl IntoIterator<Item = ast::RecordExprField>, + ) { + add_record_fields( + editor, + self.syntax(), + self.fields().last().map(|it| it.syntax().clone()), + self.l_curly_token(), + fields.into_iter().map(|it| it.syntax().clone().into()), + ); + } +} + +impl ast::RecordPatFieldList { + pub fn add_fields( + &self, + editor: &SyntaxEditor, + fields: impl IntoIterator<Item = ast::RecordPatField>, + ) { + add_record_fields( + editor, + self.syntax(), + self.fields().last().map(|it| it.syntax().clone()), + self.l_curly_token(), + fields.into_iter().map(|it| it.syntax().clone().into()), + ); + } +} + +fn add_record_fields( + editor: &SyntaxEditor, + field_list: &SyntaxNode, + last_field: Option<SyntaxNode>, + l_curly: Option<SyntaxToken>, + fields: impl Iterator<Item = SyntaxElement>, +) { + let fields = fields.collect::<Vec<_>>(); + if fields.is_empty() { + return; + } + + let make = editor.make(); + let is_multiline = field_list.text().contains_char('\n'); + let whitespace = || { + if is_multiline { + let indent = IndentLevel::from_node(field_list) + 1; + make.whitespace(&format!("\n{indent}")) + } else { + make.whitespace(" ") + } + }; + + if is_multiline { + normalize_ws_between_braces(editor, field_list); + } + + let mut elements = Vec::new(); + let next_after_insert; + let position = match last_field { + Some(last_field) => match comma_after(&last_field) { + Some(comma) => { + next_after_insert = comma.next_sibling_or_token(); + Position::after(comma) + } + None => { + next_after_insert = last_field.next_sibling_or_token(); + elements.push(make.token(T![,]).into()); + Position::after(last_field) + } + }, + None => match l_curly { + Some(it) => { + next_after_insert = it.next_sibling_or_token(); + Position::after(it) + } + None => { + next_after_insert = None; + Position::last_child_of(field_list) + } + }, + }; + + let fields_len = fields.len(); + for (idx, field) in fields.into_iter().enumerate() { + elements.push(whitespace().into()); + elements.push(field); + if is_multiline || idx + 1 != fields_len { + elements.push(make.token(T![,]).into()); + } + } + if !is_multiline && next_after_insert.is_some_and(|it| it.kind() != SyntaxKind::WHITESPACE) { + elements.push(make.whitespace(" ").into()); + } + + editor.insert_all(position, elements); +} + +fn comma_after(syntax: &SyntaxNode) -> Option<SyntaxToken> { + syntax + .siblings_with_tokens(Direction::Next) + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == T![,]) +} + impl ast::Impl { pub fn get_or_create_assoc_item_list_with_editor( &self, diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index a51698aca8..8975fa56d7 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -11,6 +11,7 @@ //! add: //! asm: //! assert: +//! async_iterator: option, future, pin //! as_mut: sized //! as_ref: sized //! async_fn: fn, tuple, future, copy @@ -27,12 +28,14 @@ //! default: sized //! deref_mut: deref //! deref: sized +//! deref_pat: deref //! derive: //! discriminant: //! drop: sized //! env: option //! eq: sized //! error: fmt +//! float_consts: //! fmt: option, result, transmute, coerce_unsized, copy, clone, derive //! fmt_before_1_93_0: fmt //! fmt_before_1_89_0: fmt_before_1_93_0 @@ -50,6 +53,7 @@ //! iterator: option //! iterators: iterator, fn //! manually_drop: drop +//! matches: //! non_null: //! non_zero: //! option: panic @@ -66,7 +70,7 @@ //! size_of: sized //! sized: //! slice: -//! str: +//! str: sized, result //! sync: sized //! transmute: //! try: infallible @@ -614,6 +618,15 @@ pub mod ops { } // endregion:deref_mut + // region:deref_pat + #[lang = "deref_pure"] + #[rustc_dyn_incompatible_trait] + pub unsafe trait DerefPure: PointeeSized {} + + unsafe impl<T: ?Sized> DerefPure for &T {} + unsafe impl<T: ?Sized> DerefPure for &mut T {} + // endregion:deref_pat + // region:receiver #[lang = "receiver"] pub trait Receiver: PointeeSized { @@ -631,8 +644,9 @@ pub mod ops { } pub use self::deref::{ Deref, - DerefMut, // :deref_mut - Receiver, // :receiver + DerefMut, // :deref_mut + DerefPure, // :deref_pat + Receiver, // :receiver }; // endregion:deref @@ -698,6 +712,37 @@ pub mod ops { unsafe impl<T> SliceIndex<[T]> for usize { type Output = T; } + + macro_rules! impl_index_range { + ( $($range:ty,)* ) => { + $( + unsafe impl<T> SliceIndex<[T]> for $range { + type Output = [T]; + } + )* + } + } + + // region:range + impl_index_range!( + crate::ops::RangeFull, + crate::ops::Range<usize>, + crate::ops::RangeFrom<usize>, + crate::ops::RangeTo<usize>, + crate::ops::RangeInclusive<usize>, + crate::ops::RangeToInclusive<usize>, + ); + // endregion:range + + // region:new_range + impl_index_range!( + crate::range::Range<usize>, + crate::range::RangeFrom<usize>, + crate::range::RangeInclusive<usize>, + crate::range::RangeToInclusive<usize>, + ); + // endregion:new_range + // endregion:slice } pub use self::index::{Index, IndexMut}; @@ -735,6 +780,30 @@ pub mod ops { pub struct RangeToInclusive<Idx> { pub end: Idx, } + + // region:iterator + pub trait Step {} + macro_rules! impl_step { + ( $( $ty:ty ),* $(,)? ) => { + $( + impl Step for $ty {} + )* + }; + } + impl_step!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize); + + macro_rules! impl_iterator { + ( $( $range:ident ),* $(,)? ) => { + $( + impl<Idx: Step> Iterator for $range<Idx> { + type Item = Idx; + fn next(&mut self) -> Option<Self::Item> { loop {} } + } + )* + }; + } + impl_iterator!(Range, RangeFrom, RangeTo, RangeInclusive, RangeToInclusive); + // endregion:iterator } pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; pub use self::range::{RangeInclusive, RangeToInclusive}; @@ -1289,6 +1358,38 @@ pub mod fmt { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } + impl<T: ?Sized + Debug> Debug for &T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + T::fmt(&**self, f) + } + } + impl<T: ?Sized + Display> Display for &T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + T::fmt(&**self, f) + } + } + + macro_rules! impl_fmt_traits { + ( $($ty:ty),* $(,)? ) => { + $( + impl Debug for $ty { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { loop {} } + } + impl Display for $ty { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { loop {} } + } + )* + } + } + + impl_fmt_traits!(str); + + // region:builtin_impls + impl_fmt_traits!( + i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, char, + ); + // endregion:builtin_impls + mod rt { use super::*; @@ -1529,6 +1630,7 @@ pub mod slice { // region:option pub mod option { + #[lang = "Option"] pub enum Option<T> { #[lang = "None"] None, @@ -1678,6 +1780,7 @@ pub mod future { } } pub mod task { + #[lang = "Poll"] pub enum Poll<T> { #[lang = "Ready"] Ready(T), @@ -1691,6 +1794,22 @@ pub mod task { } // endregion:future +// region:async_iterator +pub mod async_iter { + use crate::{ + pin::Pin, + task::{Context, Poll}, + }; + + #[lang = "async_iterator"] + pub trait AsyncIterator { + type Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>; + } +} +// endregion:async_iterator + // region:iterator pub mod iter { // region:iterators @@ -1725,6 +1844,22 @@ pub mod iter { } } + pub struct Map<I, F> { + iter: I, + f: F, + } + impl<B, I: Iterator, F> Iterator for Map<I, F> + where + F: FnMut(I::Item) -> B, + { + type Item = B; + + #[inline] + fn next(&mut self) -> B { + loop {} + } + } + pub struct FilterMap<I, F> { iter: I, f: F, @@ -1799,6 +1934,13 @@ pub mod iter { { loop {} } + fn map<B, F>(self, _f: F) -> crate::iter::Map<Self, F> + where + Self: Sized, + F: FnMut(Self::Item) -> B, + { + loop {} + } fn filter_map<B, F>(self, _f: F) -> crate::iter::FilterMap<Self, F> where Self: Sized, @@ -1861,7 +2003,7 @@ pub mod iter { pub struct Iter<'a, T> { slice: &'a [T], } - impl<'a, T> IntoIterator for &'a [T; N] { + impl<'a, T, const N: usize> IntoIterator for &'a [T; N] { type Item = &'a T; type IntoIter = Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { @@ -2127,6 +2269,13 @@ mod macros { #[macro_export] macro_rules! option_env {} // endregion:env + + // region:deref_pat + #[allow_internal_unstable(builtin_syntax)] + pub macro deref($pat:pat) { + builtin # deref($pat) + } + // endregion:deref_pat } // region:non_zero @@ -2181,6 +2330,32 @@ pub mod error { } // endregion:error +// region:float_consts +impl f32 { + pub const INFINITY: f32 = 0.0; + pub const NEG_INFINITY: f32 = -0.0; +} + +impl f64 { + pub const INFINITY: f64 = 0.0; + pub const NEG_INFINITY: f64 = -0.0; +} + +pub mod f32 { + #[deprecated] + pub const INFINITY: f32 = 0.0; + #[deprecated] + pub const NEG_INFINITY: f32 = -0.0; +} + +pub mod f64 { + #[deprecated] + pub const INFINITY: f64 = 0.0; + #[deprecated] + pub const NEG_INFINITY: f64 = -0.0; +} +// endregion:float_consts + // region:column #[rustc_builtin_macro] #[macro_export] @@ -2189,6 +2364,20 @@ macro_rules! column { } // endregion:column +// region:matches +#[macro_export] +#[allow_internal_unstable(non_exhaustive_omitted_patterns_lint, stmt_expr_attributes)] +macro_rules! matches { + ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { + #[allow(non_exhaustive_omitted_patterns)] + match $expression { + $pattern $(if $guard)? => true, + _ => false + } + }; +} +// endregion:matches + pub mod prelude { pub mod v1 { pub use crate::{ @@ -2216,6 +2405,7 @@ pub mod prelude { panic, // :panic result::Result::{self, Err, Ok}, // :result str::FromStr, // :str + macros::deref, // :deref_pat }; } diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md index da37fc1582..069c8211db 100644 --- a/docs/book/src/configuration_generated.md +++ b/docs/book/src/configuration_generated.md @@ -375,6 +375,15 @@ If false, `-p <package>` will be passed instead if applicable. In case it is not check will be performed. +## rust-analyzer.completion.addColonsToModule {#completion.addColonsToModule} + +Default: `true` + +Automatically add `::` when completing the module. + +Will not be completed in `use`. + + ## rust-analyzer.completion.addSemicolonToUnit {#completion.addSemicolonToUnit} Default: `true` diff --git a/docs/book/src/contributing/lsp-extensions.md b/docs/book/src/contributing/lsp-extensions.md index 8ba6f6ab53..b74c40c422 100644 --- a/docs/book/src/contributing/lsp-extensions.md +++ b/docs/book/src/contributing/lsp-extensions.md @@ -1,5 +1,5 @@ <!--- -lsp/ext.rs hash: dc4ba5f417c74aa6 +lsp/ext.rs hash: 57ae57d2a5c65b14 If you need to change the above hash to make the test pass, please check if you need to adjust this doc as well and ping this issue: diff --git a/editors/code/package.json b/editors/code/package.json index 67570cd067..14369e6f33 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1277,6 +1277,16 @@ { "title": "Completion", "properties": { + "rust-analyzer.completion.addColonsToModule": { + "markdownDescription": "Automatically add `::` when completing the module.\n\nWill not be completed in `use`.", + "default": true, + "type": "boolean" + } + } + }, + { + "title": "Completion", + "properties": { "rust-analyzer.completion.addSemicolonToUnit": { "markdownDescription": "Automatically add a semicolon when completing unit-returning functions.\n\nIn `match` arms it completes a comma instead.", "default": true, diff --git a/lib/smol_str/README.md b/lib/smol_str/README.md index 56296fb53f..7b52a6c5b3 100644 --- a/lib/smol_str/README.md +++ b/lib/smol_str/README.md @@ -8,7 +8,7 @@ A `SmolStr` is a string type that has the following properties: * `size_of::<SmolStr>() == 24` (therefore `== size_of::<String>()` on 64 bit platforms) -* `Clone` is `O(1)` +* `Clone` is `O(1)` (no allocation) * Strings are stack-allocated if they are: * Up to 23 bytes long * Longer than 23 bytes, but substrings of `WS` (see `src/lib.rs`). Such strings consist diff --git a/lib/smol_str/src/lib.rs b/lib/smol_str/src/lib.rs index 55ede286c2..b76c1c7d14 100644 --- a/lib/smol_str/src/lib.rs +++ b/lib/smol_str/src/lib.rs @@ -15,8 +15,8 @@ use core::{ /// A `SmolStr` is a string type that has the following properties: /// -/// * `size_of::<SmolStr>() == 24` (therefor `== size_of::<String>()` on 64 bit platforms) -/// * `Clone` is `O(1)` +/// * `size_of::<SmolStr>() == 24` (therefore `== size_of::<String>()` on 64 bit platforms) +/// * `Clone` is `O(1)` (no allocation) /// * Strings are stack-allocated if they are: /// * Up to 23 bytes long /// * Longer than 23 bytes, but substrings of `WS` (see below). Such strings consist diff --git a/rust-version b/rust-version index e9fc6c4cd0..1e8579c045 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -e22c616e4e87914135c1db261a03e0437255335e +8afb6a8b1b32fce2f8aa7520517833338dc36c5e diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs index a0abdf09d3..43462d1c6e 100644 --- a/xtask/src/codegen/grammar/ast_src.rs +++ b/xtask/src/codegen/grammar/ast_src.rs @@ -150,6 +150,7 @@ const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ // "raw", "readonly", "sym", + "deref", ]; // keywords that are keywords depending on the edition |