diff --git a/Cargo.lock b/Cargo.lock index 9c6d6c22de4dd..9b2bcfd1e8b8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1759,12 +1759,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1772,9 +1773,9 @@ dependencies = [ [[package]] name = "icu_list" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a0b7b126e2fc42777d3c348611553d540bd3683caa39b387c5dd1036bb21a8" +checksum = "aeeaf517689324395bed4767f7c65504f5455942ed4c14ee54c2087ca00b816e" dependencies = [ "icu_provider", "regex-automata", @@ -1785,9 +1786,9 @@ dependencies = [ [[package]] name = "icu_locale" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532b11722e350ab6bf916ba6eb0efe3ee54b932666afec989465f9243fe6dd60" +checksum = "d5a396343c7208121dc86e35623d3dfe19814a7613cfd14964994cdc9c9a2e26" dependencies = [ "icu_collections", "icu_locale_core", @@ -1800,9 +1801,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1814,15 +1815,15 @@ dependencies = [ [[package]] name = "icu_locale_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03e2fcaefecdf05619f3d6f91740e79ab969b4dd54f77cbf546b1d0d28e3147" +checksum = "d5fdcc9ac77c6d74ff5cf6e65ef3181d6af32003b16fce3a77fb451d2f695993" [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1834,15 +1835,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -1854,15 +1855,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -3113,9 +3114,9 @@ checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" [[package]] name = "quote" -version = "1.0.42" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -5339,9 +5340,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.38.4" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f" +checksum = "cd9f9fe3d2b7b75cf4f2805e5b9926e8ac47146667b16b86298c4a8bf08cc469" dependencies = [ "libc", "objc2-core-foundation", @@ -5552,9 +5553,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "serde_core", @@ -6714,9 +6715,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -6725,9 +6726,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -6778,20 +6779,21 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", "zerofrom", + "zerovec", ] [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "serde", "yoke", @@ -6801,9 +6803,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", diff --git a/compiler/rustc_baked_icu_data/Cargo.toml b/compiler/rustc_baked_icu_data/Cargo.toml index 2f1ab7df3790e..c3887e4580d71 100644 --- a/compiler/rustc_baked_icu_data/Cargo.toml +++ b/compiler/rustc_baked_icu_data/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -icu_list = { version = "2.0", default-features = false } -icu_locale = { version = "2.0", default-features = false, features = ["compiled_data"] } -icu_provider = { version = "2.0", features = ["baked", "sync"] } +icu_list = { version = "2.2", default-features = false } +icu_locale = { version = "2.2", default-features = false, features = ["compiled_data"] } +icu_provider = { version = "2.2", features = ["baked", "sync"] } zerovec = "0.11.0" # tidy-alphabetical-end diff --git a/compiler/rustc_baked_icu_data/src/data/list_and_v1.rs.data b/compiler/rustc_baked_icu_data/src/data/list_and_v1.rs.data index 1d60e0085fcbb..e89e10d20de7b 100644 --- a/compiler/rustc_baked_icu_data/src/data/list_and_v1.rs.data +++ b/compiler/rustc_baked_icu_data/src/data/list_and_v1.rs.data @@ -19,17 +19,17 @@ #[macro_export] macro_rules! __impl_list_and_v1 { ($ provider : ty) => { - #[clippy::msrv = "1.82"] + #[clippy::msrv = "1.86"] const _: () = <$provider>::MUST_USE_MAKE_PROVIDER_MACRO; - #[clippy::msrv = "1.82"] + #[clippy::msrv = "1.86"] impl $provider { const DATA_LIST_AND_V1: icu_provider::baked::zerotrie::Data = { const TRIE: icu_provider::baked::zerotrie::ZeroTrieSimpleAscii<&'static [u8]> = icu_provider::baked::zerotrie::ZeroTrieSimpleAscii { store: b"\xC8efijprtz\x18#.9DOZ\xC2ns\n\x1E\xC3NSW\x01\x02\x80\x85\x8A\x1E\xC3NSW\x01\x02\x81\x81\x81r\x1E\xC3NSW\x01\x02\x80\x86\x86t\x1E\xC3NSW\x01\x02\x82\x82\x82a\x1E\xC3NSW\x01\x02\x83\x83\x83t\x1E\xC3NSW\x01\x02\x80\x82\x82u\x1E\xC3NSW\x01\x02\x80\x87\x87r\x1E\xC3NSW\x01\x02\x80\x88\x88h\xC2\x1E-\t\xC3NSW\x01\x02\x83\x89\x89Han\xC2st\n\x1E\xC3NSW\x01\x02\x83\x89\x89\x1E\xC3NSW\x01\x02\x84\x89\x89" }; - const VALUES: &'static [::DataStruct] = &[icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" y ") }, 3u8), special_case: Some(icu_list::provider::SpecialCasePattern { condition: unsafe { icu_list::provider::SerdeDFA::from_dfa_bytes_unchecked(if cfg!(target_endian = "little") { b"rust-regex-automata-dfa-sparse\0\0\xFF\xFE\0\0\x02\0\0\0\0\0\0\0\x02\0\0\0\x0E\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x02\x02\x03\x04\x04\x05\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\t\t\t\n\x0B\x0B\x0C\r\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12\x12\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x14\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x16\x17\x17\x18\x19\x19\x19\x1A\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B(\x01\0\0\x01\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\x01\x80\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\x05\0\x05\x05\x06\x06\x0C\x0C\r\r\0\0S\0\0\0D\0\0\0S\0\0\0D\0\0\0\0\0\0\0\0\x02\0\0\x1B\0\0\x12\0\0\0\x12\0\0\0\0\x03\0\x06\x06\r\r\0\0h\0\0\0h\0\0\0\0\0\0\0\0\x0E\0\0\0\x02\x02\x04\x07\t\t\x0B\x0E\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17\x18\x18\x19\x19\x1A\x1A\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0\xBF\0\0\0\xCE\0\0\0\xDD\0\0\0\xEC\0\0\0\xDD\0\0\0\xFB\0\0\0\n\x01\0\0\x19\x01\0\0\x12\0\0\0\0\x02\0\x0F\x11\0\0D\0\0\0\0\0\0\0\0\x02\0\x11\x11\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x0F\x11\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x0F\x10\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x10\x11\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\x0F\x11\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\x0F\x0F\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\x01\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF`\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0\x12\0\0\0\t\0\0\0\x12\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" } else { b"rust-regex-automata-dfa-sparse\0\0\0\0\xFE\xFF\0\0\0\x02\0\0\0\0\0\0\0\x02\0\0\0\x0E\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x02\x02\x03\x04\x04\x05\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\t\t\t\n\x0B\x0B\x0C\r\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12\x12\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x14\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x16\x17\x17\x18\x19\x19\x19\x1A\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\0\0\x01(\0\x01\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\x05\x05\x05\x06\x06\x0C\x0C\r\r\0\0\0\0\0S\0\0\0D\0\0\0S\0\0\0D\0\0\0\0\0\0\x02\0\x1B\0\0\0\0\0\x12\0\0\0\x12\0\0\x03\x06\x06\r\r\0\0\0\0\0h\0\0\0h\0\0\0\0\0\0\x0E\0\0\x02\x02\x04\x07\t\t\x0B\x0E\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17\x18\x18\x19\x19\x1A\x1A\0\0\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0\xBF\0\0\0\xCE\0\0\0\xDD\0\0\0\xEC\0\0\0\xDD\0\0\0\xFB\0\0\x01\n\0\0\x01\x19\0\0\0\x12\0\0\x02\x0F\x11\0\0\0\0\0D\0\0\0\0\0\0\x02\x11\x11\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x0F\x11\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x0F\x10\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x10\x11\0\0\0\0\0\xDD\0\0\0\0\0\0\x02\x0F\x11\0\0\0\0\0\xDD\0\0\0\0\0\0\x02\x0F\x0F\0\0\0\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\x01\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0`\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0\x12\0\0\0\t\0\0\0\x12\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }) }, pattern: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" e ") }, 3u8) }) }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" e ") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE5\x92\x8C") }, 3u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), special_case: None }) }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", & ") }, 4u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" & ") }, 3u8), special_case: None }) }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" et ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" \xD0\xB8 ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" ve ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE5\x92\x8C") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", and ") }, 6u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" and ") }, 5u8), special_case: None }) }]; + const VALUES: &'static [::DataStruct] = &[icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" y ") }, 3u8), special_case: Some(icu_list::provider::SpecialCasePattern { condition: unsafe { icu_list::provider::SerdeDFA::from_dfa_bytes_unchecked(if cfg!(target_endian = "little") { b"rust-regex-automata-dfa-sparse\0\0\xFF\xFE\0\0\x02\0\0\0\0\0\0\0\x02\0\0\0\x0E\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x02\x02\x03\x04\x04\x05\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\t\t\t\n\x0B\x0B\x0C\r\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12\x12\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x14\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x16\x17\x17\x18\x19\x19\x19\x1A\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B(\x01\0\0\x01\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\x01\x80\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\x05\0\x05\x05\x06\x06\x0C\x0C\r\r\0\0S\0\0\0D\0\0\0S\0\0\0D\0\0\0\0\0\0\0\0\x02\0\0\x1B\0\0\x12\0\0\0\x12\0\0\0\0\x03\0\x06\x06\r\r\0\0h\0\0\0h\0\0\0\0\0\0\0\0\x0E\0\0\0\x02\x02\x04\x07\t\t\x0B\x0E\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17\x18\x18\x19\x19\x1A\x1A\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0\xBF\0\0\0\xCE\0\0\0\xDD\0\0\0\xEC\0\0\0\xDD\0\0\0\xFB\0\0\0\n\x01\0\0\x19\x01\0\0\x12\0\0\0\0\x02\0\x0F\x11\0\0D\0\0\0\0\0\0\0\0\x02\0\x11\x11\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x0F\x11\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x0F\x10\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x10\x11\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\x0F\x11\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\x0F\x0F\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\x01\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF#\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0\x12\0\0\0\t\0\0\0\x12\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" } else { b"rust-regex-automata-dfa-sparse\0\0\0\0\xFE\xFF\0\0\0\x02\0\0\0\0\0\0\0\x02\0\0\0\x0E\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x02\x02\x03\x04\x04\x05\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\t\t\t\n\x0B\x0B\x0C\r\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12\x12\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x14\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x16\x17\x17\x18\x19\x19\x19\x1A\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\0\0\x01(\0\x01\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\x05\x05\x05\x06\x06\x0C\x0C\r\r\0\0\0\0\0S\0\0\0D\0\0\0S\0\0\0D\0\0\0\0\0\0\x02\0\x1B\0\0\0\0\0\x12\0\0\0\x12\0\0\x03\x06\x06\r\r\0\0\0\0\0h\0\0\0h\0\0\0\0\0\0\x0E\0\0\x02\x02\x04\x07\t\t\x0B\x0E\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17\x18\x18\x19\x19\x1A\x1A\0\0\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0\xBF\0\0\0\xCE\0\0\0\xDD\0\0\0\xEC\0\0\0\xDD\0\0\0\xFB\0\0\x01\n\0\0\x01\x19\0\0\0\x12\0\0\x02\x0F\x11\0\0\0\0\0D\0\0\0\0\0\0\x02\x11\x11\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x0F\x11\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x0F\x10\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x10\x11\0\0\0\0\0\xDD\0\0\0\0\0\0\x02\x0F\x11\0\0\0\0\0\xDD\0\0\0\0\0\0\x02\x0F\x0F\0\0\0\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\x01\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0#\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0\x12\0\0\0\t\0\0\0\x12\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }) }, pattern: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" e ") }, 3u8) }) }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" e ") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE5\x92\x8C") }, 3u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), special_case: None }) }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", & ") }, 4u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" & ") }, 3u8), special_case: None }) }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" et ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" \xD0\xB8 ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" ve ") }, 4u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, 3u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE3\x80\x81") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b"\xE5\x92\x8C") }, 3u8), special_case: None }, pair: None }, icu_list::provider::ListFormatterPatterns { start: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, 2u8), middle: unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", ") }, end: icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b", and ") }, 6u8), special_case: None }, pair: Some(icu_list::provider::ConditionalListJoinerPattern { default: icu_list::provider::ListJoinerPattern::from_parts(unsafe { zerovec::VarZeroCow::from_bytes_unchecked(b" and ") }, 5u8), special_case: None }) }]; unsafe { icu_provider::baked::zerotrie::Data::from_trie_and_values_unchecked(TRIE, VALUES) } }; } - #[clippy::msrv = "1.82"] + #[clippy::msrv = "1.86"] impl icu_provider::DataProvider for $provider { fn load(&self, req: icu_provider::DataRequest) -> Result, icu_provider::DataError> { let mut metadata = icu_provider::DataResponseMetadata::default(); @@ -55,7 +55,7 @@ macro_rules! __impl_list_and_v1 { }; ($ provider : ty , ITER) => { __impl_list_and_v1!($provider); - #[clippy::msrv = "1.82"] + #[clippy::msrv = "1.86"] impl icu_provider::IterableDataProvider for $provider { fn iter_ids(&self) -> Result>, icu_provider::DataError> { Ok(icu_provider::baked::DataStore::iter(&Self::DATA_LIST_AND_V1).collect()) diff --git a/compiler/rustc_baked_icu_data/src/data/mod.rs b/compiler/rustc_baked_icu_data/src/data/mod.rs index 3146188a8e7af..fd43456b9e619 100644 --- a/compiler/rustc_baked_icu_data/src/data/mod.rs +++ b/compiler/rustc_baked_icu_data/src/data/mod.rs @@ -15,7 +15,7 @@ include!("list_and_v1.rs.data"); #[macro_export] macro_rules! __make_provider { ($ name : ty) => { - #[clippy::msrv = "1.82"] + #[clippy::msrv = "1.86"] impl $name { #[allow(dead_code)] pub(crate) const MUST_USE_MAKE_PROVIDER_MACRO: () = (); diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 79b6fc59978f3..3d3906637b8b1 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -301,12 +301,12 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { } Mir => { let mut out = Vec::new(); - write_mir_pretty(ex.tcx(), None, &mut out).unwrap(); + write_mir_pretty(ex.tcx(), &mut out).unwrap(); String::from_utf8(out).unwrap() } MirCFG => { let mut out = Vec::new(); - write_mir_graphviz(ex.tcx(), None, &mut out).unwrap(); + write_mir_graphviz(ex.tcx(), &mut out).unwrap(); String::from_utf8(out).unwrap() } StableMir => { diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index 687aff5e9229a..50f0b265527fe 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -6,8 +6,8 @@ edition = "2024" [dependencies] # tidy-alphabetical-start fluent-bundle = "0.16" -icu_list = { version = "2.0", default-features = false, features = ["alloc"] } -icu_locale = { version = "2.0", default-features = false } +icu_list = { version = "2.2", default-features = false, features = ["alloc"] } +icu_locale = { version = "2.2", default-features = false } intl-memoizer = "0.5.1" rustc_baked_icu_data = { path = "../rustc_baked_icu_data" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_middle/src/mir/graphviz.rs b/compiler/rustc_middle/src/mir/graphviz.rs index 30a781b025fd5..4002d0ebd392d 100644 --- a/compiler/rustc_middle/src/mir/graphviz.rs +++ b/compiler/rustc_middle/src/mir/graphviz.rs @@ -4,24 +4,22 @@ use gsgdt::GraphvizSettings; use rustc_graphviz as dot; use super::generic_graph::mir_fn_to_generic_graph; -use super::pretty::dump_mir_def_ids; use crate::mir::*; /// Write a graphviz DOT graph of a list of MIRs. -pub fn write_mir_graphviz(tcx: TyCtxt<'_>, single: Option, w: &mut W) -> io::Result<()> +pub fn write_mir_graphviz(tcx: TyCtxt<'_>, w: &mut W) -> io::Result<()> where W: Write, { - let def_ids = dump_mir_def_ids(tcx, single); - - let mirs = def_ids + let mirs = tcx + .mir_keys(()) .iter() .filter(|&&def_id| !tcx.is_trivial_const(def_id)) .flat_map(|&def_id| { if tcx.is_const_fn(def_id) { vec![tcx.optimized_mir(def_id), tcx.mir_for_ctfe(def_id)] } else { - vec![tcx.instance_mir(ty::InstanceKind::Item(def_id))] + vec![tcx.instance_mir(ty::InstanceKind::Item(def_id.to_def_id()))] } }) .collect::>(); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index e1bc29d5c3684..70ad7fc1c3d6c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -312,13 +312,9 @@ impl<'a, 'tcx> MirDumper<'a, 'tcx> { /////////////////////////////////////////////////////////////////////////// // Whole MIR bodies -/// Write out a human-readable textual representation for the given MIR, with the default -/// [PrettyPrintMirOptions]. -pub fn write_mir_pretty<'tcx>( - tcx: TyCtxt<'tcx>, - single: Option, - w: &mut dyn io::Write, -) -> io::Result<()> { +/// Write out a human-readable textual representation of this crate's MIR, +/// with the default [`PrettyPrintMirOptions`]. +pub fn write_mir_pretty<'tcx>(tcx: TyCtxt<'tcx>, w: &mut dyn io::Write) -> io::Result<()> { let writer = MirWriter::new(tcx); writeln!(w, "// WARNING: This output format is intended for human consumers only")?; @@ -326,7 +322,7 @@ pub fn write_mir_pretty<'tcx>( writeln!(w, "// HINT: See also -Z dump-mir for MIR at specific points during compilation.")?; let mut first = true; - for def_id in dump_mir_def_ids(tcx, single) { + for &def_id in tcx.mir_keys(()) { if first { first = false; } else { @@ -360,7 +356,7 @@ pub fn write_mir_pretty<'tcx>( } writeln!(w, ": {} = const {};", ty, Const::Val(val, ty))?; } else { - let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id)); + let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id.to_def_id())); render_body(w, instance_mir)?; } } @@ -698,14 +694,6 @@ fn write_user_type_annotations( Ok(()) } -pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option) -> Vec { - if let Some(i) = single { - vec![i] - } else { - tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect() - } -} - /////////////////////////////////////////////////////////////////////////// // Basic blocks and their parts (statements, terminators, ...) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 701e1102f33f1..93233efae6650 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2073,7 +2073,8 @@ impl<'tcx> TyCtxt<'tcx> { /// /// Even if this returns `true`, constness may still be unstable! #[inline] - pub fn is_const_fn(self, def_id: DefId) -> bool { + pub fn is_const_fn(self, def_id: impl IntoQueryKey) -> bool { + let def_id = def_id.into_query_key(); matches!( self.def_kind(def_id), DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Closure diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index e4fcbaa483d04..50d4e9894e74a 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -25,11 +25,11 @@ pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> { match tcx.output_filenames(()).path(OutputType::Mir) { OutFileName::Stdout => { let mut f = io::stdout(); - write_mir_pretty(tcx, None, &mut f)?; + write_mir_pretty(tcx, &mut f)?; } OutFileName::Real(path) => { let mut f = File::create_buffered(&path)?; - write_mir_pretty(tcx, None, &mut f)?; + write_mir_pretty(tcx, &mut f)?; if tcx.sess.opts.json_artifact_notifications { tcx.dcx().emit_artifact_notification(&path, "mir"); } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 61da931df4d5d..4fb274188a8cf 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -253,7 +253,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match vis.kind { ast::VisibilityKind::Public => Ok(Visibility::Public), ast::VisibilityKind::Inherited => { - Ok(match parent_scope.module.kind { + Ok(match parent_scope.module.expect_local().kind { // Any inherited visibility resolved directly inside an enum or trait // (i.e. variants, fields, and trait items) inherits from the visibility // of the enum or trait. @@ -535,7 +535,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { root_id: NodeId, vis: Visibility, ) { - let current_module = self.parent_scope.module; + let current_module = self.parent_scope.module.expect_local(); let import = self.r.arenas.alloc_import(ImportData { kind, parent_scope: self.parent_scope, @@ -560,7 +560,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { if target.name != kw::Underscore { self.r.per_ns(|this, ns| { let key = BindingKey::new(IdentKey::new(target), ns); - this.resolution_or_default(current_module, key, target.span) + this.resolution_or_default(current_module.to_module(), key, target.span) .borrow_mut(this) .single_imports .insert(import); @@ -1136,7 +1136,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { if let Some(Attribute::Parsed(AttributeKind::MacroUse { span, arguments })) = AttributeParser::parse_limited(self.r.tcx.sess, &item.attrs, &[sym::macro_use]) { - if self.parent_scope.module.parent.is_some() { + if self.parent_scope.module.expect_local().parent.is_some() { self.r .dcx() .emit_err(errors::ExternCrateLoadingMacroNotAtCrateRoot { span: item.span }); @@ -1247,7 +1247,8 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { /// directly into its parent scope's module. pub(crate) fn visit_invoc_in_module(&mut self, id: NodeId) -> MacroRulesScopeRef<'ra> { let invoc_id = self.visit_invoc(id); - self.parent_scope.module.unexpanded_invocations.borrow_mut(self.r).insert(invoc_id); + let module = self.parent_scope.module.expect_local(); + module.unexpanded_invocations.borrow_mut(self.r).insert(invoc_id); self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id)) } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 5e52ebcc3f194..4ba6106bf09e6 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -240,7 +240,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return self.report_conflict(ident, ns, new_binding, old_binding); } - let container = match old_binding.parent_module.unwrap().kind { + let container = match old_binding.parent_module.unwrap().expect_local().kind { // Avoid using TyCtxt::def_kind_descr in the resolver, because it // indirectly *calls* the resolver, and would cause a query cycle. ModuleKind::Def(kind, def_id, _, _) => kind.descr(def_id), @@ -2597,7 +2597,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let module = module.to_module(); current_module.is_ancestor_of(module) && current_module != module }) - .flat_map(|(_, module)| module.kind.name()), + .flat_map(|(_, module)| module.name()), ) .chain( self.extern_module_map @@ -2607,7 +2607,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let module = module.to_module(); current_module.is_ancestor_of(module) && current_module != module }) - .flat_map(|(_, module)| module.kind.name()), + .flat_map(|(_, module)| module.name()), ) .filter(|c| !c.to_string().is_empty()) .collect::>(); @@ -2631,8 +2631,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> (String, String, Option) { let is_last = failed_segment_idx == path.len() - 1; let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; - let module_res = match module { - Some(ModuleOrUniformRoot::Module(module)) => module.res(), + let module_def_id = match module { + Some(ModuleOrUniformRoot::Module(module)) => module.opt_def_id(), _ => None, }; let scope = match &path[..failed_segment_idx] { @@ -2647,7 +2647,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let message = format!("cannot find `{ident}` in {scope}"); - if module_res == self.graph_root.res() { + if module_def_id == Some(CRATE_DEF_ID.to_def_id()) { let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); candidates @@ -3145,7 +3145,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if !kinds.contains(MacroKinds::BANG) { return None; } - let module_name = crate_module.kind.name().unwrap_or(kw::Crate); + let module_name = crate_module.name().unwrap_or(kw::Crate); let import_snippet = match import.kind { ImportKind::Single { source, target, .. } if source != target => { format!("{source} as {target}") @@ -3290,20 +3290,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return cached; } visited.insert(parent_module, false); - let m = r.expect_module(parent_module); let mut res = false; - for importer in m.glob_importers.borrow().iter() { - if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() { - if next_parent_module == module - || comes_from_same_module_for_glob( - r, - next_parent_module, - module, - visited, - ) + let m = r.expect_module(parent_module); + if m.is_local() { + for importer in m.glob_importers.borrow().iter() { + if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() { - res = true; - break; + if next_parent_module == module + || comes_from_same_module_for_glob( + r, + next_parent_module, + module, + visited, + ) + { + res = true; + break; + } } } } diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index b33b77d43999d..49461651412b2 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -643,10 +643,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Err(ControlFlow::Break(..)) => return decl, } } - Scope::ModuleGlobs(module, _) - if let ModuleKind::Def(_, def_id, _, _) = module.kind - && !def_id.is_local() => - { + Scope::ModuleGlobs(module, _) if !module.is_local() => { // Fast path: external module decoding only creates non-glob declarations. Err(Determined) } @@ -699,9 +696,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Scope::MacroUsePrelude => match self.macro_use_prelude.get(&ident.name).cloned() { Some(decl) => Ok(decl), - None => Err(Determinacy::determined( - self.graph_root.unexpanded_invocations.borrow().is_empty(), - )), + None => Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations())), }, Scope::BuiltinAttrs => match self.builtin_attr_decls.get(&ident.name) { Some(decl) => Ok(*decl), @@ -714,9 +709,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { finalize.is_some(), ) { Some(decl) => Ok(decl), - None => Err(Determinacy::determined( - self.graph_root.unexpanded_invocations.borrow().is_empty(), - )), + None => { + Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations())) + } } } Scope::ExternPreludeFlags => { @@ -1126,6 +1121,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) }; } + // In extern modules everything is determined from the start. + if !module.is_local() { + return Err(ControlFlow::Continue(Determined)); + }; + // Check if one of single imports can still define the name, block if it can. if self.reborrow().single_import_can_define_name( &resolution, @@ -1139,7 +1139,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Check if one of unexpanded macros can still define the name. - if !module.unexpanded_invocations.borrow().is_empty() { + if module.has_unexpanded_invocations() { return Err(ControlFlow::Continue(Undetermined)); } @@ -1223,7 +1223,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // scopes we return `Undetermined` with `ControlFlow::Continue`. // Check if one of unexpanded macros can still define the name, // if it can then our "no resolution" result is not determined and can be invalidated. - if !module.unexpanded_invocations.borrow().is_empty() { + if module.has_unexpanded_invocations() { return Err(ControlFlow::Continue(Undetermined)); } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index b16347af7ba0c..3b948f2bed194 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1790,7 +1790,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Add to module's glob_importers - module.glob_importers.borrow_mut_unchecked().push(import); + if module.is_local() { + module.glob_importers.borrow_mut_unchecked().push(import); + } // Ensure that `resolutions` isn't borrowed during `try_define`, // since it might get updated via a glob cycle. diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 3337a4626b040..79c95fe713ed6 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1477,7 +1477,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // During late resolution we only track the module component of the parent scope, // although it may be useful to track other components as well for diagnostics. let graph_root = resolver.graph_root; - let parent_scope = ParentScope::module(graph_root.to_module(), resolver.arenas); + let parent_scope = ParentScope::module(graph_root, resolver.arenas); let start_rib_kind = RibKind::Module(graph_root); LateResolutionVisitor { r: resolver, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6f86759d46c5f..d7ce4f731e21b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -38,8 +38,8 @@ use crate::late::{ }; use crate::ty::fast_reject::SimplifiedType; use crate::{ - Finalize, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Res, - Resolver, ScopeSet, Segment, errors, path_names_to_string, + Finalize, Module, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Res, Resolver, + ScopeSet, Segment, errors, path_names_to_string, }; /// A field or associated item from self type suggested in case of resolution failure. @@ -1698,7 +1698,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { && let TyKind::Path(_, self_ty_path) = &self_ty.kind && let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None, source) - && let ModuleKind::Def(DefKind::Trait, ..) = module.kind + && module.def_kind() == Some(DefKind::Trait) && trait_ref.path.span == span && let PathSource::Trait(_) = source && let Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) = res @@ -3014,12 +3014,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { fn find_module(&self, def_id: DefId) -> Option<(Module<'ra>, ImportSuggestion)> { let mut result = None; let mut seen_modules = FxHashSet::default(); - let root_did = self.r.graph_root.def_id(); - let mut worklist = vec![( - self.r.graph_root.to_module(), - ThinVec::new(), - root_did.is_local() || !self.r.tcx.is_doc_hidden(root_did), - )]; + let mut worklist = vec![(self.r.graph_root.to_module(), ThinVec::new(), true)]; while let Some((in_module, path_segments, doc_visible)) = worklist.pop() { // abort if the module is already found diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 879a4dd3983a6..44a72fec56b95 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -61,7 +61,6 @@ use rustc_hir::definitions::{PerParentDisambiguatorState, PerParentDisambiguator use rustc_hir::{PrimTy, TraitCandidate, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_metadata::creader::CStore; -use rustc_middle::bug; use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::query::Providers; @@ -69,6 +68,7 @@ use rustc_middle::ty::{ self, DelegationInfo, MainDefinition, RegisteredTools, ResolverAstLowering, ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility, }; +use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency}; @@ -172,9 +172,9 @@ struct ParentScope<'ra> { impl<'ra> ParentScope<'ra> { /// Creates a parent scope with the passed argument used as the module scope component, /// and other scope components set to default empty values. - fn module(module: Module<'ra>, arenas: &'ra ResolverArenas<'ra>) -> ParentScope<'ra> { + fn module(module: LocalModule<'ra>, arenas: &'ra ResolverArenas<'ra>) -> ParentScope<'ra> { ParentScope { - module, + module: module.to_module(), expansion: LocalExpnId::ROOT, macro_rules: arenas.alloc_macro_rules_scope(MacroRulesScope::Empty), derives: &[], @@ -547,20 +547,23 @@ enum ModuleKind { } impl ModuleKind { - /// Get name of the module. - fn name(&self) -> Option { - match *self { - ModuleKind::Block => None, - ModuleKind::Def(.., name) => name, - } - } - fn opt_def_id(&self) -> Option { match self { ModuleKind::Def(_, def_id, _, _) => Some(*def_id), _ => None, } } + + fn def_id(&self) -> DefId { + self.opt_def_id().expect("`Module::def_id` is called on a block module") + } + + fn is_local(&self) -> bool { + match self { + ModuleKind::Def(_, def_id, ..) => def_id.is_local(), + ModuleKind::Block => true, + } + } } /// Combination of a symbol and its macros 2.0 normalized hygiene context. @@ -723,11 +726,16 @@ impl<'ra> ModuleData<'ra> { expansion: ExpnId, span: Span, no_implicit_prelude: bool, - self_decl: Option>, + vis: Visibility, + arenas: &'ra ResolverArenas<'ra>, ) -> Self { - let is_foreign = match kind { - ModuleKind::Def(_, def_id, _, _) => !def_id.is_local(), - ModuleKind::Block => false, + let is_foreign = !kind.is_local(); + let self_decl = match kind { + ModuleKind::Def(def_kind, def_id, ..) => { + let expn_id = expansion.as_local().unwrap_or(LocalExpnId::ROOT); + Some(arenas.new_def_decl(Res::Def(def_kind, def_id), vis, span, expn_id, parent)) + } + ModuleKind::Block => None, }; ModuleData { parent, @@ -746,12 +754,28 @@ impl<'ra> ModuleData<'ra> { } } + /// Get name of the module. + fn name(&self) -> Option { + match self.kind { + ModuleKind::Block => None, + ModuleKind::Def(.., name) => name, + } + } + fn opt_def_id(&self) -> Option { self.kind.opt_def_id() } fn def_id(&self) -> DefId { - self.opt_def_id().expect("`ModuleData::def_id` is called on a block module") + self.kind.def_id() + } + + fn is_local(&self) -> bool { + self.kind.is_local() + } + + fn has_unexpanded_invocations(&self) -> bool { + !self.unexpanded_invocations.borrow().is_empty() } fn res(&self) -> Option { @@ -760,6 +784,13 @@ impl<'ra> ModuleData<'ra> { _ => None, } } + + fn def_kind(&self) -> Option { + match self.kind { + ModuleKind::Def(def_kind, ..) => Some(def_kind), + ModuleKind::Block => None, + } + } } impl<'ra> Module<'ra> { @@ -813,16 +844,16 @@ impl<'ra> Module<'ra> { // `self` resolves to the first module ancestor that `is_normal`. fn is_normal(self) -> bool { - matches!(self.kind, ModuleKind::Def(DefKind::Mod, _, _, _)) + self.def_kind() == Some(DefKind::Mod) } fn is_trait(self) -> bool { - matches!(self.kind, ModuleKind::Def(DefKind::Trait, _, _, _)) + matches!(self.def_kind(), Some(DefKind::Trait)) } fn nearest_item_scope(self) -> Module<'ra> { - match self.kind { - ModuleKind::Def(DefKind::Enum | DefKind::Trait, ..) => { + match self.def_kind() { + Some(DefKind::Enum | DefKind::Trait) => { self.parent.expect("enum or trait module without a parent") } _ => self, @@ -862,7 +893,7 @@ impl<'ra> Module<'ra> { fn expect_local(self) -> LocalModule<'ra> { match self.kind { ModuleKind::Def(_, def_id, _, _) if !def_id.is_local() => { - panic!("`Module::expect_local` is called on a non-local module: {self:?}") + span_bug!(self.span, "unexpected extern module: {self:?}") } ModuleKind::Def(..) | ModuleKind::Block => LocalModule(self.0), } @@ -873,19 +904,49 @@ impl<'ra> Module<'ra> { match self.kind { ModuleKind::Def(_, def_id, _, _) if !def_id.is_local() => ExternModule(self.0), ModuleKind::Def(..) | ModuleKind::Block => { - panic!("`Module::expect_extern` is called on a local module: {self:?}") + span_bug!(self.span, "unexpected local module: {self:?}") } } } } impl<'ra> LocalModule<'ra> { + fn new( + parent: Option>, + kind: ModuleKind, + vis: Visibility, + expn_id: ExpnId, + span: Span, + no_implicit_prelude: bool, + arenas: &'ra ResolverArenas<'ra>, + ) -> LocalModule<'ra> { + assert!(kind.is_local()); + let parent = parent.map(|m| m.to_module()); + let data = ModuleData::new(parent, kind, expn_id, span, no_implicit_prelude, vis, arenas); + LocalModule(Interned::new_unchecked(arenas.modules.alloc(data))) + } + fn to_module(self) -> Module<'ra> { Module(self.0) } } impl<'ra> ExternModule<'ra> { + fn new( + parent: Option>, + kind: ModuleKind, + vis: Visibility, + expn_id: ExpnId, + span: Span, + no_implicit_prelude: bool, + arenas: &'ra ResolverArenas<'ra>, + ) -> ExternModule<'ra> { + assert!(!kind.is_local()); + let parent = parent.map(|m| m.to_module()); + let data = ModuleData::new(parent, kind, expn_id, span, no_implicit_prelude, vis, arenas); + ExternModule(Interned::new_unchecked(arenas.modules.alloc(data))) + } + fn to_module(self) -> Module<'ra> { Module(self.0) } @@ -917,9 +978,9 @@ impl<'ra> std::ops::Deref for ExternModule<'ra> { impl<'ra> fmt::Debug for Module<'ra> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - ModuleKind::Block => write!(f, "block"), - ModuleKind::Def(..) => write!(f, "{:?}", self.res()), + match self.res() { + None => write!(f, "block"), + Some(res) => write!(f, "{:?}", res), } } } @@ -1208,8 +1269,7 @@ impl<'ra> DeclData<'ra> { fn determined(&self) -> bool { match &self.kind { DeclKind::Import { source_decl, import, .. } if import.is_glob() => { - import.parent_scope.module.unexpanded_invocations.borrow().is_empty() - && source_decl.determined() + !import.parent_scope.module.has_unexpanded_invocations() && source_decl.determined() } _ => true, } @@ -1538,31 +1598,6 @@ impl<'ra> ResolverArenas<'ra> { self.new_def_decl(res, Visibility::Public, span, expn_id, None) } - fn new_module( - &'ra self, - parent: Option>, - kind: ModuleKind, - vis: Visibility, - expn_id: ExpnId, - span: Span, - no_implicit_prelude: bool, - ) -> Module<'ra> { - let self_decl = match kind { - ModuleKind::Def(def_kind, def_id, _, _) => { - let expn_id = expn_id.as_local().unwrap_or(LocalExpnId::ROOT); - Some(self.new_def_decl(Res::Def(def_kind, def_id), vis, span, expn_id, parent)) - } - ModuleKind::Block => None, - }; - Module(Interned::new_unchecked(self.modules.alloc(ModuleData::new( - parent, - kind, - expn_id, - span, - no_implicit_prelude, - self_decl, - )))) - } fn alloc_decl(&'ra self, data: DeclData<'ra>) -> Decl<'ra> { Interned::new_unchecked(self.dropless.alloc(data)) } @@ -1724,26 +1759,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { arenas: &'ra ResolverArenas<'ra>, ) -> Resolver<'ra, 'tcx> { let root_def_id = CRATE_DEF_ID.to_def_id(); - let graph_root = arenas.new_module( + let graph_root = LocalModule::new( None, ModuleKind::Def(DefKind::Mod, root_def_id, CRATE_NODE_ID, None), Visibility::Public, ExpnId::root(), crate_span, attr::contains_name(attrs, sym::no_implicit_prelude), + arenas, ); - let graph_root = graph_root.expect_local(); let local_modules = vec![graph_root]; let local_module_map = FxIndexMap::from_iter([(CRATE_DEF_ID, graph_root)]); - let empty_module = arenas.new_module( + let empty_module = LocalModule::new( None, ModuleKind::Def(DefKind::Mod, root_def_id, CRATE_NODE_ID, None), Visibility::Public, ExpnId::root(), DUMMY_SP, true, + arenas, ); - let empty_module = empty_module.expect_local(); let mut node_id_to_def_id = NodeMap::default(); let crate_feed = tcx.create_local_crate_def_id(crate_span); @@ -1825,7 +1860,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .. }; - let root_parent_scope = ParentScope::module(graph_root.to_module(), resolver.arenas); + let root_parent_scope = ParentScope::module(graph_root, resolver.arenas); resolver.invocation_parent_scopes.insert(LocalExpnId::ROOT, root_parent_scope); resolver.feed_visibility(crate_feed, Visibility::Public); @@ -1840,13 +1875,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span: Span, no_implicit_prelude: bool, ) -> LocalModule<'ra> { - let parent = parent.map(|m| m.to_module()); let vis = kind.opt_def_id().map_or(Visibility::Public, |def_id| self.tcx.visibility(def_id)); - let module = self - .arenas - .new_module(parent, kind, vis, expn_id, span, no_implicit_prelude) - .expect_local(); + let module = + LocalModule::new(parent, kind, vis, expn_id, span, no_implicit_prelude, self.arenas); self.local_modules.push(module); if let Some(def_id) = module.opt_def_id() { self.local_module_map.insert(def_id.expect_local(), module); @@ -1862,14 +1894,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span: Span, no_implicit_prelude: bool, ) -> ExternModule<'ra> { - let parent = parent.map(|m| m.to_module()); - let vis = - kind.opt_def_id().map_or(Visibility::Public, |def_id| self.tcx.visibility(def_id)); - let module = self - .arenas - .new_module(parent, kind, vis, expn_id, span, no_implicit_prelude) - .expect_extern(); - self.extern_module_map.borrow_mut().insert(module.def_id(), module); + let def_id = kind.def_id(); + let module = ExternModule::new( + parent, + kind, + self.tcx.visibility(def_id), + expn_id, + span, + no_implicit_prelude, + self.arenas, + ); + self.extern_module_map.borrow_mut().insert(def_id, module); module } @@ -2220,7 +2255,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Do not report the lint if the macro name resolves in stdlib prelude // even without the problematic `macro_use` import. let found_in_stdlib_prelude = self.prelude.is_some_and(|prelude| { - let empty_module = self.empty_module.to_module(); + let empty_module = self.empty_module; let arenas = self.arenas; self.cm() .maybe_resolve_ident_in_module( @@ -2341,7 +2376,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { "resolve_crate_root({:?}): got module {:?} ({:?}) (ident.span = {:?})", ident, module, - module.kind.name(), + module.name(), ident.span ); module @@ -2586,12 +2621,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return; } - let module = self.graph_root.to_module(); + let module = self.graph_root; let ident = Ident::with_dummy_span(sym::main); let parent_scope = &ParentScope::module(module, self.arenas); let Ok(name_binding) = self.cm().maybe_resolve_ident_in_module( - ModuleOrUniformRoot::Module(module), + ModuleOrUniformRoot::Module(module.to_module()), ident, ValueNS, parent_scope, @@ -2685,22 +2720,9 @@ fn path_names_to_string(path: &Path) -> String { /// A somewhat inefficient routine to obtain the name of a module. fn module_to_string(mut module: Module<'_>) -> Option { let mut names = Vec::new(); - loop { - if let ModuleKind::Def(.., name) = module.kind { - if let Some(parent) = module.parent { - // `unwrap` is safe: the presence of a parent means it's not the crate root. - names.push(name.unwrap()); - module = parent - } else { - break; - } - } else { - names.push(sym::opaque_module_name_placeholder); - let Some(parent) = module.parent else { - return None; - }; - module = parent; - } + while let Some(parent) = module.parent { + names.push(module.name().unwrap_or(sym::opaque_module_name_placeholder)); + module = parent; } if names.is_empty() { return None; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index f4464138ef1fc..206e32f61b485 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -175,10 +175,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { fn resolve_dollar_crates(&self) { hygiene::update_dollar_crate_names(|ctxt| { let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt)); - match self.resolve_crate_root(ident).kind { - ModuleKind::Def(.., name) if let Some(name) = name => name, - _ => kw::Crate, - } + self.resolve_crate_root(ident).name().unwrap_or(kw::Crate) }); } @@ -193,7 +190,8 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { let output_macro_rules_scope = collect_definitions(self, fragment, parent_scope); self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope); - parent_scope.module.unexpanded_invocations.borrow_mut(self).remove(&expansion); + let module = parent_scope.module.expect_local(); + module.unexpanded_invocations.borrow_mut(self).remove(&expansion); if let Some(unexpanded_invocations) = self.impl_unexpanded_invocations.get_mut(&self.invocation_parent(expansion)) { @@ -523,7 +521,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { star_span: Span, ) -> Result)>, Indeterminate> { let target_trait = self.expect_module(trait_def_id); - if !target_trait.unexpanded_invocations.borrow().is_empty() { + if target_trait.has_unexpanded_invocations() { return Err(Indeterminate); } // FIXME: Instead of waiting try generating all trait methods, and pruning diff --git a/compiler/rustc_target/src/spec/base/cygwin.rs b/compiler/rustc_target/src/spec/base/cygwin.rs index fcc2a732c18de..1e4f17289db14 100644 --- a/compiler/rustc_target/src/spec/base/cygwin.rs +++ b/compiler/rustc_target/src/spec/base/cygwin.rs @@ -36,7 +36,6 @@ pub(crate) fn opts() -> TargetOptions { families: cvs!["unix"], is_like_windows: true, binary_format: BinaryFormat::Coff, - allows_weak_linkage: false, pre_link_args, late_link_args, abi_return_struct_as_int: true, diff --git a/compiler/rustc_target/src/spec/base/uefi_msvc.rs b/compiler/rustc_target/src/spec/base/uefi_msvc.rs index 99005715018a0..cb9424fe33762 100644 --- a/compiler/rustc_target/src/spec/base/uefi_msvc.rs +++ b/compiler/rustc_target/src/spec/base/uefi_msvc.rs @@ -39,7 +39,6 @@ pub(crate) fn opts() -> TargetOptions { linker_flavor: LinkerFlavor::Msvc(Lld::Yes), disable_redzone: true, exe_suffix: ".efi".into(), - allows_weak_linkage: false, panic_strategy: PanicStrategy::Abort, // LLVM does not emit inline assembly because the LLVM target does not get considered as… // "Windows". diff --git a/compiler/rustc_target/src/spec/base/windows_gnu.rs b/compiler/rustc_target/src/spec/base/windows_gnu.rs index cee3f91226998..1a343c737ad45 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnu.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnu.rs @@ -91,7 +91,6 @@ pub(crate) fn opts() -> TargetOptions { families: cvs!["windows"], is_like_windows: true, binary_format: BinaryFormat::Coff, - allows_weak_linkage: false, pre_link_args, pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(), link_self_contained: LinkSelfContainedDefault::InferredForMingw, diff --git a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs index c1b4eecae3f5c..5db5fb95924c5 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs @@ -36,7 +36,6 @@ pub(crate) fn opts() -> TargetOptions { families: cvs!["windows"], is_like_windows: true, binary_format: BinaryFormat::Coff, - allows_weak_linkage: false, pre_link_args, pre_link_objects_self_contained: pre_mingw_self_contained(), link_self_contained: LinkSelfContainedDefault::InferredForMingw, diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 8448c2ab51b3d..c8a22205a5f3c 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -153,7 +153,6 @@ impl Target { forward!(is_like_vexos); forward!(binary_format); forward!(default_dwarf_version); - forward!(allows_weak_linkage); forward!(has_rpath); forward!(no_default_libraries); forward!(position_independent_executables); @@ -352,7 +351,6 @@ impl ToJson for Target { target_option_val!(is_like_vexos); target_option_val!(binary_format); target_option_val!(default_dwarf_version); - target_option_val!(allows_weak_linkage); target_option_val!(has_rpath); target_option_val!(no_default_libraries); target_option_val!(position_independent_executables); @@ -575,7 +573,6 @@ struct TargetSpecJson { is_like_vexos: Option, binary_format: Option, default_dwarf_version: Option, - allows_weak_linkage: Option, has_rpath: Option, no_default_libraries: Option, position_independent_executables: Option, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index b1a9c4b3cde6a..d6a9e27c46553 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2485,13 +2485,6 @@ pub struct TargetOptions { /// Default supported version of DWARF on this platform. /// Useful because some platforms (osx, bsd) only want up to DWARF2. pub default_dwarf_version: u32, - /// The MinGW toolchain has a known issue that prevents it from correctly - /// handling COFF object files with more than 215 sections. Since each weak - /// symbol needs its own COMDAT section, weak linkage implies a large - /// number sections that easily exceeds the given limit for larger - /// codebases. Consequently we want a way to disallow weak linkage on some - /// platforms. - pub allows_weak_linkage: bool, /// Whether the linker support rpaths or not. Defaults to false. pub has_rpath: bool, /// Whether to disable linking to the default libraries, typically corresponds @@ -2878,7 +2871,6 @@ impl Default for TargetOptions { is_like_vexos: false, binary_format: BinaryFormat::Elf, default_dwarf_version: 4, - allows_weak_linkage: true, has_rpath: false, no_default_libraries: true, position_independent_executables: false, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 890f5ab2286c6..fdd4831336179 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -460,6 +460,7 @@ impl i128 { midpoint_impl! { i128, signed } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "16")] impl isize { int_impl! { @@ -485,6 +486,7 @@ impl isize { midpoint_impl! { isize, i32, signed } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "32")] impl isize { int_impl! { @@ -510,6 +512,7 @@ impl isize { midpoint_impl! { isize, i64, signed } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "64")] impl isize { int_impl! { @@ -1334,6 +1337,7 @@ impl u128 { carrying_carryless_mul_impl! { u128, u256 } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "16")] impl usize { uint_impl! { @@ -1365,6 +1369,7 @@ impl usize { carrying_carryless_mul_impl! { usize, u32 } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "32")] impl usize { uint_impl! { @@ -1396,6 +1401,7 @@ impl usize { carrying_carryless_mul_impl! { usize, u64 } } +#[doc(auto_cfg = false)] #[cfg(target_pointer_width = "64")] impl usize { uint_impl! { diff --git a/library/coretests/tests/net/ip_addr.rs b/library/coretests/tests/net/ip_addr.rs index 3fec59d67b748..ea78d7c58e9e4 100644 --- a/library/coretests/tests/net/ip_addr.rs +++ b/library/coretests/tests/net/ip_addr.rs @@ -218,18 +218,15 @@ fn ipv6_to_ipv4() { #[test] fn ip_properties() { - macro_rules! ip { - ($s:expr) => { - IpAddr::from_str($s).unwrap() - }; - } - macro_rules! check { ($s:expr) => { check!($s, 0); }; ($s:expr, $mask:expr) => {{ + let ip = IpAddr::from_str($s).unwrap(); + assert_eq!($s, ip.to_string()); + let unspec: u8 = 1 << 0; let loopback: u8 = 1 << 1; let global: u8 = 1 << 2; @@ -238,39 +235,39 @@ fn ip_properties() { let benchmarking: u8 = 1 << 5; if ($mask & unspec) == unspec { - assert!(ip!($s).is_unspecified()); + assert!(ip.is_unspecified()); } else { - assert!(!ip!($s).is_unspecified()); + assert!(!ip.is_unspecified()); } if ($mask & loopback) == loopback { - assert!(ip!($s).is_loopback()); + assert!(ip.is_loopback()); } else { - assert!(!ip!($s).is_loopback()); + assert!(!ip.is_loopback()); } if ($mask & global) == global { - assert!(ip!($s).is_global()); + assert!(ip.is_global()); } else { - assert!(!ip!($s).is_global()); + assert!(!ip.is_global()); } if ($mask & multicast) == multicast { - assert!(ip!($s).is_multicast()); + assert!(ip.is_multicast()); } else { - assert!(!ip!($s).is_multicast()); + assert!(!ip.is_multicast()); } if ($mask & doc) == doc { - assert!(ip!($s).is_documentation()); + assert!(ip.is_documentation()); } else { - assert!(!ip!($s).is_documentation()); + assert!(!ip.is_documentation()); } if ($mask & benchmarking) == benchmarking { - assert!(ip!($s).is_benchmarking()); + assert!(ip.is_benchmarking()); } else { - assert!(!ip!($s).is_benchmarking()); + assert!(!ip.is_benchmarking()); } }}; } @@ -339,18 +336,15 @@ fn ip_properties() { #[test] fn ipv4_properties() { - macro_rules! ip { - ($s:expr) => { - Ipv4Addr::from_str($s).unwrap() - }; - } - macro_rules! check { ($s:expr) => { check!($s, 0); }; ($s:expr, $mask:expr) => {{ + let ip = Ipv4Addr::from_str($s).unwrap(); + assert_eq!($s, ip.to_string()); + let unspec: u16 = 1 << 0; let loopback: u16 = 1 << 1; let private: u16 = 1 << 2; @@ -364,69 +358,69 @@ fn ipv4_properties() { let shared: u16 = 1 << 11; if ($mask & unspec) == unspec { - assert!(ip!($s).is_unspecified()); + assert!(ip.is_unspecified()); } else { - assert!(!ip!($s).is_unspecified()); + assert!(!ip.is_unspecified()); } if ($mask & loopback) == loopback { - assert!(ip!($s).is_loopback()); + assert!(ip.is_loopback()); } else { - assert!(!ip!($s).is_loopback()); + assert!(!ip.is_loopback()); } if ($mask & private) == private { - assert!(ip!($s).is_private()); + assert!(ip.is_private()); } else { - assert!(!ip!($s).is_private()); + assert!(!ip.is_private()); } if ($mask & link_local) == link_local { - assert!(ip!($s).is_link_local()); + assert!(ip.is_link_local()); } else { - assert!(!ip!($s).is_link_local()); + assert!(!ip.is_link_local()); } if ($mask & global) == global { - assert!(ip!($s).is_global()); + assert!(ip.is_global()); } else { - assert!(!ip!($s).is_global()); + assert!(!ip.is_global()); } if ($mask & multicast) == multicast { - assert!(ip!($s).is_multicast()); + assert!(ip.is_multicast()); } else { - assert!(!ip!($s).is_multicast()); + assert!(!ip.is_multicast()); } if ($mask & broadcast) == broadcast { - assert!(ip!($s).is_broadcast()); + assert!(ip.is_broadcast()); } else { - assert!(!ip!($s).is_broadcast()); + assert!(!ip.is_broadcast()); } if ($mask & documentation) == documentation { - assert!(ip!($s).is_documentation()); + assert!(ip.is_documentation()); } else { - assert!(!ip!($s).is_documentation()); + assert!(!ip.is_documentation()); } if ($mask & benchmarking) == benchmarking { - assert!(ip!($s).is_benchmarking()); + assert!(ip.is_benchmarking()); } else { - assert!(!ip!($s).is_benchmarking()); + assert!(!ip.is_benchmarking()); } if ($mask & reserved) == reserved { - assert!(ip!($s).is_reserved()); + assert!(ip.is_reserved()); } else { - assert!(!ip!($s).is_reserved()); + assert!(!ip.is_reserved()); } if ($mask & shared) == shared { - assert!(ip!($s).is_shared()); + assert!(ip.is_shared()); } else { - assert!(!ip!($s).is_shared()); + assert!(!ip.is_shared()); } }}; } @@ -479,23 +473,19 @@ fn ipv4_properties() { #[test] fn ipv6_properties() { - macro_rules! ip { - ($s:expr) => { - Ipv6Addr::from_str($s).unwrap() - }; - } - macro_rules! check { ($s:expr, &[$($octet:expr),*]) => { check!($s, &[$($octet),*], 0); }; ($s:expr, &[$($octet:expr),*], $mask:expr) => { - assert_eq!($s, ip!($s).to_string()); + let ip = Ipv6Addr::from_str($s).unwrap(); + assert_eq!($s, ip.to_string()); + let octets = &[$($octet),*]; - assert_eq!(&ip!($s).octets(), octets); - assert_eq!(Ipv6Addr::from(*octets), ip!($s)); - assert_eq!(Ipv6Addr::from_octets(*octets), ip!($s)); + assert_eq!(&ip.octets(), octets); + assert_eq!(Ipv6Addr::from(*octets), ip); + assert_eq!(Ipv6Addr::from_octets(*octets), ip); let unspecified: u32 = 1 << 0; let loopback: u32 = 1 << 1; @@ -522,84 +512,84 @@ fn ipv6_properties() { let ipv4_mapped: u32 = 1 << 17; if ($mask & unspecified) == unspecified { - assert!(ip!($s).is_unspecified()); + assert!(ip.is_unspecified()); } else { - assert!(!ip!($s).is_unspecified()); + assert!(!ip.is_unspecified()); } if ($mask & loopback) == loopback { - assert!(ip!($s).is_loopback()); + assert!(ip.is_loopback()); } else { - assert!(!ip!($s).is_loopback()); + assert!(!ip.is_loopback()); } if ($mask & unique_local) == unique_local { - assert!(ip!($s).is_unique_local()); + assert!(ip.is_unique_local()); } else { - assert!(!ip!($s).is_unique_local()); + assert!(!ip.is_unique_local()); } if ($mask & global) == global { - assert!(ip!($s).is_global()); + assert!(ip.is_global()); } else { - assert!(!ip!($s).is_global()); + assert!(!ip.is_global()); } if ($mask & unicast_link_local) == unicast_link_local { - assert!(ip!($s).is_unicast_link_local()); + assert!(ip.is_unicast_link_local()); } else { - assert!(!ip!($s).is_unicast_link_local()); + assert!(!ip.is_unicast_link_local()); } if ($mask & unicast_global) == unicast_global { - assert!(ip!($s).is_unicast_global()); + assert!(ip.is_unicast_global()); } else { - assert!(!ip!($s).is_unicast_global()); + assert!(!ip.is_unicast_global()); } if ($mask & documentation) == documentation { - assert!(ip!($s).is_documentation()); + assert!(ip.is_documentation()); } else { - assert!(!ip!($s).is_documentation()); + assert!(!ip.is_documentation()); } if ($mask & benchmarking) == benchmarking { - assert!(ip!($s).is_benchmarking()); + assert!(ip.is_benchmarking()); } else { - assert!(!ip!($s).is_benchmarking()); + assert!(!ip.is_benchmarking()); } if ($mask & multicast) != 0 { - assert!(ip!($s).multicast_scope().is_some()); - assert!(ip!($s).is_multicast()); + assert!(ip.multicast_scope().is_some()); + assert!(ip.is_multicast()); } else { - assert!(ip!($s).multicast_scope().is_none()); - assert!(!ip!($s).is_multicast()); + assert!(ip.multicast_scope().is_none()); + assert!(!ip.is_multicast()); } if ($mask & multicast_interface_local) == multicast_interface_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), + assert_eq!(ip.multicast_scope().unwrap(), Ipv6MulticastScope::InterfaceLocal); } if ($mask & multicast_link_local) == multicast_link_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), + assert_eq!(ip.multicast_scope().unwrap(), Ipv6MulticastScope::LinkLocal); } if ($mask & multicast_realm_local) == multicast_realm_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), + assert_eq!(ip.multicast_scope().unwrap(), Ipv6MulticastScope::RealmLocal); } if ($mask & multicast_admin_local) == multicast_admin_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), + assert_eq!(ip.multicast_scope().unwrap(), Ipv6MulticastScope::AdminLocal); } if ($mask & multicast_site_local) == multicast_site_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), + assert_eq!(ip.multicast_scope().unwrap(), Ipv6MulticastScope::SiteLocal); } if ($mask & multicast_organization_local) == multicast_organization_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), + assert_eq!(ip.multicast_scope().unwrap(), Ipv6MulticastScope::OrganizationLocal); } if ($mask & multicast_global) == multicast_global { - assert_eq!(ip!($s).multicast_scope().unwrap(), + assert_eq!(ip.multicast_scope().unwrap(), Ipv6MulticastScope::Global); } if ($mask & ipv4_mapped) == ipv4_mapped { - assert!(ip!($s).is_ipv4_mapped()); + assert!(ip.is_ipv4_mapped()); } else { - assert!(!ip!($s).is_ipv4_mapped()); + assert!(!ip.is_ipv4_mapped()); } } } diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index facf74f10fb29..25a5b767699fe 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1,10 +1,7 @@ use rand::RngCore; -#[cfg(not(miri))] use super::Dir; use crate::fs::{self, File, FileTimes, OpenOptions, TryLockError}; -#[cfg(not(miri))] -use crate::io; use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; use crate::mem::MaybeUninit; @@ -20,7 +17,7 @@ use crate::path::Path; use crate::sync::Arc; use crate::test_helpers::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; -use crate::{assert_matches, env, str, thread}; +use crate::{assert_matches, env, io, str, thread}; macro_rules! check { ($e:expr) => { @@ -825,10 +822,12 @@ fn recursive_mkdir_failure() { #[test] fn concurrent_recursive_mkdir() { - for _ in 0..100 { + let count = if cfg!(miri) { 10 } else { 100 }; + let nest = if cfg!(miri) { 10 } else { 40 }; + for _ in 0..count { let dir = tmpdir(); let mut dir = dir.join("a"); - for _ in 0..40 { + for _ in 0..nest { dir = dir.join("a"); } let mut join = vec![]; @@ -1835,7 +1834,7 @@ fn create_dir_long_paths() { fn read_large_dir() { let tmpdir = tmpdir(); - let count = 32 * 1024; + let count = if cfg!(miri) { 1024 } else { 32 * 1024 }; for i in 0..count { check!(fs::File::create(tmpdir.join(&i.to_string()))); } @@ -1920,6 +1919,7 @@ fn test_eq_windows_file_type() { /// Regression test for https://github.com/rust-lang/rust/issues/50619. #[test] #[cfg(target_os = "linux")] +#[cfg_attr(miri, ignore)] // Cannot spawn processes on Miri fn test_read_dir_infinite_loop() { use crate::io::ErrorKind; use crate::process::Command; @@ -2495,8 +2495,6 @@ fn test_fs_set_times_nofollow() { } #[test] -// FIXME: libc calls fail on miri -#[cfg(not(miri))] fn test_dir_smoke_test() { let tmpdir = tmpdir(); let dir = Dir::open(tmpdir.path()); @@ -2504,8 +2502,6 @@ fn test_dir_smoke_test() { } #[test] -// FIXME: libc calls fail on miri -#[cfg(not(miri))] fn test_dir_read_file() { let tmpdir = tmpdir(); let mut f = check!(File::create(tmpdir.join("foo.txt"))); diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 4f38d3be52f7d..3c1778cae6e22 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -26,7 +26,7 @@ use crate::sys::cmath; #[cfg(not(test))] impl f32 { - /// Returns the largest integer less than or equal to `self`. + /// Returns the largest integer that is less than or equal to `self`. /// /// This function always returns the precise result. /// @@ -50,7 +50,7 @@ impl f32 { core::f32::math::floor(self) } - /// Returns the smallest integer greater than or equal to `self`. + /// Returns the smallest integer that is greater than or equal to `self`. /// /// This function always returns the precise result. /// @@ -59,9 +59,11 @@ impl f32 { /// ``` /// let f = 3.01_f32; /// let g = 4.0_f32; + /// let h = -3.01_f32; /// /// assert_eq!(f.ceil(), 4.0); /// assert_eq!(g.ceil(), 4.0); + /// assert_eq!(h.ceil(), -3.0); /// ``` #[doc(alias = "ceiling")] #[rustc_allow_incoherent_impl] diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 8a771185f6fe4..af262474f1b0f 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -26,7 +26,7 @@ use crate::sys::cmath; #[cfg(not(test))] impl f64 { - /// Returns the largest integer less than or equal to `self`. + /// Returns the largest integer that is less than or equal to `self`. /// /// This function always returns the precise result. /// @@ -50,7 +50,7 @@ impl f64 { core::f64::math::floor(self) } - /// Returns the smallest integer greater than or equal to `self`. + /// Returns the smallest integer that is greater than or equal to `self`. /// /// This function always returns the precise result. /// @@ -59,9 +59,11 @@ impl f64 { /// ``` /// let f = 3.01_f64; /// let g = 4.0_f64; + /// let h = -3.01_f64; /// /// assert_eq!(f.ceil(), 4.0); /// assert_eq!(g.ceil(), 4.0); + /// assert_eq!(h.ceil(), -3.0); /// ``` #[doc(alias = "ceiling")] #[rustc_allow_incoherent_impl] diff --git a/library/std/src/sys/io/kernel_copy/linux/tests.rs b/library/std/src/sys/io/kernel_copy/linux/tests.rs index 9b2d9cbef9902..ba0828039dddb 100644 --- a/library/std/src/sys/io/kernel_copy/linux/tests.rs +++ b/library/std/src/sys/io/kernel_copy/linux/tests.rs @@ -166,10 +166,14 @@ fn bench_file_to_socket_copy(b: &mut test::Bencher) { let mut sink = crate::net::TcpStream::connect(sink_drainer.local_addr().unwrap()).unwrap(); let mut sink_drainer = sink_drainer.accept().unwrap().0; - crate::thread::spawn(move || { + let t = crate::thread::spawn(move || { let mut sink_buf = vec![0u8; 1024 * 1024]; loop { - sink_drainer.read(&mut sink_buf[..]).unwrap(); + let n = sink_drainer.read(&mut sink_buf[..]).unwrap(); + if n == 0 { + // EOF + break; + } } }); @@ -178,6 +182,9 @@ fn bench_file_to_socket_copy(b: &mut test::Bencher) { src.seek(SeekFrom::Start(0)).unwrap(); assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap()); }); + + drop(sink); // close our end, so that the thread goes down + t.join().unwrap(); } #[bench] @@ -196,10 +203,14 @@ fn bench_file_to_uds_copy(b: &mut test::Bencher) { let (mut sink, mut sink_drainer) = crate::os::unix::net::UnixStream::pair().unwrap(); - crate::thread::spawn(move || { + let t = crate::thread::spawn(move || { let mut sink_buf = vec![0u8; 1024 * 1024]; loop { - sink_drainer.read(&mut sink_buf[..]).unwrap(); + let n = sink_drainer.read(&mut sink_buf[..]).unwrap(); + if n == 0 { + // EOF + break; + } } }); @@ -208,6 +219,9 @@ fn bench_file_to_uds_copy(b: &mut test::Bencher) { src.seek(SeekFrom::Start(0)).unwrap(); assert_eq!(BYTES as u64, io::copy(&mut src, &mut sink).unwrap()); }); + + drop(sink); // close our end, so that the thread goes down + t.join().unwrap(); } #[cfg(any(target_os = "linux", target_os = "android"))] diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 0991be4cd02d7..e22fde79a8444 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -742,9 +742,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.38.4" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f" +checksum = "cd9f9fe3d2b7b75cf4f2805e5b9926e8ac47146667b16b86298c4a8bf08cc469" dependencies = [ "libc", "memchr", diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 9b1039bbc4f6a..363802093a13a 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -57,7 +57,7 @@ walkdir = "2.4" xz2 = "0.1" # Dependencies needed by the build-metrics feature -sysinfo = { version = "0.38.4", default-features = false, optional = true, features = ["system"] } +sysinfo = { version = "0.39.0", default-features = false, optional = true, features = ["system"] } # Dependencies needed by the `tracing` feature chrono = { version = "0.4", default-features = false, optional = true, features = ["now", "std"] } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 6518224576c9d..fd3e88e1a36f4 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1679,6 +1679,9 @@ impl Build { /// Returns the `a.b.c` version that the given package is at. fn release_num(&self, package: &str) -> String { + if self.config.dry_run() { + return "0.0.0 (dry-run)".into(); + } let toml_file_name = self.src.join(format!("src/tools/{package}/Cargo.toml")); let toml = t!(fs::read_to_string(toml_file_name)); for line in toml.lines() { diff --git a/src/etc/pre-push.sh b/src/etc/pre-push.sh index 33ed2f0e406b1..210edbf0cda66 100755 --- a/src/etc/pre-push.sh +++ b/src/etc/pre-push.sh @@ -21,6 +21,13 @@ if $SKIP; then exit 0 fi +if [ -n "$(git status --porcelain --untracked-files=no)" ]; then + echo "error: working tree is dirty, refusing to push" + echo " commit, stash, or discard changes first." + echo "You may use \`git push --no-verify\` to skip this check." + exit 1 +fi + ROOT_DIR="$(git rev-parse --show-toplevel)" echo "Running pre-push script $ROOT_DIR/x test tidy" diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml index 9b54f62da9bdf..d80e6caac0885 100644 --- a/src/tools/opt-dist/Cargo.toml +++ b/src/tools/opt-dist/Cargo.toml @@ -10,7 +10,7 @@ log = "0.4" anyhow = "1" humantime = "2" humansize = "2" -sysinfo = { version = "0.38.4", default-features = false, features = ["disk"] } +sysinfo = { version = "0.39.0", default-features = false, features = ["disk"] } fs_extra = "1" camino = "1" tar = "0.4.45" diff --git a/src/tools/rust-analyzer/.config/nextest.toml b/src/tools/rust-analyzer/.config/nextest.toml new file mode 100644 index 0000000000000..47e5bf53d0f0f --- /dev/null +++ b/src/tools/rust-analyzer/.config/nextest.toml @@ -0,0 +1,2 @@ +[profile.default] +slow-timeout = { period = "60s", terminate-after = 5 } diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index c27d84fb0bc06..b9427e1927f6c 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.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/src/tools/rust-analyzer/.github/workflows/coverage.yaml b/src/tools/rust-analyzer/.github/workflows/coverage.yaml index 9460c6a3c77a6..55edbbefbaa10 100644 --- a/src/tools/rust-analyzer/.github/workflows/coverage.yaml +++ b/src/tools/rust-analyzer/.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/src/tools/rust-analyzer/.gitignore b/src/tools/rust-analyzer/.gitignore index 7192e685e29bf..3beabe39bc7a3 100644 --- a/src/tools/rust-analyzer/.gitignore +++ b/src/tools/rust-analyzer/.gitignore @@ -1,4 +1,5 @@ target/ +vendor/ /dist/ **/*.rs.bk **/*.rs.pending-snap diff --git a/src/tools/rust-analyzer/.typos.toml b/src/tools/rust-analyzer/.typos.toml index e954b08fb1e1b..873daa3bf3b83 100644 --- a/src/tools/rust-analyzer/.typos.toml +++ b/src/tools/rust-analyzer/.typos.toml @@ -34,6 +34,7 @@ thir = "thir" jod = "jod" tructure = "tructure" taits = "taits" +inh = "inh" [default.extend-identifiers] anc = "anc" diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index e6575c28c1dd0..cbbeef00309e3 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -647,6 +647,12 @@ version = "0.1.5" 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" @@ -734,7 +740,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -743,6 +749,17 @@ version = "0.16.0" 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" @@ -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", @@ -3108,6 +3129,12 @@ version = "2.0.2" 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" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index b8dedc6c50a01..a08b2e412e47f 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/bench_data/glorious_old_parser b/src/tools/rust-analyzer/bench_data/glorious_old_parser index 5022514924687..a6de9daa86dee 100644 --- a/src/tools/rust-analyzer/bench_data/glorious_old_parser +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index e438505c07e4b..a209a0e631e0b 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/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 = indexmap::IndexSet; pub type FxIndexMap = diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs index 5cf5a9b6be847..90fae16ccd0c2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/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 { - let mut result = None; - collect_attrs::(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 { + let mut result = None; + collect_attrs::(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> { + 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> { + 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/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index 9a715b19688e6..0d01d54786a1f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/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::().map(|parse| parse.tree())?; expander.recursion_depth -= 1; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 9dd7768ead868..11e5c54246440 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 497ed7d37f417..4dc72672314cc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/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; pub type LifetimePtr = AstPtr; pub type LifetimeSource = InFile; -/// 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; ]` — expected type is `usize`. - ArrayLength, - /// Const parameter default value: `const N: usize = `. - ConstParam(crate::hir::generics::LocalTypeOrConstParamId), - /// Const generic argument in a path: `SomeType::<{ }>` or `some_fn::<{ }>()`. - /// 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, /// 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, block_scopes: Vec, ident_hygiene: FxHashMap, - pub inference_roots: Option>, + inference_roots: Option>, // 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>, name: Name }, AwaitOutsideOfAsync { node: InFile>, location: String }, UndeclaredLabel { node: InFile>, name: Name }, + PatternArgInExternFn { node: InFile> }, } 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 { - self.const_expr_origins().iter().map(|&(id, _)| id) + pub fn expr_roots(&self) -> impl DoubleEndedIterator { + 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 { - 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); + + impl StoreVisitor for Visitor { + 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 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 { + callback: F, + } + + impl StoreVisitor for Visitor { + 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 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 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) { + if let Some(expr) = expr { + self.on_expr(expr); + } + } + fn on_pat_opt(&mut self, pat: Option) { + if let Some(pat) = pat { + self.on_pat(pat); + } + } + fn on_type_opt(&mut self, ty: Option) { + if let Some(ty) = ty { + self.on_type(ty); + } + } + fn on_lifetime_opt(&mut self, lifetime: Option) { + if let Some(lifetime) = lifetime { + self.on_lifetime(lifetime); + } + } + fn on_generic_args_opt(&mut self, args: &Option>) { + if let Some(args) = args { + self.on_generic_args(args.borrow()); + } + } + + fn on_exprs(&mut self, exprs: impl IntoIterator>) { + exprs.into_iter().for_each(|expr| self.on_expr(*expr.borrow())); + } + fn on_pats(&mut self, pats: impl IntoIterator>) { + pats.into_iter().for_each(|pat| self.on_pat(*pat.borrow())); + } + fn on_types(&mut self, types: impl IntoIterator>) { + types.into_iter().for_each(|ty| self.on_type(*ty.borrow())); + } + fn on_type_bounds(&mut self, bounds: impl IntoIterator>) { + bounds.into_iter().for_each(|bound| self.on_type_bound(bound.borrow())); + } +} +impl StoreVisitorExt for V {} + impl Index for ExpressionStore { type Output = Expr; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs index 6be3e49a70def..2fb47e59c546f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs +++ b/src/tools/rust-analyzer/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, + /// 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, } 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 { + 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 { + self.self_params.get(1).copied() } pub fn pretty_print( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs index 2fffa02c13145..c79a1db8472ba 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs +++ b/src/tools/rust-analyzer/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::(); + let parse = res.value.0.clone().cast::(); 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/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 04437a59ac815..8818096500242 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/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, body: Option, 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>, ) -> (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, where_clause: Option, ) -> (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, impl_id: ImplId, ) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId, Option, 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, 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) { - 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, 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(: , : , : ) { + // + // } + // + // into: + // + // fn foo(__arg0: , __arg1: , __arg2: ) { + // async move { + // let __arg2 = __arg2; + // let = __arg2; + // let __arg1 = __arg1; + // let = __arg1; + // let __arg0 = __arg0; + // let = __arg0; + // drop-temps { } // see comments later in fn for details + // } + // } + // + // If `` is a simple ident, then it is lowered to a single + // `let = ;` 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 = __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, @@ -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, params: &mut [PatId], expr: Option, 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) -> 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) -> 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, 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, 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(arena: &la_arena::Arena) -> la_arena::Idx { + 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/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs index 51616afb3892c..b058ad6d9ab8b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs +++ b/src/tools/rust-analyzer/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 /// ::new_…(arg) /// ``` - fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId { + fn make_argument( + &mut self, + arg_ptr: Option>, + 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/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs index 5ffc4f5851d3e..7ef9c80de1ae8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs index 579465e10f932..236255c404f89 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs index 6819eb3deb575..f507841a91bf5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs @@ -21,7 +21,7 @@ fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option) { 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/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 70ea54c734cad..34cedbd728f9a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index c6ba0241b7140..ddb828513775c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs +++ b/src/tools/rust-analyzer/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| label.map(|label| (label, store[label].name.clone())); + let make_label = |label: Option| 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/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs index db12775df95f6..e97718ca22c32 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs +++ b/src/tools/rust-analyzer/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 0, param4 @ _, mut 1) async { + fn main(self, param1, mut param2, mut 0, mut param4, mut 1) async { + let self = self; + let mut param1 = param1; + let mut param2 = param2; let ref mut param2 = param2; + let mut 0 = 0; let _ = 0; + let mut param4 = param4; + let param4 @ _ = param4; + let mut 1 = 1; let 123 = 1; {} }"#]], diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs index 5e0184dfad82d..24bdc6ece31e7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 8308203693368..2a1b7f7cb0cf8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/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(); @@ -2031,6 +2022,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#" diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 9e51d0eac98a1..6eba85926403b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -259,7 +259,7 @@ pub enum Expr { expr: Option, }, RecordLit { - path: Option>, + 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>, + path: Path, args: Box<[RecordFieldPat]>, ellipsis: bool, }, @@ -687,7 +696,6 @@ pub enum Pat { slice: Option, 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, }, TupleStruct { - path: Option>, + path: Path, args: Box<[PatId]>, ellipsis: Option, }, @@ -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/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index 271484da7b9a2..366857f233168 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/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>, } #[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, fmt_snippet: Option, 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/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs index 43dd7d1c54cd1..04ccc7bf90cf0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs @@ -188,6 +188,11 @@ impl GenericParams { pub const SELF_PARAM_ID_IN_SELF: la_arena::Idx = 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/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs index b64199fa2697e..43e29c1f5ffcf 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs +++ b/src/tools/rust-analyzer/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, TypeRefId)]>, pub is_varargs: bool, pub is_unsafe: bool, - pub abi: Option, + 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/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index e7ab2b390f264..4b50161c04cd0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/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 = 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 { +) -> Option> { let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered(); - static EMPTY: OnceLock> = 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/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 31e409d86e490..91c2e60fd7c4c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/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, + span_map: OnceCell>, 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/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 37d70b1e33a91..c2cbb9eda7268 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/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 Option, def: Option| 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, 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, 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> { 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, )* + $( + 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/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 22c9073a581fc..3aaf89102fab9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/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 = indexmap::IndexMap; @@ -223,9 +220,9 @@ impl AstIdLoc for AssocItemLoc { } 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; -impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function); +impl_intern!(FunctionId, FunctionLoc); type StructLoc = ItemLoc; -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; -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; -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; -impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const); +impl_intern!(ConstId, ConstLoc); pub type StaticLoc = AssocItemLoc; -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; -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; -impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); +impl_intern!(TypeAliasId, TypeAliasLoc); type ImplLoc = ItemLoc; -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; -impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use); +impl_intern!(UseId, UseLoc); type ExternCrateLoc = ItemLoc; -impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate); +impl_intern!(ExternCrateId, ExternCrateLoc); type ExternBlockLoc = ItemLoc; -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 { + 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 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 { - 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 for DefWithBodyId { } impl DefWithBodyId { - pub fn as_generic_def_id(self, db: &dyn DefDatabase) -> Option { + 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/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index eabdada67c2a7..1d6812ad56a54 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 56b3f03f7b60b..88e3408a33b14 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/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, /// Unstable features of Rust enabled with `#![feature(A, B)]`. - unstable_features: FxHashSet, + 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/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index f5a852b39c8d8..b1d554738f71f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/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, @@ -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/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index 062b55fcefdf2..5aabd7dbc6fcc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 703f070dbaeac..5ef51dbb2ca53 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index 0f1828abceadb..08f672aa0c0df 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index bb292ac1a6bcc..8320221ffc8ca 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 6d704274f45d8..e58befae20c04 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/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 { 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 { + 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 { - 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, - pub abi: Option, + 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( override_visibility: Option>, ) -> Option<(Arena, 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 { +#[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/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs b/src/tools/rust-analyzer/crates/hir-def/src/unstable_features.rs new file mode 100644 index 0000000000000..f581f02617883 --- /dev/null +++ b/src/tools/rust-analyzer/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 { + 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, + + $( 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/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index d1f962f7ffd38..395b997972965 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs index c94663ca0cbcb..9b13f9fb00eba 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs @@ -122,7 +122,7 @@ fn derive_expand( tt: &tt::TopSubtree, span: Span, ) -> ExpandResult { - 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/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index 8f513a2bcf666..8b031e364775c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/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 { + 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 { + 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, 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/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 9962677a9da6a..eb7175c686a08 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -357,7 +357,7 @@ fn cfg_select_expand( tt: &tt::TopSubtree, span: Span, ) -> ExpandResult { - 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 { - 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 { - 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 { - 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/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs index 51c4e225168fe..d84756377fc75 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs @@ -199,9 +199,9 @@ impl 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/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index 6258fac0e9923..81edc9f2cffaf 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/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) -> (bool, Vec) { 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/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 8dddddfabb7a2..beae6e843e493 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/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, 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, 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), + 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, Arc)>; + ) -> &ExpandResult<(Parse, 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; + 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; - - /// 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, - ) -> Arc; + ) -> &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>; + #[salsa::transparent] + fn expand_proc_macro(&self, call: MacroCallId) -> &ExpandResult; /// 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) -> 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>>>; + ) -> Option>>; #[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, Arc)> { +) -> ExpandResult<(Parse, 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>>> { +) -> Option>> { let e: ExpandResult> = - 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, SpanMap) { +) -> (Parse, 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 { - Arc(Arc), - Owned(T), -} - fn macro_expand( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, loc: MacroCallLoc, -) -> ExpandResult<(CowArc, 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) -> Span { @@ -641,11 +630,9 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { span_map.span_for_range(range) } -fn expand_proc_macro( - db: &dyn ExpandDatabase, - id: MacroCallId, -) -> ExpandResult> { - let loc = db.lookup_intern_macro_call(id); +#[salsa_macros::tracked(returns(ref))] +fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult { + 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/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 4b2c6e73517b8..99db0dbcb99f8 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/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)> { - 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, - ) -> Arc { + ) -> 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/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index 0b6124ebf3598..a19f58709b8f9 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -20,9 +20,10 @@ //! See the full discussion : 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, krate: Crate, call_site: SyntaxContext, eager_callback: EagerCallBackFn<'_>, -) -> ExpandResult<(InFile>, Arc)> { +) -> ExpandResult<(InFile>, &'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, eager_callback: EagerCallBackFn<'_>, ) -> ExpandResult> { - 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/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs index 71da560b15f1a..a4c206156da34 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -128,6 +128,16 @@ impl ErasedAstId { } } +impl InFileWrapper> { + #[inline] + pub fn upcast(self) -> InFileWrapper> + where + N: Into, + { + self.map(|it| it.upcast()) + } +} + impl InFileWrapper { pub fn new(file_id: FileKind, value: T) -> Self { Self { file_id, value } @@ -256,8 +266,10 @@ impl> InFile { ) -> impl Iterator> + '_ { let succ = move |node: &InFile| 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> InFile { ) -> impl Iterator> + '_ { let succ = move |node: &InFile| 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> InFile { 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 { 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 { } // 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 { 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 { impl InMacroFile { 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 { (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 { 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 { 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 { 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 { 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 InFile { 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/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index 424655ed651bd..939104b709163 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/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>>, + original: Option>, } 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::::default(); let mut remove = FxHashSet::::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/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs index ce7650d077117..1cf8ce2a57f95 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs index 53b624d9a67ac..4185b7b018954 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 8d42a24e2fae8..0850d6156d112 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/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 = ValueResult; @@ -279,7 +273,7 @@ impl MacroDefKind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EagerCallInfo { /// The expanded argument of the eager macro. - arg: Arc, + 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, @@ -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>, + eager: Option>, }, Derive { ast_id: AstId, @@ -311,7 +305,7 @@ pub enum MacroCallKind { Attr { ast_id: AstId, // FIXME: This shouldn't be here, we can derive this from `invoc_attr_index`. - attr_args: Option>, + attr_args: Option>, /// 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> { - 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> { - 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> { 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 { - 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 { - 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 { @@ -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, /// The argument TokenTree or item for attributes arg: InFile>, - exp_map: Arc, - arg_map: SpanMap, + exp_map: &'db ExpansionSpanMap, + arg_map: SpanMap<'db>, loc: MacroCallLoc, } -impl ExpansionInfo { +impl<'db> ExpansionInfo<'db> { pub fn expanded(&self) -> InMacroFile { 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/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 78228cf82e678..9142ad8b92119 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index 0408a6943d59f..3ddc305f9592c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/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 { 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 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 for Symbol { + #[inline] fn eq(&self, name: &Name) -> bool { *self == name.symbol } } impl PartialEq 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!("{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 { - 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("") } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs index 6431d46d39e97..79e6f0f5b7aa4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs index aa8603341b3b2..6a94df8b5a554 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/span_map.rs +++ b/src/tools/rust-analyzer/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(&'db ExpansionSpanMap), /// Spanmap for a real file - RealSpanMap(Arc), + 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 { 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 { - db.parse_macro_expansion(file_id).value.1 +) -> &ExpansionSpanMap { + &db.parse_macro_expansion(file_id).value.1 } diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 18426f3095b11..e8eda74e40017 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index abab3bfb25093..a8ed4126abeae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/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> + 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/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index 6a9b1671e7be1..fe60fbc5109f5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 80e7e05d76fed..e1d6cec59421e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/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 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, 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 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 { - match (*c).kind() { +pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option { + 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, + path: &Path, +) -> Result, 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, +) -> Result, 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, @@ -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>, +) -> Result, 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, + ) -> Result { + 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, + ) -> Result { + 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 { let interner = DbInterner::new_no_crate(db); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 723fa0fc687a3..f15866106928b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/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() -> usize; - } + #[rustc_intrinsic] + pub fn size_of() -> usize; const fn f() -> usize { let r = const { size_of::() }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 10282d21168a3..85e917fe1a0d1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/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: ARG, called_in_const: F, called_at_rt: G) -> RET - where - G: FnOnce, - F: FnOnce; - } + #[rustc_intrinsic] + pub fn const_eval_select(arg: ARG, called_in_const: F, called_at_rt: G) -> RET + where + G: FnOnce, + F: FnOnce; 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(a: T, b: T) -> T; - } + #[rustc_intrinsic] + pub fn wrapping_add(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(ptr: *const T, base: *const T) -> isize; - pub fn ptr_offset_from_unsigned(ptr: *const T, base: *const T) -> usize; - } + #[rustc_intrinsic] + pub fn ptr_offset_from(ptr: *const T, base: *const T) -> isize; + #[rustc_intrinsic] + pub fn ptr_offset_from_unsigned(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(a: T, b: T) -> T; - } + #[rustc_intrinsic] + pub fn saturating_add(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(a: T, b: T) -> T; - } + #[rustc_intrinsic] + pub fn saturating_sub(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(a: T, b: T) -> T; - } + #[rustc_intrinsic] + pub fn saturating_add(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(x: T, y: T) -> (T, bool); - } + #[rustc_intrinsic] + pub fn add_with_overflow(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(x: T, y: T) -> (T, bool); - } + #[rustc_intrinsic] + pub fn add_with_overflow(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() -> bool; - } + #[rustc_intrinsic] + pub fn needs_drop() -> 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(v: &T) -> ::Discriminant; - } + #[rustc_intrinsic] + pub fn discriminant_value(v: &T) -> ::Discriminant; const GOAL: bool = { discriminant_value(&Some(2i32)) == discriminant_value(&Some(5i32)) && discriminant_value(&Some(2i32)) != discriminant_value(&None::) @@ -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(src: *const T) -> T; - pub fn atomic_xchg_acquire(dst: *mut T, src: T) -> T; - pub fn atomic_cxchg_release_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); - pub fn atomic_cxchgweak_acquire_acquire(dst: *mut T, old: T, src: T) -> (T, bool); - pub fn atomic_store_release(dst: *mut T, val: T); - pub fn atomic_xadd_acqrel(dst: *mut T, src: T) -> T; - pub fn atomic_xsub_seqcst(dst: *mut T, src: T) -> T; - pub fn atomic_and_acquire(dst: *mut T, src: T) -> T; - pub fn atomic_nand_seqcst(dst: *mut T, src: T) -> T; - pub fn atomic_or_release(dst: *mut T, src: T) -> T; - pub fn atomic_xor_seqcst(dst: *mut T, src: T) -> T; - pub fn atomic_fence_seqcst(); - pub fn atomic_singlethreadfence_acqrel(); - } + #[rustc_intrinsic] + pub fn atomic_load_seqcst(src: *const T) -> T; + #[rustc_intrinsic] + pub fn atomic_xchg_acquire(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_cxchg_release_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); + #[rustc_intrinsic] + pub fn atomic_cxchgweak_acquire_acquire(dst: *mut T, old: T, src: T) -> (T, bool); + #[rustc_intrinsic] + pub fn atomic_store_release(dst: *mut T, val: T); + #[rustc_intrinsic] + pub fn atomic_xadd_acqrel(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_xsub_seqcst(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_and_acquire(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_nand_seqcst(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_or_release(dst: *mut T, src: T) -> T; + #[rustc_intrinsic] + pub fn atomic_xor_seqcst(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(dst: Ptr, offset: Delta) -> Ptr; - pub fn arith_offset(dst: *const T, offset: isize) -> *const T; - } + #[rustc_intrinsic] + pub fn offset(dst: Ptr, offset: Delta) -> Ptr; + #[rustc_intrinsic] + pub fn arith_offset(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(dst: *const T, offset: isize) -> *const T; - } + #[rustc_intrinsic] + pub fn arith_offset(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(src: *const T, dst: *mut T, count: usize); - } + #[rustc_intrinsic] + pub fn copy_nonoverlapping(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(dst: *mut T, val: u8, count: usize); - } + #[rustc_intrinsic] + fn write_bytes(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(ptr: *mut T, value: T); - } + #[rustc_intrinsic] + fn write_via_move(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(src: *const T, dst: *mut T, count: usize); - } + #[rustc_intrinsic] + pub fn copy(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(x: T) -> T; - } + #[rustc_intrinsic] + pub fn ctpop(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(x: T) -> T; - } + #[rustc_intrinsic] + pub fn ctlz(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(x: T) -> T; - } + #[rustc_intrinsic] + pub fn cttz(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(x: T, y: T) -> T; - } + #[rustc_intrinsic] + pub fn rotate_left(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(x: T, y: T) -> T; - } + #[rustc_intrinsic] + pub fn rotate_right(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(x: T, y: T) -> T; - } + #[rustc_intrinsic] + pub fn rotate_left(x: T, y: T) -> T; const GOAL: i8 = rotate_left(129, 2); "#, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 3bf2d9a6a60b4..c24a5b943db08 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/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, 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, 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, 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, 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, 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>, ) -> Result, 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>, + ) -> Result, ConstEvalError>; + #[salsa::invoke(crate::consteval::const_eval_static)] #[salsa::transparent] fn const_eval_static<'db>(&'db self, def: StaticId) -> Result, ConstEvalError>; @@ -106,8 +136,10 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { env: StoredParamEnvAndCrate, ) -> Result, LayoutError>; - #[salsa::invoke(crate::layout::target_data_layout_query)] - fn target_data_layout(&self, krate: Crate) -> Result, 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; @@ -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>; /// 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>>; + #[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>; + + #[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>; + #[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>; #[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>; + + #[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; - #[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>>; #[salsa::invoke(crate::lower::impl_trait_query)] #[salsa::transparent] fn impl_trait<'db>(&'db self, def: ImplId) -> Option>>; - #[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>, Diagnostics); + ) -> &TyLoweringResult>>; #[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>; + #[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; /// 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>>; + + #[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, + /// 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 { + 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/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 89d8c0e91d183..76fbb66b063f0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 068118c7053d8..760ebd27e0573 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/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, 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 { + ) -> Vec> { 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, + diagnostics: Vec>, 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>, + pat: PatId, + initializer: Option, + ) -> Option> { + 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> { + 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/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index f559c26bf57ea..8356329d96ae3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index bc3d9bbec6fa5..46959aaa5ac03 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index ee33f7d1585e3..c37a194d47572 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index e4a8def4425a5..bc726b652fd4e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/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> { + 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 = std::result::Result; 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<::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>, /// 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<::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>, - }, - #[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>( + fn format_bounds_with 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 - // `::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::>(); - 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 + // `::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::>(); + 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::() => match adt.def_id().0 { + TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => 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(""); }; @@ -941,7 +985,7 @@ fn render_const_scalar_inner<'db>( return f.write_str(""); }; 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(""); }; @@ -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(""); }; @@ -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::>(); 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/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 0d25d7dbd1d13..61e6720a29186 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -22,9 +22,9 @@ use crate::{ #[salsa::tracked] pub fn destructor(db: &dyn HirDatabase, adt: AdtId) -> Option { 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/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index ba63343d49351..34858212cb3ec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/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 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( - 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>>>, ) -> 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 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/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index 822942eec37dd..c4321e8a61ded 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/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, 2>, +} + +#[derive(Debug)] +pub(crate) struct SingleGenerics<'db> { def: GenericDefId, + preceding_params_len: u32, params: &'db GenericParams, store: &'db ExpressionStore, - parent_generics: Option>>, - has_trait_self_param: bool, } -impl ops::Index for Generics<'_> -where - GenericParams: ops::Index, -{ - type Output = >::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 + '_ { - 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 + '_ { - self.iter_self().map(|(id, _)| id) + fn iter_lifetimes(&self) -> impl Iterator { + 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 + '_ { - self.iter_parent().map(|(id, _)| id) + pub(crate) fn iter_type_or_consts( + &self, + ) -> impl Iterator { + 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 + '_ - { - 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)> { + 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)> + '_ { - self.iter_parent().chain(self.iter_self()) + ) -> ( + Option<(GenericParamId, GenericParamDataRef<'db>)>, + impl Iterator)>, + ) { + 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)> { + 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), &ExpressionStore)> + '_ - { - self.iter_parent() - .zip(self.parent_generics().into_iter().flat_map(|it| std::iter::repeat(it.store))) + ) -> impl Iterator)> { + std::iter::zip(self.preceding_params_len.., self.iter()) + .map(|(index, (id, data))| (index, id, data)) + } + + pub(crate) fn iter_id(&self) -> impl Iterator { + self.iter().map(|(id, _)| id) + } +} + +impl<'db> Generics<'db> { + pub(crate) fn iter_owners(&self) -> impl DoubleEndedIterator> { + 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)> + '_ { - 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)> { + 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)> + '_ { - 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)> { + self.owner().iter_with_idx() + } + + pub(crate) fn iter_parent_id(&self) -> impl Iterator { + self.parent().into_iter().flat_map(|parent| parent.iter_id()) + } + + pub(crate) fn iter_self_type_or_consts( + &self, + ) -> impl Iterator { + self.owner().iter_type_or_consts() + } + + /// Iterate over the parent params followed by self params. + #[cfg(test)] + pub(crate) fn iter(&self) -> impl Iterator)> { + self.iter_owners().flat_map(|owner| owner.iter()) + } + + pub(crate) fn iter_id(&self) -> impl Iterator { + 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 { - 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 { - 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 { - 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 { - 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 { +fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { 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/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 339ce7933af13..39ffb91a8c5db 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/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. -/// -#[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, + #[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, + #[type_visitable(ignore)] assoc_func_with_same_name: Option, }, 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, + }, + 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`. + 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, pub(crate) type_of_opaque: FxHashMap, - pub(crate) type_mismatches: Option>>, /// 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, + // FIXME: Remove this, change it to be in `InferenceContext`: + nodes_with_type_mismatches: Option>>, /// Interned `Error` type to return references to. // FIXME: Remove this. @@ -620,7 +694,7 @@ pub struct InferenceResult { pub(crate) expr_adjustments: FxHashMap>, /// Stores the types which were implicitly dereferenced in pattern binding modes. - pub(crate) pat_adjustments: FxHashMap>, + pub(crate) pat_adjustments: FxHashMap>, /// 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, + /// 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, + pub(crate) coercion_casts: FxHashSet, pub closures_data: FxHashMap, + + defined_anon_consts: ThinVec, } #[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) -> &InferenceResult { + #[inline] + pub fn of(db: &dyn HirDatabase, def: impl Into) -> &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 { - 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 { - 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)> { 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> { + 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>, + identity_args: OnceCell>, 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, pub(crate) result: InferenceResult, @@ -1147,6 +1230,9 @@ pub(crate) struct InferenceContext<'body, 'db> { deferred_call_resolutions: FxHashMap>>, diagnostics: Diagnostics, + vars_emitted_type_must_be_known_for: FxHashSet>, + + defined_anon_consts: RefCell>, } #[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(dest: &mut FxHashSet, source: &FxHashSet) { + dest.extend(source.iter().cloned()); + } + + #[cfg_attr(debug_assertions, track_caller)] + fn merge_hash_maps( + dest: &mut FxHashMap, + source: &FxHashMap, + ) { + 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( + dest: &mut ArenaMap, V>, + source: &ArenaMap, 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::>(), ); @@ -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(&mut self, ty: T) -> T where T: TypeFoldable>, @@ -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>>(&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, - ) -> 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, - // 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) { - 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) { // `::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, path: &ModPath, ) -> (Ty<'db>, Option) { 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 { - trait_.trait_items(self.db).associated_type_by_name(&Name::new_symbol_root(sym::Output)) - } - - fn resolve_future_future_output(&self) -> Option { - 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 { 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 { @@ -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> { + fn to_option(&self, table: &unify::InferenceTable<'db>) -> Option> { 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> { 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/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs index a6c7b2dbb9c38..0ba3b3dd056d1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index 3d478912a3db2..057ba7fa868fe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/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> { 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, 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: 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, + 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::>(); 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/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index e5ee73447471d..93aed344d4f60 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/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>, ()> { - 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/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 2207bc37e8be7..ab111736d56a1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/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`, // 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>, Option) { 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>, @@ -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> { @@ -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` 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> { - 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> { - 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], - decl_output: Option, + closure_expr: ExprId, + decl_inputs: &[PatId], + decl_input_tys: &[Option], + decl_output_ty: Option, expected_sig: Option>, + 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], decl_output: Option, + 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], - decl_output: Option, + closure_expr: ExprId, + decl_inputs: &[PatId], + decl_input_tys: &[Option], + decl_output_ty: Option, 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], - decl_output: Option, + closure_expr: ExprId, + decl_inputs: &[PatId], + decl_input_tys: &[Option], + decl_output_ty: Option, 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], decl_output: Option, + 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`, 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> { + 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 + // + // `::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 + /// + /// `::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> { + 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/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs index 668d7496cd1b2..5ea43bc03ca86 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 099fa18168b2c..deafff6b43e06 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/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, } -impl<'db> TypeVisitable> for Place { - fn visit_with>>(&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> for Place { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - 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::>()?; - Ok(Self { base_ty, base, projections }) - } - - fn fold_with>>(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> { - self.node_ty(pat.into()) - } - fn expr_ty_adjusted(&mut self, expr: ExprId) -> Result> { 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> { - 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 { @@ -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 { 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(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 { + 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/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 732a583047494..343919f5babbc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/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`. - 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, Ty<'db>), TypeError>> { 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/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs index 2bdc6f9491dcb..dd0efea4d754b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs +++ b/src/tools/rust-analyzer/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, + diagnostics: ThinVec, ) { 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, +} + +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>, } 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>, lifetime_elision: LifetimeElisionKind<'db>, + allow_using_generic_params: bool, + infer_vars: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>, + defined_anon_consts: &'a RefCell>, ) -> 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/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index d80ea71674775..0675b5e8578fc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/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::>(), - _ => (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::>(); + + 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, 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, Vec, 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 . 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( &mut self, kind: BreakableKind, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs index c7669b346fe8d..3744a434d2199 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/fallback.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index b2369f6a87e83..c3b532638f9d3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs index 95d63ffb508f6..9119af9628eb9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs +++ b/src/tools/rust-analyzer/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), + (op_method, trait_did): (Option, Option), ) -> Result, Vec>> { - 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) { - let (method_name, trait_lang_item) = + fn lang_item_for_bin_op(&self, op: BinaryOp) -> (Option, Option) { + 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) { - 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, Option) { + 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/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs index a39288721b3e1..63149deb82b77 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/opaques.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 8033680dcc5cf..f21438647c14f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/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, + /// 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) -> 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>`, 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, ()>>, + adjust_mode: AdjustMode, expected: Ty<'db>, - default_bm: BindingMode, - id: PatId, - ellipsis: Option, - subs: &[PatId], - decl: Option, + 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, ()>>, + adjust_mode: AdjustMode, expected: Ty<'db>, - default_bm: BindingMode, - id: PatId, - subs: impl ExactSizeIterator, - decl: Option, + 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`; 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, ()>>, + ) -> 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`. + 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, + rhs_expr: Option, expected: Ty<'db>, - default_bm: BindingMode, - ellipsis: Option, - elements: &[PatId], - decl: Option, ) -> 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| 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, + 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", 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, ()> { + // 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, ()> { + 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, - ) { - 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, ()> { + // 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, + pat_ty: Ty<'db>, + variant: VariantId, expected: Ty<'db>, - mut default_bm: BindingMode, - decl: Option, + 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, + 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::>(); + 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::>(); + + 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, + 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, + inner: PatId, expected: Ty<'db>, - decl: Option, + 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 `::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>, + ) -> 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, - suffix: &[PatId], - default_bm: BindingMode, - decl: Option, + 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, + pat: PatId, ) -> Option> { 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) -> 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, + 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, + len: Const<'db>, + min_len: u128, + ) -> (Option>, 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/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 3cadc8e93359e..704f15cc86cc6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/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> { - 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>, ) -> Option> { - 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>)> { // 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. `::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/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs index 1298b38097034..bbf047b8bacfb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/place_op.rs +++ b/src/tools/rust-analyzer/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>> { - 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>, op: PlaceOp, ) -> Option>> { 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>`. 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>, 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/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index b0f916b8c0763..f9ad76b0c12c0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/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>, -} - -pub(crate) struct InferenceTableSnapshot<'db> { - ctxt_snapshot: CombinedSnapshot, - obligations: FulfillmentCtxt<'db>, + pub(super) trait_errors: Vec>, } impl<'db> InferenceTable<'db> { @@ -145,14 +144,10 @@ impl<'db> InferenceTable<'db> { db: &'db dyn HirDatabase, trait_env: ParamEnv<'db>, krate: Crate, - owner: Option, + 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(&mut self, t: T) -> rustc_type_ir::Canonical, T> - where - T: TypeFoldable>, - { - // 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(&mut self, value: T) -> T - where - T: TypeFoldable>, - { - 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>(&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>(&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>>(&self, t: T) -> T { + self.infer_ctxt.resolve_vars_if_possible(t) + } + pub(crate) fn resolve_vars_with_obligations(&mut self, t: T) -> T where T: rustc_type_ir::TypeFoldable>, @@ -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( @@ -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>, { - 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 { - 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) { + 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, + has_errors: bool, + spans_emitted_type_must_be_known_for: FxHashSet, + types: &'db DefaultAny<'db>, + } + + impl<'db> WriteBackCtxt<'db> { + pub(crate) fn new( + table: InferenceTable<'db>, + diagnostics: ThinVec, + vars_emitted_type_must_be_known_for: FxHashSet>, + ) -> 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(&mut self, value_ref: &mut T) + where + T: TypeFoldable>, + { + self.resolve_completely_with_default(value_ref, value_ref.clone()); + } + + pub(crate) fn resolve_completely_with_default(&mut self, value_ref: &mut T, default: T) + where + T: TypeFoldable>, + { + 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, 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> 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>>, @@ -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>>, ) -> Resolver<'a, 'db> { @@ -645,8 +730,9 @@ mod resolve_completely { T: Into> + TypeSuperFoldable> + 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> for Resolver<'cx, 'db> { + impl<'db> TypeFolder> 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/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index 74d66123ea53a..0070d14f373dc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/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> 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 { 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/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs index ae53276f56a5b..19d2d29c9e333 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs +++ b/src/tools/rust-analyzer/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)> { +) -> Option<(Option, Option)> { 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/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 798c62c192405..3e569076ad9ec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 6090ddfd45ccb..b7e1697059633 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/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, Bound) { - 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/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs index 1752b56b0f6d9..26fa73e76bc3c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs +++ b/src/tools/rust-analyzer/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, TargetLoadError> { +) -> Result { 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/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 484ecebba5343..bc18f05790f02 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index d004b5e3ef1d6..91e3b85aa1311 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/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 { +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 - // - >::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>, +pub fn known_const_to_ast<'db>( + konst: Const<'db>, + db: &'db dyn HirDatabase, + target_module: ModuleId, +) -> Option { + Some(make::expr_const_value( + &konst.display_source_code(db, target_module, true).unwrap_or_else(|_| "_".to_owned()), + )) } -impl<'db> rustc_type_ir::TypeVisitor> 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 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> -where - T: ?Sized + rustc_type_ir::TypeVisitable>, -{ - 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 { - 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 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 { + match self { + InferBodyId::DefWithBodyId(DefWithBodyId::FunctionId(it)) => Some(it), + _ => None, + } + } + + #[inline] + pub fn as_variant(self) -> Option { + 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 { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 335aff2c1df16..5b0bcd2be8380 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/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>, + def: ExpressionStoreOwnerId, + generic_def: GenericDefId, + generics: &'a OnceCell>, in_binders: DebruijnIndex, impl_trait_mode: ImplTraitLoweringState, /// Tracks types with explicit `?Sized` bounds. pub(crate) unsized_types: FxHashSet>, - pub(crate) diagnostics: Vec, + pub(crate) diagnostics: ThinVec, 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, + forbid_params_after: Option, + forbid_params_after_reason: ForbidParamsAfterReason, + pub(crate) defined_anon_consts: ThinVec, + 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>, 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> { - 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 { + pub value: T, + info: Option, ThinVec)>>, +} + +impl std::fmt::Debug for TyLoweringResult { + 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 TyLoweringResult { + fn new( + value: T, + mut diagnostics: ThinVec, + mut defined_anon_consts: ThinVec, + ) -> 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>>( 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>; - -pub(crate) fn create_diagnostics(diagnostics: Vec) -> 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>> { - 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>> { + 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>> { 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 { +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 { +#[salsa_macros::tracked(returns(ref))] +pub(crate) fn type_for_const_with_diagnostics( + db: &dyn HirDatabase, + def: ConstId, +) -> TyLoweringResult> { 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 { +#[salsa_macros::tracked(returns(ref))] +pub(crate) fn type_for_static_with_diagnostics( + db: &dyn HirDatabase, + def: StaticId, +) -> TyLoweringResult> { 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> { +) -> Option>> { 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> { +) -> Option>> { 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>> { - 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> { - 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, Diagnostics) { - let type_alias_data = TypeAliasSignature::of(db, t); - let mut diags = None; +) -> TyLoweringResult> { + 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, 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> { + 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, Diagnostics) { - let resolver = impl_id.resolver(db); +) -> TyLoweringResult> { + 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> { + 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, 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 { + &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> { + 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> { + TyLoweringResult::empty(ArenaMap::default()) } pub(crate) fn field_types_query( db: &dyn HirDatabase, variant_id: VariantId, ) -> &ArenaMap> { - &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>, Diagnostics) { +) -> TyLoweringResult>> { 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 { +pub struct TypeAliasBounds { 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>>, 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>> { + 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>, 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 { generic_predicates(db, def) } } @@ -2058,16 +2075,25 @@ 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 { + TyLoweringResult::empty(GenericPredicates::from_explicit_own_predicates( + StoredEarlyBinder::bind(Clauses::default().store()), + )) } impl GenericPredicates { + #[inline] + pub fn empty() -> &'static GenericPredicates { + static EMPTY: OnceLock = 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, @@ -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 { 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::>(); 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>, - 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>]>>); +pub struct GenericDefaults(ThinVec>>); impl GenericDefaults { #[inline] - pub fn get<'db>(&self, idx: usize) -> Option>> { - 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>]); + +impl<'db> GenericDefaultsRef<'db> { + #[inline] + pub fn get(self, idx: usize) -> Option>> { + 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 { + 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::>(); + 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>, bool) { - ctx.lowering_param_default(idx as u32); + ) -> Option> { + 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 { + 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 { - 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> { + 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 { +fn fn_sig_for_fn( + db: &dyn HirDatabase, + def: FunctionId, +) -> TyLoweringResult> { 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 { +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/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs index 009f047109dfb..2565fb46ce8ce 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/diagnostics.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index 4f707321782a2..ff9718af111be 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/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, infer_args: bool, + span: Span, ) -> (Ty<'db>, Option) { 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) { 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, infer_args: bool) -> Ty<'db> { + fn select_associated_type( + &mut self, + res: Option, + 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>, 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, 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) -> 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/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 68c4833d81b01..5e90e371fcd9c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/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, 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( &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>, 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/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index 94c70c29f74bc..c425e69dc59dd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/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` // 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> + 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/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 3604076ccdc77..4b2f0cfd7066b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index a8e06f3a2b586..5f61b1defb8c0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/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; @@ -200,9 +197,10 @@ impl ProjectionElem { } }, 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, pub locals: Arena, pub start_block: BasicBlockId, - pub owner: DefWithBodyId, + pub owner: InferBodyId, pub binding_locals: ArenaMap, pub upvar_locals: FxHashMap>, pub param_locals: Vec, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 17715d3fcd23b..940bc572595e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/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, + owner: Either, pub mutability_of_locals: ArenaMap, pub moved_out_of_ref: Vec, pub partially_moved: Vec, pub borrow_regions: Vec, } -fn all_mir_bodies( - db: &dyn HirDatabase, - def: DefWithBodyId, - mut cb: impl FnMut(Arc) -> BorrowckResult, - mut merge_from_closures: impl FnMut(&mut BorrowckResult, &BorrowckResult), -) -> Result, 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) -> BorrowckResult, + mut merge_from_closures: impl FnMut( + (&mut BorrowckResult, &'db MirBody), + (&BorrowckResult, &'db MirBody), + ), +) -> Result, MirLowerError> { + fn for_closure<'db>( + db: &'db dyn HirDatabase, c: InternedClosureId, - results: &mut Vec, - cb: &mut impl FnMut(Arc) -> BorrowckResult, - merge_from_closures: &mut impl FnMut(&mut BorrowckResult, &BorrowckResult), + results: &mut Vec<(BorrowckResult, &'db MirBody)>, + cb: &mut impl FnMut(&'db MirBody, Either) -> 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, MirLowerError> { + def: InferBodyId, +) -> Result, 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/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 80e429c4c8232..3372f6ec2ed9b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/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, prev_stack_ptr: usize, - span: (MirSpan, DefWithBodyId), + span: (MirSpan, InferBodyId), } #[derive(Clone)] -enum MirOrDynIndex { - Mir(Arc), +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, + target_data_layout: &'db TargetDataLayout, stack: Vec, heap: Vec, - code_stack: Vec, + code_stack: Vec>, /// 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, @@ -190,11 +189,11 @@ pub struct Evaluator<'db> { layout_cache: RefCell, Arc>>, projected_ty_cache: RefCell, PlaceElem), Ty<'db>>>, not_special_fn_cache: RefCell>, - mir_or_dyn_index_cache: RefCell), MirOrDynIndex>>, + mir_or_dyn_index_cache: RefCell), 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>>, + unused_locals_store: RefCell>>>, cached_ptr_size: usize, cached_fn_trait_func: Option, cached_fn_mut_trait_func: Option, @@ -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 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, - Vec<(Either, MirSpan, DefWithBodyId)>, + Vec<(Either, 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 = 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, - body: Arc, + 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, + 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::() { 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>, - ) -> 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)> { 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, + body: &'a MirBody, args: impl Iterator, ) -> 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, ) -> 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, + body: &'a MirBody, destination: Option, - ) -> 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, 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, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { 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> { + ) -> Result<'db, Option>> { 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, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { 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, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { 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, - locals: &Locals, + mir_body: &'a MirBody, + locals: &Locals<'a>, def: FunctionId, arg_bytes: impl Iterator, span: MirSpan, destination: Interval, target_bb: Option, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { 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, span: MirSpan, - ) -> Result<'db, Option> { + ) -> Result<'db, Option>> { 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/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 9586d38abc517..b4a5aa8a87e2c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/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, 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> { 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/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs index e0b3e571b8567..074c5a9c7707a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 0f0ed729c930a..3852db909e392 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/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, labeled_loop_blocks: FxHashMap, discr_temp: Option, @@ -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; 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> { - 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> { +#[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> { + _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, + 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)> + 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/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index fb4a9add818f3..2ed7aedecffef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 99c5f0fc653f5..c924c5bdf0fdf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/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)> { 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/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs index 73399dab7fbd0..8e10284cc1f8a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index 41044f00c2e96..06871a3f18424 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/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> 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, MirLowerError> { +) -> Result { 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, MirLowerError> { +) -> Result { 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, MirLowerError> { +) -> Result { 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 { + Err(MirLowerError::Loop) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index de5ee223a1487..777cf170bc262 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 161a3142df556..47b4b1dc4a3a3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/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, T>; pub type EarlyBinder<'db, T> = rustc_type_ir::EarlyBinder, T>; +pub type Unnormalized<'db, T> = rustc_type_ir::Unnormalized, T>; pub type Canonical<'db, T> = rustc_type_ir::Canonical, T>; pub type CanonicalVarValues<'db> = rustc_type_ir::CanonicalVarValues>; pub type CanonicalVarKind<'db> = rustc_type_ir::CanonicalVarKind>; @@ -55,7 +57,7 @@ pub type CanonicalQueryInput<'db, V> = rustc_type_ir::CanonicalQueryInput = rustc_type_ir::AliasTy>; pub type FnSig<'db> = rustc_type_ir::FnSig>; pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig>>; -pub type TypingMode<'db> = rustc_type_ir::TypingMode>; +pub type TypingMode<'db, S = MayBeErased> = rustc_type_ir::TypingMode, S>; pub type TypeError<'db> = rustc_type_ir::error::TypeError>; pub type QueryResult<'db> = rustc_type_ir::solve::QueryResult>; pub type FxIndexMap = rustc_type_ir::data_structures::IndexMap; @@ -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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs index 1813abab86e7b..3121d5631f3f8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs +++ b/src/tools/rust-analyzer/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> for Safety { Self::Safe => "", } } + + fn unsafe_mode() -> Self { + Safety::Unsafe + } +} + +impl<'db> TypeVisitable> for ExternAbi { + fn visit_with>>(&self, _visitor: &mut V) -> V::Result { + V::Result::output() + } +} + +impl<'db> TypeFoldable> for ExternAbi { + fn try_fold_with>>( + self, + _folder: &mut F, + ) -> Result { + Ok(self) + } + + fn fold_with>>(self, _folder: &mut F) -> Self { + self + } } -impl<'db> Relate> for FnAbi { +impl<'db> Relate> for ExternAbi { fn relate>>( _relation: &mut R, a: Self, @@ -55,13 +81,3 @@ impl<'db> Relate> for FnAbi { } } } - -impl<'db> rustc_type_ir::inherent::Abi> for FnAbi { - fn rust() -> Self { - FnAbi::Rust - } - - fn is_rust(self) -> bool { - matches!(self, FnAbi::Rust) - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs index 3645f8096cfd8..95f437165d045 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/binder.rs +++ b/src/tools/rust-analyzer/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 { } } +impl StoredEarlyBinder { + #[inline] + pub fn get<'db>(&self) -> EarlyBinder<'db, GenericArg<'db>> { + self.get_with(|it| it.as_ref()) + } +} + impl StoredEarlyBinder { #[inline] pub fn get<'db>(&self) -> EarlyBinder<'db, Clauses<'db>> { @@ -45,13 +53,25 @@ impl StoredEarlyBinder { } } +impl StoredEarlyBinder { + #[inline] + pub fn get<'db>(&'db self) -> EarlyBinder<'db, PolyFnSig<'db>> { + self.get_with(|it| it.get()) + } +} + +impl StoredEarlyBinder { + #[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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index fa90e3d8a004b..2df9a5259ddfb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/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>` 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 542eca3ded243..63337b297dc7a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/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 for SolverDefId { } } +impl From for SolverDefId { + #[inline] + fn from(value: InferBodyId) -> Self { + match value { + InferBodyId::DefWithBodyId(id) => id.into(), + InferBodyId::AnonConstId(id) => id.into(), + } + } +} + impl From for SolverDefId { #[inline] fn from(value: VariantId) -> Self { @@ -246,6 +260,32 @@ impl TryFrom for DefWithBodyId { } } +impl TryFrom for InferBodyId { + type Error = (); + + #[inline] + fn try_from(value: SolverDefId) -> Result { + 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 for GenericDefId { type Error = (); @@ -270,26 +310,6 @@ impl TryFrom 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> for SolverDefId { fn as_local(self) -> Option { Some(self) @@ -301,6 +321,26 @@ impl<'db> inherent::DefId> 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 for $name { + type Error = (); + + #[inline] + fn try_from(value: SolverDefId) -> Result { + 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 for $name { + impl<'db> inherent::DefId, $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 for $term_id_name { type Error = (); #[inline] fn try_from(value: SolverDefId) -> Result { 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> for $name { - fn as_local(self) -> Option { - 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 { + 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 { + 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 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/format_proof_tree.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/format_proof_tree.rs index 66da6d540082d..4b2f66a17d519 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/format_proof_tree.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index 6739795a0060d..33dd33cb1d806 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/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>, ) { - // 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> { - // 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>, } 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> for StalledOnCoroutines<'_, 'db> { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum NextSolverError<'db> { TrueError(PredicateObligation<'db>), Ambiguity(PredicateObligation<'db>), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 72cf2f9f07f20..51f070cd64e8c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/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> for StoredGenericArg { + fn visit_with>>(&self, visitor: &mut V) -> V::Result { + self.as_ref().visit_with(visitor) + } +} + +impl<'db> TypeFoldable> for StoredGenericArg { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + + fn fold_with>>(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> for GenericArg<'db> {} +impl<'db> TypeVisitable> for StoredGenericArgs { + fn visit_with>>(&self, visitor: &mut V) -> V::Result { + self.as_ref().visit_with(visitor) + } +} + +impl<'db> TypeFoldable> for StoredGenericArgs { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + + fn fold_with>>(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, N> { + fn push(&mut self, arg: GenericArg<'db>) { + self.push(arg); + } +} + +impl<'db> GenericArgsBuilder<'db> for Vec> { + fn push(&mut self, arg: GenericArg<'db>) { + self.push(arg); + } +} + impl<'db> GenericArgs<'db> { + #[inline(always)] + fn fill_builder( + 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(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( 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( - 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(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> { self.iter().filter_map(|it| it.as_type()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs index f31de21796fe3..a798582cb9891 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs +++ b/src/tools/rust-analyzer/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, - pub parent_count: usize, - pub own_params: Vec, +pub struct Generics<'db> { + generics: crate::generics::Generics<'db>, + /// This is used for builtin derives, specifically `CoercePointee`. + additional_param: Option, } -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 { + self.generics.iter_id().chain(self.additional_param) } } -impl<'db> rustc_type_ir::inherent::GenericsOf> for Generics { +impl<'db> rustc_type_ir::inherent::GenericsOf> 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs index dc0b584084e88..f63200a2e08cc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs +++ b/src/tools/rust-analyzer/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> { 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> { 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs index 1738552a8e015..6e2037550097c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs index 1fefc0f265c55..a5e29e7836c49 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs +++ b/src/tools/rust-analyzer/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( &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>, 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs index 397986e2edd3b..1e2a3ff0c2b33 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs +++ b/src/tools/rust-analyzer/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> { - self.typing_mode() + fn disable_trait_solver_fast_paths(&self) -> bool { + false + } + + fn typing_mode_raw(&self) -> TypingMode> { + 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) -> ::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> + 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> + 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/errors.rs new file mode 100644 index 0000000000000..7d3f111f668fa --- /dev/null +++ b/src/tools/rust-analyzer/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>, TypeError<'db>), // always comes from a SubtypePredicate + ConstEquate(ExpectedFound>, 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, + }, +} + +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) -> >::Result, + ) -> >::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> { + 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> { + 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> { + 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> { + 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> { + 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> { + 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>; + + 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 Trait for for<..> fn(..A) -> R` was written + // instead of `impl 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>, + cause: ObligationCause, + _idx: usize, + _parent_trait_pred: PolyTraitPredicate<'db>, +) -> ObligationCause { + cause +} + +fn derive_host_cause<'db>( + _interner: DbInterner<'db>, + _candidate_kind: inspect::ProbeKind>, + cause: ObligationCause, + _idx: usize, + _parent_host_pred: Binder<'db, HostEffectPredicate<'db>>, +) -> ObligationCause { + cause +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index a6352c7899fff..839bdf17e7589 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/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( mut self, + span: Span, input: &CanonicalQueryInput<'db, T>, ) -> (InferCtxt<'db>, T, CanonicalVarValues<'db>) where T: TypeFoldable>, { 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>>, + 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::::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>, ) -> 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) -> 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, 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 { + 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( &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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs index 7bb39519f50ac..544d79daf0a3d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs +++ b/src/tools/rust-analyzer/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, 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::from(value_count)..RegionVid::from(self.storage.unification_table.len()) + pub fn vars_since_snapshot(&self, value_count: usize) -> (Range, Vec) { + 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs index d621dd4906e81..e55e43a4cdb32 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs +++ b/src/tools/rust-analyzer/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> + Relate>>( &self, + span: Span, structurally_relate_aliases: StructurallyRelateAliases, target_vid: impl Into, 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> 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> 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> 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> 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs index 3522827a9e959..f3af697febcb2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs +++ b/src/tools/rust-analyzer/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> 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> 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> 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> for LatticeOp<'_, 'db> { preds: impl IntoIterator, 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>>) { 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs index bd407fd15718a..d6f0379c111a2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs +++ b/src/tools/rust-analyzer/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>>; + 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> { +fn to_selection<'db>(span: Span, cand: InspectCandidate<'_, 'db>) -> Option> { if let Certainty::Maybe { .. } = cand.shallow_certainty() { return None; } @@ -376,7 +386,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option> 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> // 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs index 5902f8043b5ea..7cb3ab09d4b28 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs +++ b/src/tools/rust-analyzer/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, Vec) { +) -> (Range, Vec) { 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, - type_vars: (Range, Vec), + region_vars: (Range, Vec), + type_vars: (Range, Vec), int_vars: Range, float_vars: Range, - const_vars: (Range, Vec), + const_vars: (Range, Vec), } 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> 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> 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> 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs index 705aa43fb1199..39c8a37adbdbd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -47,7 +47,7 @@ impl<'db> InferCtxt<'db> { UndoLogs::>::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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index dde623483642f..4584b35796456 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/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>(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> 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> 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> { 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, 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, 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, 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs index 29e7b883c93bf..6b3936ba80428 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs +++ b/src/tools/rust-analyzer/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> for TypeVariableStorage<'tcx> { #[derive(Debug, Clone, Default)] pub(crate) struct TypeVariableStorage<'db> { - /// The origins of each type variable. - values: IndexVec, /// 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, -} - -#[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> { 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, Vec) { + pub(crate) fn vars_since_snapshot(&mut self, value_count: usize) -> (Range, Vec) { 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 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 { + 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs index a09f65f082d97..061b8531d3b33 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs +++ b/src/tools/rust-analyzer/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 }, + 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index 63a225b98f9fd..7e2dfb7112d3b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/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> { + pub(crate) fn instantiate_nested_goals(&self, span: Span) -> Vec> { 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index cfb55e2e00a05..b3d31dd40bf9e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/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> 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)> { - 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, - /// The error guarantees from parser, if any. - tainted: Option, - /// 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(&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::() == 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> 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, 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> for AdtDef { db.field_types(id).iter().map(|(_, ty)| ty.get().skip_binder()).collect::>() }; 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> for AdtDef { } fn destructor(self, interner: DbInterner<'db>) -> Option { - 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> 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> 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, - ) -> 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::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 { - 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 { + 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 { 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 { - 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, + } + + 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, - _def_id: Self::DefId, + _kind: impl Into>, ) -> Option { None } - fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) -> EarlyBinder { - 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 { + 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> { + 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>, @@ -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>, { - 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs index 5dd372a367563..925640e1f6799 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs +++ b/src/tools/rust-analyzer/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> for DbInterner<'db> { } fn print_debug(t: &ty::AliasTerm, 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> 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> 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs index 5d8f3fe5194aa..152b58baeb6de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs +++ b/src/tools/rust-analyzer/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> 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>>( infcx: &InferCtxt<'db>, param_env: ParamEnv<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 8658d03a9e3e8..30738dea5c958 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -31,6 +31,7 @@ pub type ExistentialPredicate<'db> = ty::ExistentialPredicate>; pub type ExistentialTraitRef<'db> = ty::ExistentialTraitRef>; pub type ExistentialProjection<'db> = ty::ExistentialProjection>; pub type TraitPredicate<'db> = ty::TraitPredicate>; +pub type HostEffectPredicate<'db> = ty::HostEffectPredicate>; pub type ClauseKind<'db> = ty::ClauseKind>; pub type PredicateKind<'db> = ty::PredicateKind>; pub type NormalizesTo<'db> = ty::NormalizesTo>; @@ -705,34 +706,6 @@ impl<'db> rustc_type_ir::inherent::Predicate> 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> 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs index 3f0aebac2dea7..72a25f4df6da4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs +++ b/src/tools/rust-analyzer/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>; +pub type RegionConstraint<'db> = rustc_type_ir::RegionConstraint>; #[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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index d45ac6c959695..b1b3a0e0dc78a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/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, { - 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, - ::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: ::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>>, ) { 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, - trait_assoc_def_id: SolverDefId, + trait_assoc_def_id: TraitAssocTermId, impl_id: AnyImplId, - ) -> Result, ErrorGuaranteed> { + ) -> FetchEligibleAssocItemResponse { 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs index 00c3708358b92..0dbd97a6366e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs @@ -30,18 +30,18 @@ impl<'db> At<'_, 'db> { ) -> Result, Vec>> { 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 39abdaf079b63..c43e04b9d0ca4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/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; pub type TyKind<'db> = rustc_type_ir::TyKind>; pub type FnHeader<'db> = rustc_type_ir::FnHeader>; +pub type AliasTyKind<'db> = rustc_type_ir::AliasTyKind>; +pub type AliasTermKind<'db> = rustc_type_ir::AliasTermKind>; +pub type FnSigKind<'db> = rustc_type_ir::FnSigKind>; #[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>> { 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> { 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>>(t: &T) -> bool { - t.references_error() && t.visit_with(&mut ReferencesNonLifetimeError).is_break() -} - -struct ReferencesNonLifetimeError; - -impl<'db> TypeVisitor> 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>>(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> 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> for Ty<'db> { } } +impl<'db> TypeVisitable> for StoredTy { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.as_ref().visit_with(visitor) + } +} + impl<'db> TypeSuperVisitable> for Ty<'db> { fn super_visit_with>>( &self, @@ -913,6 +959,18 @@ impl<'db> TypeFoldable> for Ty<'db> { } } +impl<'db> TypeFoldable> for StoredTy { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(self.as_ref().try_fold_with(folder)?.store()) + } + fn fold_with>>(self, folder: &mut F) -> Self { + self.as_ref().fold_with(folder).store() + } +} + impl<'db> TypeSuperFoldable> for Ty<'db> { fn try_super_fold_with>>( self, @@ -1294,9 +1352,11 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { false } - fn discriminant_ty(self, interner: 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/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 858233cb2c900..fb3bd18bf48ac 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/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> { clauses.into_iter().map(move |clause| Obligation { - cause: cause.clone(), + cause, param_env, predicate: clause.as_predicate(), recursion_depth: 0, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs index 2e85beea9163d..4244b1bac443b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/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, ) { - 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> { 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> { // 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/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs index bae204c4ef7ca..0b7dc4d30954f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs +++ b/src/tools/rust-analyzer/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::>(); 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/src/tools/rust-analyzer/crates/hir-ty/src/solver_errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/solver_errors.rs new file mode 100644 index 0000000000000..e4e76fa67bacb --- /dev/null +++ b/src/tools/rust-analyzer/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, + }, +} + +#[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 { + 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 { + 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/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs index 8bc6c51fae6f0..467b598447d6e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 430a570444d8a..d259ce7963f65 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/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, Ty<'_>)> = Vec::new(); - let mut mismatches: Vec<(InFile, &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::>(); + let mut mismatches: Vec<(InFile, (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/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 5324d8c605495..3942a7ae27afa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/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 &' 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 &' Foo"], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index a80ce5002deab..db06d55278b2b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -391,6 +391,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( @@ -878,11 +906,11 @@ struct V { 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(x: [T; N]) -> Vec { + 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/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs index f257aa1b6e602..94ca88088ddc5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 960155a8e4f93..4c26b191208ca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index fc8c1f8164801..4291c9ba18dc7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 993293bb56857..91273cd177e8f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index d6bc03f57dee0..e719f43e74b7b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/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) { +fn test() { if let 1..76 = 2u32 {} if let 1..=76 = 2u32 {} } "#, expect![[r#" - 8..9 'x': Range - 8..12 'x..y': Range - 11..12 'y': Range - 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); @@ -835,12 +830,13 @@ fn box_pattern() { } "#, expect![[r#" - 52..58 'params': Box - 70..124 '{ ... } }': () - 76..122 'match ... }': () - 82..88 'params': Box - 99..110 'box integer': Box - 114..116 '{}': () + 77..83 'params': Box + 95..149 '{ ... } }': () + 101..147 'match ... }': () + 107..113 'params': Box + 124..135 'box integer': Box + 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); +impl Deref for Box { + type Target = T; + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl DerefPure for Box {} + +pub struct Foo(T); +impl Deref for Foo { + type Target = [T]; + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl DerefPure for Foo {} + +fn foo(v: &Box>) { + match v { + deref!(deref!(inner)) => {} + _ => {} + } +} + "#, + expect![[r#" + 142..146 'self': &'? Box + 165..188 '{ ... }': &'? T + 175..182 'loop {}': ! + 180..182 '{}': () + 310..314 'self': &'? Foo + 333..356 '{ ... }': &'? [T] + 343..350 'loop {}': ! + 348..350 '{}': () + !0..20 'builti...inner)': Foo + !0..28 'builti...nner))': Box> + !14..19 'inner': &'? [i32] + 399..400 'v': &'? Box> + 418..493 '{ ... } }': () + 424..491 'match ... }': () + 430..431 'v': &'? Box> + 467..469 '{}': () + 478..479 '_': &'? Box> + 483..485 '{}': () + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index e30fa779dac25..5a90e700acac9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/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() -> Bar {} "#, expect![[r#" - 64..66 '{}': () + 64..66 '{}': impl Foo + ?Sized "#]], ); } @@ -2363,7 +2367,6 @@ fn test() { } "#, expect![[r#" - 46..49 'Foo': Foo 93..97 'self': Foo 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/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 565360dc25680..33a12fcd1ee1e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -34,7 +34,6 @@ impl Space for [u8; 1] { 223..227 'iter': IntoIter 230..231 'a': Vec 230..243 'a.into_iter()': IntoIter - 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 + 182..186 'self': Between 188..192 '_sep': &'? str - 200..206 '_other': Between - 222..242 '{ ... }': Between - 232..236 'self': Between + 200..206 '_other': Between + 222..242 '{ ... }': Between + 232..236 'self': Between 300..304 'self': Self - 343..372 '{ ... }': Between - 353..360 'Between': fn Between(Self) -> Between - 353..366 'Between(self)': Between + 343..372 '{ ... }': Between + 353..360 'Between': fn Between(Self) -> Between + 353..366 'Between(self)': Between 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/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 3ea21f8265a5f..c0b8d93b47e32 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/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#" @@ -2065,6 +2066,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 + 63..93 'gen { ... }': impl Iterator + 77..86 'yield 1i8': () + 83..86 '1i8': i8 + 103..109 'result': Option + 112..121 'generator': impl Iterator + 112..128 'genera...next()': Option + "#]], + ); +} + +#[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 + 138..174 'async ... }': impl AsyncIterator + 158..167 'yield 1i8': () + 164..167 '1i8': i8 + 184..190 'result': Poll> + 193..201 'Pin::new': fn new<&'? mut impl AsyncIterator>(&'? mut impl AsyncIterator) -> Pin<&'? mut impl AsyncIterator> + 193..217 'Pin::n...rator)': Pin<&'? mut impl AsyncIterator> + 193..236 'Pin::n...ut cx)': Poll> + 202..216 '&mut generator': &'? mut impl AsyncIterator + 207..216 'generator': impl AsyncIterator + 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 + 96..100 'html': fn html() -> impl Iterator + 96..102 'html()': impl Iterator + 112..118 'result': Option<()> + 121..130 'generator': impl Iterator + 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 + 177..181 'html': fn html() -> impl AsyncIterator + 177..183 'html()': impl AsyncIterator + 193..199 'result': Poll> + 202..210 'Pin::new': fn new<&'? mut impl AsyncIterator>(&'? mut impl AsyncIterator) -> Pin<&'? mut impl AsyncIterator> + 202..226 'Pin::n...rator)': Pin<&'? mut impl AsyncIterator> + 202..245 'Pin::n...ut cx)': Poll> + 211..225 '&mut generator': &'? mut impl AsyncIterator + 216..225 'generator': impl AsyncIterator + 237..244 '&mut cx': &'? mut Context<'?> + 242..244 'cx': Context<'?> + "#]], + ); +} + #[test] fn tuple_pattern_nested_match_ergonomics() { check_no_mismatches( @@ -2271,6 +2404,7 @@ fn infer_generic_from_later_assignment() { 89..127 'loop {... }': ! 94..127 '{ ... }': () 104..107 'end': Option + 104..107 'end': Option 104..120 'end = ...(true)': () 110..114 'Some': fn Some(bool) -> Option 110..120 'Some(true)': Option @@ -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> { t: T } @@ -2580,7 +2713,7 @@ fn generic_default_depending_on_other_type_arg() { 83..130 '{ ...2 }; }': () 89..91 't1': Thing u32> 97..99 't2': Thing u128> - 105..127 'Thing:...1u32 }': Thing {unknown}> + 105..127 'Thing:...1u32 }': Thing 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)) @@ -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/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 278666ef35923..ea978cde58c19 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -123,6 +123,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>, got i32 + }; + let a = f(4); + a; +// ^ type: impl AsyncIterator +} +"#, + ); +} + #[test] fn auto_sized_async_block() { check_no_mismatches( @@ -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 "#]], ) } @@ -4910,6 +4923,7 @@ async fn baz i32>(c: T) { } "#, expect![[r#" + 37..38 'a': T 37..38 'a': T 43..83 '{ ...ait; }': () 53..57 'fut1': >::CallRefFuture<'?> @@ -4919,6 +4933,7 @@ async fn baz i32>(c: T) { 70..74 'fut1': >::CallRefFuture<'?> 70..80 'fut1.await': i32 124..129 'mut b': T + 124..129 'mut b': T 134..174 '{ ...ait; }': () 144..148 'fut2': >::CallRefFuture<'?> 151..152 'b': T @@ -4927,6 +4942,7 @@ async fn baz i32>(c: T) { 161..165 'fut2': >::CallRefFuture<'?> 161..171 'fut2.await': i32 216..217 'c': T + 216..217 'c': T 222..262 '{ ...ait; }': () 232..236 'fut3': >::CallOnceFuture 239..240 'c': T diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 878696c721295..ad1c3fb709c9d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs b/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs index 48f3c803d8322..6dcd8b59a558a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/upvars.rs +++ b/src/tools/rust-analyzer/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>> { 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/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index ae9b2c4618960..764d02ccf1d88 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index a88457e3c745b..7eee78b8c4419 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/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 { //~ ERROR [T: o] } "#, expect![[r#" - Foo[T: bivariant] + Foo[T: invariant] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 223103b6e5d26..f9cf05e73a103 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 6cfb79d5a1f4b..a044f24587bae 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/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> for SpanAst { + fn from(value: Either) -> 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 { + 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>; + 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)] @@ -211,6 +270,12 @@ pub struct NoSuchField { pub variant: VariantId, } +#[derive(Debug)] +pub struct DuplicateField { + pub field: InFile>>, + pub variant: Variant, +} + #[derive(Debug)] pub struct PrivateAssocItem { pub expr_or_pat: InFile, @@ -224,12 +289,31 @@ pub struct MismatchedTupleStructPatArgCount { pub found: usize, } +#[derive(Debug)] +pub struct MismatchedArrayPatLen { + pub pat: InFile, + pub expected: u128, + pub found: u128, + pub has_rest: bool, +} + +#[derive(Debug)] +pub struct ExpectedArrayOrSlicePat<'db> { + pub pat: InFile, + pub found: Type<'db>, +} + #[derive(Debug)] pub struct ExpectedFunction<'db> { pub call: InFile, pub found: Type<'db>, } +#[derive(Debug)] +pub struct FunctionalRecordUpdateOnNonStruct { + pub base_expr: InFile, +} + #[derive(Debug)] pub struct UnresolvedField<'db> { pub expr: InFile, @@ -311,6 +395,11 @@ pub struct NonExhaustiveLet { pub uncovered_patterns: String, } +#[derive(Debug)] +pub struct NonExhaustiveRecordExpr { + pub expr: InFile, +} + #[derive(Debug)] pub struct TypeMismatch<'db> { pub expr_or_pat: InFile, @@ -385,6 +474,12 @@ pub struct RemoveUnnecessaryElse { pub if_expr: InFile>, } +#[derive(Debug)] +pub struct UnusedMustUse<'db> { + pub expr: InFile, + pub message: Option<&'db str>, +} + #[derive(Debug)] pub struct CastToUnsized<'db> { pub expr: InFile, @@ -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, 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>, +} + +#[derive(Debug)] +pub struct UnionExprMustHaveExactlyOneField { + pub expr: InFile, +} + +#[derive(Debug)] +pub struct InvalidLhsOfAssignment { + pub lhs: InFile>>, +} + +#[derive(Debug)] +pub struct PatternArgInExternFn { + pub node: InFile>, +} + +#[derive(Debug)] +pub struct UnimplementedTrait<'db> { + pub span: SpanSyntax, + pub trait_predicate: crate::TraitPredicate<'db>, + pub root_trait_predicate: Option>, +} + 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> { 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::() { - 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::() + { + 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::() { - return Some( - RemoveTrailingReturn { - return_expr: InFile::new(source_ptr.file_id, ptr), - } + && let Some(ptr) = source_ptr.value.cast::() + { + 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> { 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 -> 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 -> 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> { + 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/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 53f24713cdccc..a71851ea8ceff 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/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` 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/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs index 0a48be5473d2e..219eb9c3b9268 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -254,14 +254,9 @@ impl TryFrom 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/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index 45c9811cc0158..9bff8bda3a96d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index d24e2c0cb5837..63b834a8d1d59 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/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 { + pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result, 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 { + pub fn layout<'db>(self, db: &'db dyn HirDatabase) -> Result, 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 { + pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result, 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 { + pub fn layout<'db>(self, db: &'db dyn HirDatabase) -> Result, 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, 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 -> 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 = 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, 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, 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 { + 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 { 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> { - 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> { 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 + 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>> = match adt_def.def_id().0 { + let variants: Vec>> = 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> { 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> { 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 `::IntoIter` and returns the resulting type pub fn into_iterator_iter(self, db: &'db dyn HirDatabase) -> Option> { 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 { + pub fn layout(&self, db: &'db dyn HirDatabase) -> Result, 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, Arc); +pub struct Layout<'db>(Arc, &'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 { - Some(self.0.largest_niche?.available(&*self.1)) + Some(self.0.largest_niche?.available(self.1)) } pub fn field_offset(&self, field: Field) -> Option { @@ -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>, - diagnostics: Option>, + 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/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index b7cc780ae42f7..a1bbe47188b46 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/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; +type ExprToAnonConst = FxHashMap; +type DefAnonConstsMap = FxHashMap; + pub struct SemanticsImpl<'db> { pub db: &'db dyn HirDatabase, - s2d_cache: RefCell, + s2d_cache: RefCell>, /// MacroCall to its expansion's MacroCallId cache macro_call_cache: RefCell, MacroCallId>>, + /// All anon consts defined by a *signature* (not a body). + signature_anon_consts_cache: RefCell, } impl fmt::Debug for Semantics<'_, DB> { @@ -455,7 +461,12 @@ impl 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) -> SyntaxNode { + ptr.value.to_node(&self.parse_or_expand(ptr.file_id)) + } + + pub fn to_node(&self, ptr: InFile>) -> N { + ptr.value.to_node(&self.parse_or_expand(ptr.file_id)) + } + pub fn expand(&self, file_id: MacroCallId) -> ExpandResult { - 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> { 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 { - // 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> { + 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>, + ) { + 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::() + { + 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 { 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 { 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> { - 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> { 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 { + 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 { + 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> { 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> { 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 { 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 { + 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, 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/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index d932198b43a7d..583b3e4bdace1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/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, + expansion_info_cache: FxHashMap>, pub(super) file_to_def_cache: FxHashMap>, pub(super) included_file_cache: FxHashMap>, /// Rootnode to HirFileId cache pub(super) root_to_file_cache: FxHashMap, } -impl SourceToDefCache { +impl<'db> SourceToDefCache<'db> { pub(super) fn cache( root_to_file_cache: &mut FxHashMap, 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 { 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/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 6c43f80ce8789..06182620c86e2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/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>, + pub(crate) infer_body: Option, } #[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, ) -> 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, ) -> 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, @@ -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, @@ -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 { @@ -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> { 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 { 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> { - 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, Function>, Option>)> { - 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 { - 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 { + 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 { 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 { - 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, - 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> { 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 { +) -> 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 { +) -> 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 { +) -> 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, path: &Path, hygiene: HygieneId, store: Option<&ExpressionStore>, ) -> Option { - 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, 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, + infer_body: Option, path: &Path, hygiene: HygieneId, ) -> Option { 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/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index f2dc1ce798ad9..af2371d49349d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/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> { +pub fn term_search<'db, DB: HirDatabase>(ctx: &TermSearchCtx<'_, 'db, DB>) -> Vec> { let module = ctx.scope.module(); let mut defs = FxHashSet::default(); defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(module))); diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index c7ef4e5d5deb0..8700326e17b37 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/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, lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator> + 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, lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator> + 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, 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, 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, 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, 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, lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator> + 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, 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, lookup: &'lt mut LookupTable<'db>, should_continue: &'a dyn std::ops::Fn() -> bool, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs index 207a7548f49b5..d8e097f0e27b7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_context.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs index c5ec88ffb88a3..4bd987a3710ac 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_braces.rs +++ b/src/tools/rust-analyzer/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::>(); let (parent_type, body) = if let Some(eq_token) = ctx.find_token_syntax_at_offset(T![=]) { let parent = eq_token.parent()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs index 1809b8f305b62..40d48bee3ccfe 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs index 75c5f84b85012..db9f9dc074be4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs +++ b/src/tools/rust-analyzer/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::()?; let enum_def = ctx.sema.to_def(&enum_node)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs index 0dd01b67e8f47..e543005e67a4d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs +++ b/src/tools/rust-analyzer/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::>()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs index 41e9b6cc84539..b2194ab3dcd71 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/src/tools/rust-analyzer/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::()?; 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 { fn insert_label_after_token( editor: &SyntaxEditor, token: &SyntaxToken, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, builder: &mut SourceChangeBuilder, ) { let make = editor.make(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs index 265ee3d2d4e71..dc847dcdbe7d7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_lifetime_to_type.rs +++ b/src/tools/rust-analyzer/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::()?; if ref_type_focused.lifetime().is_some_and(|lifetime| lifetime.text() != "'_") { return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index d1f1f9f123387..efbe3817e12f0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 3c33ddec31624..667a1d7813c5c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/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::()?; 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> { 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs index 7934a80bfabbc..e7203a96bb218 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs +++ b/src/tools/rust-analyzer/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::>()?; let (fn_type, tail_expr, return_type_range, action) = match node { Either::Left(closure) => { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs index dcd2124f7bebc..53205910c8e35 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/src/tools/rust-analyzer/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::().map(Either::Left).or_else(|| { let callable_expr = ctx.find_node_at_offset::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index b87a757047ac5..10262445a2dfe 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/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; @@ -393,6 +407,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( @@ -643,6 +677,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( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index f9d618790c6e8..ac0bae7cd9b1a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/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>)> { // Deduplicate this with the `expected_type_and_name` logic for completions let expected = |expr_or_pat: Either| 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs index 50e4a367e9a1b..5d3d8127baeff 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs index 7119d5b9c23eb..f17197a75055a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs +++ b/src/tools/rust-analyzer/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::() { 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index a2a71bcba6baa..5ecc11ccc526a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/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::()?; 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::()?; let mcall = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; let receiver = mcall.receiver()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index e88778a62e193..4c3168219fc63 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/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 { +fn find_bool_node(ctx: &AssistContext<'_, '_>) -> Option { let name = ctx.find_node_at_offset::()?; 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, 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 { /// 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_char_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_char_literal.rs index 0a50ba86ba6fb..b23525665a73f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_char_literal.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index acade433978ce..e6d31b966099c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/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::()?; if ctx.find_node_at_offset::() != 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::>>()?; 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::>(); 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, closure: &ast::ClosureExpr, ) -> (Option, Option) { @@ -472,11 +476,11 @@ fn compute_closure_type_params( })) .collect::>(); 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs index f242fe831447a..d950b6df214a3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs +++ b/src/tools/rust-analyzer/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::()?; // Only allow comments which are alone on their line if let Some(prev) = comment.syntax().prev_token() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs index 187cc74306e25..11a3c64188d49 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs +++ b/src/tools/rust-analyzer/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::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs index 9eb4c0584b362..d45c4292b8ed6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_for_to_while_let.rs +++ b/src/tools/rust-analyzer/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::()?; let iterable = for_loop.iterable()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index 18f3ced414026..3e11ee804a6bb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/src/tools/rust-analyzer/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::()?; let trait_ty = impl_.trait_()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs index bc76ade97f69d..c92ad2a7c95ad 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs index e330102423517..a01a66e7b1a52 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/src/tools/rust-analyzer/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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index cc5cc490f1bc1..9506a963183eb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/src/tools/rust-analyzer/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::()?; @@ -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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 1ae12390eedbf..07e12f0320be7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index bc49acc1ef356..a93ab138e5f0e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/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::>(); @@ -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> { +fn find_extracted_variable(ctx: &AssistContext<'_, '_>, arm: &ast::MatchArm) -> Option> { 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 5b691dba5ea76..72418f155962e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/src/tools/rust-analyzer/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, 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, ) { @@ -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( - 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, ) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs index c0fd69779aeae..d0e82d69eb51e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs +++ b/src/tools/rust-analyzer/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::()?; let function = name.syntax().parent().and_then(ast::Fn::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs index c83f8b076551b..7026b5bafdc7c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 791a6a26af381..74392ca7c14f5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/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::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 { } struct ElseBlock<'db> { - exist_else_block: Option, + exist_else_branch: Option, 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, + exist_else_branch: Option, parent_container: &SyntaxNode, ) -> Option { - 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 { + 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}; @@ -1024,6 +1052,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( @@ -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 { @@ -1133,6 +1199,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( @@ -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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 0af0cbc32a988..46e2917f72f17 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/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::()?; 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> { } 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index afbcf024b9fd4..a6a47d78a8e0c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/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::() @@ -140,7 +140,7 @@ fn edit_struct_def( } fn edit_struct_references( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, edit: &mut SourceChangeBuilder, strukt: Either, 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, names: &[ast::Name], diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs index 1af5db17f0400..73b373dbcb4e1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs +++ b/src/tools/rust-analyzer/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::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs index 793e7465c11ae..94920569a080e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_while_to_loop.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 9ffce445d1a1e..2b5b2ca13bb49 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/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::()?; 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 { +fn collect_data(target: Target, ctx: &AssistContext<'_, '_>) -> Option { 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, + ctx: &AssistContext<'_, '_>, target: &Target, usages: &[FileReference], ) -> Option> { @@ -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) -> Sm } fn update_usages( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, editor: &SyntaxEditor, data: &StructEditData, field_names: &FxHashMap, @@ -367,7 +373,7 @@ fn update_usages( } fn build_usage_edit( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, data: &StructEditData, usage: &FileReference, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 291605056b3c6..2755962362677 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/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::()?; @@ -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 { +fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_, '_>) -> Option { 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>, } 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> { @@ -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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs index 74bb0ba3f6020..e6784a0c3b397 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs +++ b/src/tools/rust-analyzer/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::()?; // Only allow doc comments let placement = comment.kind().doc?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs index fc894f0fe9a0f..5e1cd7700da36 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_try_expr.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs index 1c8cbf5af5941..f5d2a6cfc5198 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/src/tools/rust-analyzer/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 { + fn from_scope_def( + ctx: &AssistContext<'_, '_>, + name: Name, + scope_def: ScopeDef, + ) -> Option { match scope_def { ScopeDef::ModuleDef(def) => Some(Ref { visible_name: name, @@ -267,7 +272,7 @@ impl Ref { struct Refs(Vec); 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 { +fn find_imported_defs(ctx: &AssistContext<'_, '_>, use_item: Use) -> Vec { [Direction::Prev, Direction::Next] .into_iter() .flat_map(|dir| { @@ -439,6 +449,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( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index dc4976e8c29d7..4aa11b4e03c85 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index c87ded9dc47b7..178477c74605f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/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::()?; let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 4219e6845fad5..44123dc20dee0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/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 { + fn self_param_adt(&self, ctx: &AssistContext<'_, '_>) -> Option { 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> { 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 { + fn make_result_handler(&self, make: &SyntaxFactory, expr: Option) -> 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> { + fn expr_ty(&self, ctx: &AssistContext<'_, 'db>) -> Option> { 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, @@ -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> { + fn return_ty<'db>(&self, ctx: &AssistContext<'_, 'db>) -> Option> { 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 + '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> { 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, ) -> Vec> { @@ -1163,7 +1167,7 @@ fn generic_parents(parent: &SyntaxNode) -> Vec { /// 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 { +fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option { 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 { - 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 { 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, Option) { 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 { 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 { @@ -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 { +fn resolved_type_param(ctx: &AssistContext<'_, '_>, pred: &ast::WherePred) -> Option { 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 { + fn type_params(&self, ctx: &AssistContext<'_, 'db>) -> Vec { 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 ast::Param> { + fn make_this_param(&self) -> Option 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 { + fn make_ret_ty( + &self, + make: &SyntaxFactory, + ctx: &AssistContext<'_, 'db>, + module: hir::Module, + ) -> Option { 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::>(); - 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 = - block.tail_expr().map(|expr| make::expr_stmt(expr).into()); + block.tail_expr().map(|expr| make.expr_stmt(expr).into()); let mut elements: Vec = 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, 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, params: &[Param<'_>], - syntax: &SyntaxNode, -) -> SyntaxNode { +) { let mut usages_for_param: Vec<(&Param<'_>, Vec)> = Vec::new(); let mut usages_for_self_param: Vec = 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, + make: &SyntaxFactory, ) -> Option { 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index dcbeaefa21f4b..9e06a17337d8e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/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>, Vec, FxHashMap) { @@ -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>, @@ -449,7 +449,7 @@ impl Module { fn resolve_imports( &mut self, module: Option, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Vec { 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, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option { //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, selection_range: TextRange, curr_file_id: FileId, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 21013e2e614c7..50ce8d9b4d5ac 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/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::()?; 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, enum_module_def: &ModuleDef, variant_hir_name: &Name, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index eda35eba45c9a..ecb031e42d7e9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index c5c57c76b47ca..d4a0490f16a41 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/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 { +fn valid_target_expr(ctx: &AssistContext<'_, '_>) -> impl Fn(SyntaxNode) -> Option { 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 { +fn get_literal_name(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option { 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| matches!(adt, Some(hir::Adt::Struct(s)) if s.kind(db) == hir::StructKind::Unit); match path_resolution { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index d8714dd49c2db..d0f5c7c5003d4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs index 17911150f5e7b..d47f5c83cd7ea 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_binexpr.rs +++ b/src/tools/rust-analyzer/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::()?; let lhs = expr.lhs()?; let rhs = expr.rhs()?; @@ -114,7 +114,7 @@ impl From 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::()?; let op = range_expr.op_token()?; let start = range_expr.start(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs index 65dc36cdca7ee..00d659adc1300 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs index bd56331f4128b..c60c6a2a98081 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_or_pattern.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs index dfd280efa6303..77d5c042c9269 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_trait_bound.rs @@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` // fn foo() { } // ``` -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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index 0bb90f187c684..4454e4701312f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/src/tools/rust-analyzer/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::()?; 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::>(); + (!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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs index fce0ce399463c..6c5042b14f9e2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs +++ b/src/tools/rust-analyzer/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::()?; 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, String)> { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs index b4a17c376ac6b..4470791f4d017 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs +++ b/src/tools/rust-analyzer/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::()?; let variant_name = variant.name()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs index 739b63173694a..34ab3c430412d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs +++ b/src/tools/rust-analyzer/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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 9486aa6f01953..6c9808fb1c3be 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 6639f10c1f360..e21f1ab359840 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/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, ) -> Option { 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( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, strukt: &ast::Struct, old_impl: &ast::Impl, args: &Option, @@ -590,13 +594,15 @@ fn finalize_delegate( // While the last two generic args `B` and `C` doesn't change, it remains // ``. So we apply `` as generic arguments to impl. fn generate_args_for_impl( + make: &SyntaxFactory, old_impl_gpl: Option, self_ty: &ast::Type, field_ty: &ast::Type, trait_params: Option, old_trait_args: &FxHashSet, ) -> (Option, Option) { - 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( - 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index a5bdf80ac725e..9d1b257af40c1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/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::()?; let field = ctx.find_node_at_offset::()?; @@ -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::()?; let field = ctx.find_node_at_offset::()?; let field_list = ctx.find_node_at_offset::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs index 0129b1db396b2..f293e956bca5b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs +++ b/src/tools/rust-analyzer/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::()?; let target = nominal.syntax().text_range(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs index 77232dfebdfe4..89adda93866f0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs +++ b/src/tools/rust-analyzer/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::()?; 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 { +fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option { 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) -> Option { +fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option { 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> { } /// Checks if the function is public / exported -fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option { +fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option { 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 { } /// 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 { +fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option { 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 { } /// 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 { } /// 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 { +fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>, edition: Edition) -> Option { 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 { } /// 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs index e2783811f743b..867eaf4c2987f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs +++ b/src/tools/rust-analyzer/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::()?; let parent_enum = ast::Adt::Enum(variant.parent_enum()); let variants = variant diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs index 9a97ad1e8fe75..4cdc801ec19d6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs index 9b4d44d8b5177..fb43e3eaa37e0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/src/tools/rust-analyzer/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 { let scope = ctx.sema.scope(self.syntax())?; @@ -156,7 +156,7 @@ fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option { } fn expr_ty( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, make: &SyntaxFactory, arg: ast::Expr, scope: &hir::SemanticsScope<'_>, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index 55e5083811347..a9f5ab6976a1e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/src/tools/rust-analyzer/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::()?; let func = &name.syntax().parent()?; let func_node = ast::Fn::cast(func.clone())?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index 76246c3e8efd0..52df6182ac56f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/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::()?; 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> { +fn selected_variants( + ctx: &AssistContext<'_, '_>, + variant: &ast::Variant, +) -> Option> { variant .parent_enum() .variant_list()? diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 6ef492619b50c..14dd4061e72f5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/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::(); 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, 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, @@ -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, 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, ) -> (Option, 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, 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::>(); - 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::>(); - 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, call: CallExpr, ) -> Option { @@ -505,7 +484,7 @@ fn get_fn_target_info( } fn get_fn_target( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Option, call: CallExpr, ) -> Option<(GeneratedFunctionTarget, FileId)> { @@ -522,7 +501,7 @@ fn get_fn_target( } fn get_method_target( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, impl_: &Option, adt: &Adt, ) -> Option { @@ -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, + ) { + 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, + ) { + 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, +) { + 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, + cap: Option, +) { + 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, +) { + 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, @@ -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, target: &GeneratedFunctionTarget, ) -> Option<(Option, Option)> { @@ -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 = - generic_params.into_iter().map(|it| it.node.clone_for_update()).collect(); - let where_preds: Vec = - where_preds.into_iter().map(|it| it.node.clone_for_update()).collect(); - - let (generic_params, where_preds): (Vec, Vec) = - 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 = 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, Vec) { 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 { +fn containing_body(ctx: &AssistContext<'_, '_>) -> Option { 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 { } fn get_bounds_in_scope( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, def: D, ) -> (impl Iterator, impl Iterator) where @@ -894,7 +1020,7 @@ struct WherePredWithParams { } fn compute_contained_params_in_generic_param( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, node: ast::GenericParam, ) -> Option { 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 { 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 { +fn filter_generic_params( + ctx: &AssistContext<'_, '_>, + node: SyntaxNode, +) -> Option { 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, where_preds: &mut Vec, - 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, ) -> String { fn maybe_displayed_type( - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, target_module: Module, fn_arg: &ast::Expr, generic_params: &mut FxHashSet, @@ -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); @@ -3239,6 +3368,84 @@ impl Foo { ${0:todo!()} } } +", + ) + } + + #[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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index b884581041f45..7e5d5cec71bc5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/src/tools/rust-analyzer/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, Vec)> { // 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, 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, 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index c5a46f6981f59..ab0eb56fcf19d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/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 Ctx {$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::()?; 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 ${1:_} for Ctx {$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::()?; 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::()?; let trait_ = ast::Trait::cast(name.syntax().parent()?)?; let target_scope = ctx.sema.scope(trait_.syntax())?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs index f10b21b13ecf5..39f304cac9ca3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs +++ b/src/tools/rust-analyzer/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::()?; 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 { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index acf0819222d53..fd095dd9b2aab 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/src/tools/rust-analyzer/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::()?; let indent = impl_def.indent_level(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 520709adc5e29..40adfceaae145 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 10c009a2ea440..4348dfa212c70 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/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::()?; 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> { @@ -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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 049398de8c559..2493ba663264d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/src/tools/rust-analyzer/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::()?; @@ -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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index 21f2249a19c9d..af048c6ae0415 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/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::()?; @@ -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 = FxHashMap::default(); let mut remove_def = true; let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec| { 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 = + 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( - builder: &mut SourceChangeBuilder, iter: impl IntoIterator, mut map_ref: impl FnMut(ast::NameRef) -> Option, -) -> (Vec, Vec) { +) -> (Vec, Vec) { 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( // }; // } // ``` -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, 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 = 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 = 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) {} 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) {} 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) {} 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 } }); @@ -1865,6 +1935,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( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs index b11d3792bc4c7..46cedea967810 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs +++ b/src/tools/rust-analyzer/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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index 2af074f1fcdfe..531adf62ba3f5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs index 280bd7f2ca9ab..002791b88df64 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs +++ b/src/tools/rust-analyzer/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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index 6d8750afdcff1..e4a1314f5bb29 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/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::()?; 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; // } // ``` -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::()?; 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 { +fn get_type_alias(ctx: &AssistContext<'_, '_>, path: &ast::PathType) -> Option { 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 = Vec::new(); } @@ -1123,7 +1119,6 @@ mod foo; //- /foo.rs - fn foo() { let _: i32 = 0; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs index 47b273535a88f..9f8b08f4f80b6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index 2cbeae1d19c25..986989952f676 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs index 95f223420b0b8..427fbbeaa0e9f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs +++ b/src/tools/rust-analyzer/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::()?; 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::*; @@ -168,6 +173,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( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs index c8cb7bb60f69c..9dda4bbb966b6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/invert_if.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs index 1dd0833fad03d..dc40a6a640e00 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs index 6e84af5f91299..f41769150c042 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs +++ b/src/tools/rust-analyzer/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::()?; // 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>>, 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>> { let mut mapping: FxHashMap>> = FxHashMap::default(); fn recurse<'db>( map: &mut FxHashMap>>, - ctx: &AssistContext<'db>, + ctx: &AssistContext<'_, 'db>, pat: &Option, ) { if let Some(local_pat) = pat { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_nested_if.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_nested_if.rs index e491c043e1c11..bb3725a13388a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_nested_if.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs index e044068ff7578..3efc847141c70 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs +++ b/src/tools/rust-analyzer/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::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs index 86bdf3f8b4eb7..4071f16a9fb4e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs index a36d3136a16da..21a8c53e8c9bc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/src/tools/rust-analyzer/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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index 7309cc6d06a5c..7cd0d01238470 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/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::()?; 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::()?; let match_pat = match_arm.pat()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs index 503003bc6bea9..3fec102275355 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/src/tools/rust-analyzer/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::()?; let module_items = module_ast.item_list()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs index 5e95b264fc8e4..7407bd1a0c299 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/src/tools/rust-analyzer/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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs index 36da1d1788247..f97a3e583f030 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs index fac81aefe0279..a50f443d5d58f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/number_representation.rs +++ b/src/tools/rust-analyzer/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::()?; let literal = match literal.kind() { ast::LiteralKind::IntNumber(it) => it, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs index ed61d32eb657f..23c88e65d69e1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/src/tools/rust-analyzer/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::()?; let name = pat.name()?; if !pat.is_simple_ident() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs index 082052c9d42dc..4024d51ffc79d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/src/tools/rust-analyzer/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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index d7885d50651c0..edf0a855c4fe9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index e3dd77360cca3..cb48554083ab4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs index 8234a0374e777..0a745157390f5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs +++ b/src/tools/rust-analyzer/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::()?; 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::()?; 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::()?; 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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index 778533be5a005..91977c2b7fa2d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs index 0c03856417a0a..a40d691e4cc34 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_else_branches.rs +++ b/src/tools/rust-analyzer/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::() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs index 2a6024339f605..db379809c5c8d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_mut.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs index af249c97b9c42..d6606d181ebbc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs +++ b/src/tools/rust-analyzer/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::()?; let cursor_in_range = diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs index 1de1c15cf720b..efe5c945bee52 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_underscore.rs +++ b/src/tools/rust-analyzer/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::() { let text = name_ref.text(); if !text.starts_with('_') { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs index c38bdfdccf5be..2958acc4783ab 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/src/tools/rust-analyzer/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::>(); - // 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 = 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, 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, path: &[Option], @@ -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, 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, scopes: &Vec, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs index b91d678c9371f..d7b0b5ea01f27 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs +++ b/src/tools/rust-analyzer/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, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs index facbab8019b2a..9c9224b99704c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/src/tools/rust-analyzer/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::()?; let record = path.syntax().parent().and_then(>::cast)?; @@ -97,7 +97,7 @@ fn replace( fn compute_fields_ranks( path: &ast::Path, - ctx: &AssistContext<'_>, + ctx: &AssistContext<'_, '_>, ) -> Option> { let strukt = match ctx.sema.resolve_path(path) { Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(it)))) => it, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs index df5281895abdc..658947abe135f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/src/tools/rust-analyzer/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::()?; 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> { let td = trait_definition(path, &ctx.sema)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs index 5ad5efac05c24..9fadf1333f7ae 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs +++ b/src/tools/rust-analyzer/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::()?; - 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 { @@ -249,6 +258,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( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 751cd42f6e8df..4e85b30b58188 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/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::()?; 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::>(); + 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, trait_: Option, 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 0badad7d0cbe6..aa3f917f124df 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/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, conditionals: &[(Option, Option, 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, Option) { +fn let_and_guard( + cond: &ast::Expr, + ctx: &AssistContext<'_, '_>, +) -> Option<(Option<(ast::Pat, ast::Expr)>, Option)> { 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, Option) } 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)> { + 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, + sema: &hir::Semantics<'_, RootDatabase>, +) -> Option { + 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::*; @@ -1815,6 +1888,53 @@ fn foo(x: Result) { ); } + #[test] + fn test_if_matches_with_match() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result, matches +fn foo(x: Result) { + $0if matches!(x, Ok(a @ 1..2)) { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result) { + match x { + Ok(a@1..2) => (), + _ => (), + } +} +"#, + ); + + check_assist( + replace_if_let_with_match, + r#" +//- minicore: result, matches +fn foo(x: Result) { + $0if matches!(x, Ok(ref a)) { + () + } else { + () + } +} +"#, + r#" +fn foo(x: Result) { + match x { + Ok(ref a) => (), + Err(_) => (), + } +} +"#, + ); + } + #[test] fn test_replace_match_with_if_let_unwraps_simple_expressions() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 802d5f72b9973..f50614a66fbd6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/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::>()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs index 85e72130e0a6c..29688d9b9704d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_let_with_if_let.rs @@ -34,7 +34,10 @@ use crate::{AssistContext, AssistId, Assists}; // // fn compute() -> Option { 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 6e4dd8cb73a6b..7aa9a82109c1e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 17ef7727eca39..979f832978632 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/src/tools/rust-analyzer/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 `>` let type_param = ctx.find_node_at_offset::()?; @@ -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` - 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()); @@ -312,6 +314,15 @@ mod tests { ); } + #[test] + fn replace_generic_without_bounds() { + check_assist( + replace_named_generic_with_impl, + r#"fn foo(input: T) {}"#, + r#"fn foo(input: impl Sized) {}"#, + ); + } + #[test] fn replace_generic_with_multiple_trait_bounds() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index eebe93f005f99..0bd1ec12d06c2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/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 { +fn target_path(ctx: &AssistContext<'_, '_>, mut original_path: ast::Path) -> Option { let on_first = original_path.qualifier().is_none(); if on_first { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs index fb5b234d55987..df22c472a4e08 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs index a692259410dc0..5e336523b48e9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs +++ b/src/tools/rust-analyzer/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::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs index 911fa9d14b591..49b3cfb908287 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs +++ b/src/tools/rust-analyzer/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, + ctx: &AssistContext<'_, '_>, item_list: ast::AssocItemList, ) -> Option<()> { let selection = ctx.selection_trimmed(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/split_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/split_import.rs index 1729a0667c0a2..ec5ed44b56290 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/split_import.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs index 849dfa49ddb01..5642f10a6dbec 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs +++ b/src/tools/rust-analyzer/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::()?; let syntax = unexpanded.syntax(); let goal_range = syntax.text_range(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs index fa6ccb9a5f128..99be89ece1640 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs index a088fb178d257..35c304b3870c3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_ignore.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index 4d375080f50e0..e1ac9ea135193 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/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 { 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs index ab6317ad446d6..ec5c0929b4c37 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs +++ b/src/tools/rust-analyzer/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::()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs index 65300ccefdb91..555d3d2ee9cd7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs index b9385775b4765..5542180362e16 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unnecessary_async.rs +++ b/src/tools/rust-analyzer/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 { 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 { +fn find_await_expression(ctx: &AssistContext<'_, '_>, nameref: &NameRef) -> Option { // 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs index 045a27295297e..242ebc4063d0d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs +++ b/src/tools/rust-analyzer/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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs similarity index 64% rename from src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs rename to src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_branch.rs index 77941bcfb2bdb..a582af4e2ca65 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/src/tools/rust-analyzer/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::::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 { + 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 { let mut elements = stmt_list .syntax() @@ -132,16 +210,27 @@ fn extract_statements(stmt_list: ast::StmtList) -> Vec { 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 { @@ -489,10 +577,92 @@ 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 { @@ -637,10 +807,34 @@ 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs index 1fe9ea4eb8759..608c68ea85a6d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_return_type.rs +++ b/src/tools/rust-analyzer/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::()?; let parent = ret_type.syntax().parent()?; let body_expr = match_ast! { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs index e03274bbb3c9e..7d0205afe359c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs +++ b/src/tools/rust-analyzer/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::::cast)?; let mut indent_level = let_stmt.indent_level(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs index 935ae18905449..0c8e40bca1de7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs +++ b/src/tools/rust-analyzer/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::()?; let path = path_type.path()?; let segment = path.segment()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs index ddc0af31c33f2..f9c103aab8f1f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -40,7 +40,7 @@ use crate::{AssistContext, AssistId, Assists}; // fn foo() -> Result { 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::()?; 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/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 635fab857d087..94bd29049d0fa 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/src/tools/rust-analyzer/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::(); @@ -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) -> Option<()> { +fn wrap_cfg_attrs( + acc: &mut Assists, + ctx: &AssistContext<'_, '_>, + attrs: Vec, +) -> 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, + ctx: &AssistContext<'_, '_>, meta: ast::CfgAttrMeta, ) -> Option<()> { let top_attr = ast::Meta::from(meta.clone()).parent_attr()?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 80f05caf4e0dc..eabcb8093e228 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 048f3d7ce8f3e..d3ee35aa86940 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -3735,6 +3735,29 @@ fn doctest_unwrap_block() { check_doc_test( "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/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index bf1062d207bba..096f6678a58b8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/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> { @@ -536,15 +544,11 @@ pub(crate) fn generate_impl_with_item( adt: &ast::Adt, body: Option, ) -> 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 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 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, - trait_is_transitive: bool, - body: Option, -) -> 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 Trait for U` 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 { - 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/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs index fc9bf210e4872..d2b02b3373e1f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/ref_field_expr.rs +++ b/src/tools/rust-analyzer/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 { + fn is_auto_ref(ctx: &AssistContext<'_, '_>, call_expr: &MethodCallExpr) -> bool { + fn impl_(ctx: &AssistContext<'_, '_>, call_expr: &MethodCallExpr) -> Option { let rec = call_expr.receiver()?; let rec_ty = ctx.sema.type_of_expr(&rec)?.original(); // input must be actual value diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 2ed582598b729..f3190bbbc82e6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/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, ) { 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, @@ -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, @@ -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, @@ -466,7 +467,7 @@ impl Completions { pub(crate) fn add_field( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, dot_access: &DotAccess<'_>, receiver: Option, 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, @@ -514,7 +515,7 @@ impl Completions { pub(crate) fn add_union_literal( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, un: hir::Union, path: Option, local_name: Option, @@ -535,7 +536,7 @@ impl Completions { pub(crate) fn add_tuple_field( &mut self, - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, receiver: Option, 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, @@ -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/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index da1e664f961c7..df513dd8fdedc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/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 = 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/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 0d36fb7a405b8..1672e8e7930e3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs index 57ea609a40e06..9e7dabbd01046 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs index 8adc97423909f..1a2e5652e1bf6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/diagnostic.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/feature.rs new file mode 100644 index 0000000000000..1bcf05faa8712 --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs index df577b8ed02eb..2f957b5a55df9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs index 136315c61f585..618cf6fd01ab8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/macro_use.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs index cb7ccf7373123..63cddb365e301 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 18c1992afa246..2cc2200df901b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/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, 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, } - impl MethodCandidateCallback for Callback<'_, F> + impl 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/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs index 885d1a30750f8..30662877d6dca 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index c15c67173ea3d..a2a4cbac2161b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/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, } -impl PathCandidateCallback for PathCallback<'_, F> +impl 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/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs index 570d1a0a2db8a..b00b2e1907534 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs index 91202e8b32fcd..819db549d4f71 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs +++ b/src/tools/rust-analyzer/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 = ctx.scope.extern_crate_decls().collect(); for (name, module) in ctx.scope.extern_crates() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs index 26afa9c8ad96f..b4f7e1839dee3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/field.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 2cf87baf33077..b350647b9a2bd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/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, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index bd0b69215cf7a..f1e8e5f39eaac 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs index eaacd8d1a8fef..1e5240218571a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs index 39048e44001e7..1b26cab263334 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 4072f05a41f42..c165a32082dfc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/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, ) -> 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, ) -> 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, ) -> 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, 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, impl_: &Option, @@ -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 { 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 { 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")) - .syntax() - .clone_for_update(), + factory.ty(&format!("impl Future")).syntax(), + ); + } + None => { + let ret_type = factory.ret_type(factory.ty("impl Future")); + 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")) - .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, ) -> 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, ) -> 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 {} trait Foo { fn function() - where Self: SomeTrait; + where Self: SomeTrait; } struct Bar; @@ -1269,13 +1272,14 @@ trait SomeTrait {} trait Foo { fn function() - where Self: SomeTrait; + where Self: SomeTrait; } struct Bar; impl Foo for Bar { fn function() - where Self: SomeTrait { +where Self: SomeTrait +{ $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/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index fbb3cde968849..f7dd1589ae7d5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs index 8902cd09cec0c..6291b42a0368b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/macro_def.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/macro_def.rs index 2c8e7a2e62cc8..884b4dd966a1c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/macro_def.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs index 3333300045773..a8c6c82aac3a0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs index e7597bf95c80f..7b887fb7d7b20 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 82baf885ddc68..0cb39dd10885b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/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 Builder + 'ctx> { +) -> Option 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; @@ -1147,6 +1203,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, y: Option) { let _f = || { x .and(y) - .map(|it| it+2) + .map(|it| { + it+2 + }) .$0 }; } @@ -1554,10 +1683,73 @@ fn foo(x: Option, y: Option) { fn foo(x: Option, y: Option) { 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); +impl RefCell { + fn new(t: T) -> Self { RefCell(t) } +} + +fn main() { + let other_thing = OtherThing; + let thing: RefCell = other_thing.$0; +} +"#, + r#" +struct OtherThing; +struct RefCell(T); +impl RefCell { + fn new(t: T) -> Self { RefCell(t) } +} + +fn main() { + let other_thing = OtherThing; + let thing: RefCell = RefCell::new(other_thing$0); +} +"#, + ); + + check_edit( + "new", + r#" +mod foo { + pub struct OtherThing; + pub struct RefCell(T); + impl RefCell { + pub fn new(t: T) -> Self { RefCell(t) } + } +} + +fn main() { + let thing: foo::RefCell = foo::OtherThing.$0; +} +"#, + r#" +mod foo { + pub struct OtherThing; + pub struct RefCell(T); + impl RefCell { + pub fn new(t: T) -> Self { RefCell(t) } + } +} + +fn main() { + let thing: foo::RefCell = foo::RefCell::new(foo::OtherThing$0); +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs index 85a8899fd10b0..3b22e8a266e73 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/ra_fixture.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/ra_fixture.rs index 5a8881edc73e9..08ad37b7f2035 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/ra_fixture.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 12c564af5cba4..1238a91dad871 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs index 04450aea75bf7..7432c5226bfad 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index 20bbf0dd8bacc..0b2b6682aaabe 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs index f39b641649326..1ff7dd6deff04 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs +++ b/src/tools/rust-analyzer/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, ) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs index 28d906d91ce5a..49a52f298658a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 80c1572972cec..21f624be2cb7d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/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, + pub add_colons_to_module: bool, pub add_semicolon_to_unit: bool, pub snippet_cap: Option, pub insert_use: InsertUseConfig, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index b9520e9132143..f7fced3f062e9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/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, /// The expected type of what we are completing. - pub(crate) expected_type: Option>, + pub(crate) expected_type: Option>, 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, - ) -> 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/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 58c0f683a344c..faeb97f93f7d1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/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) -> Option { } fn next_non_trivia_sibling(ele: SyntaxElement) -> Option { - 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/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 6abf4f632aa8a..cfadec6287947 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -61,6 +61,9 @@ pub struct CompletionItem { pub documentation: Option>, /// 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/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 3df511a5ad0f3..4ca3257c5cbaf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index a636c0603ba52..fbbdffefe324b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/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, doc_aliases: Vec, } -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> { 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, 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, 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 { @@ -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 { @@ -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 { 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 { // 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 { 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, @@ -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, @@ -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, resolution: ScopeDef, @@ -580,9 +594,10 @@ fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option, 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 { @@ -652,7 +667,7 @@ fn match_types( } fn compute_type_match( - ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option { 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 { 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,30 +861,66 @@ 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}]") } } + #[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( @@ -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); +fn foo(s: &mut S) {} +fn main() { + let mut ssss = S(2u32); + foo($0); +} + "#, + expect![[r#" + st S(…) S(T) [] + st &mut S(…) [type] + lc ssss S [local] + lc &mut ssss [type+local] + st S S<{unknown}> [] + st &mut S [type] + fn foo(…) fn(&mut S) [] + 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] @@ -3640,6 +3857,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( @@ -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/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index 134a77a8991e3..c14fc1704c5ba 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/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 { +pub(crate) fn render_const( + ctx: RenderContext<'_, '_>, + const_: hir::Const, +) -> Option { let _p = tracing::info_span!("render_const").entered(); render(ctx, const_) } -fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option { +fn render(ctx: RenderContext<'_, '_>, const_: hir::Const) -> Option { let db = ctx.db(); let name = const_.name(db)?; let (name, escaped_name) = diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 18151cffcd391..97d5a25f493d5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/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, 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, local_name: Option, @@ -48,7 +48,7 @@ pub(crate) fn render_method( } fn render( - ctx @ RenderContext { completion, .. }: RenderContext<'_>, + ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>, local_name: Option, 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/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index b7de3da468dde..9e0cec62e6418 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/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, 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, @@ -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> { + fn fields(self, ctx: &CompletionContext<'_, '_>) -> Option> { 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/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index ff4cf9a75b60e..85a0761c17e0b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 022e97e4f7600..392ecbc302ae5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/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, @@ -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/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs index 2b79ca2deb693..ce1be2d55117d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs +++ b/src/tools/rust-analyzer/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 { 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 { 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 { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs index 7164c94fde946..e7ee59d489b95 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs +++ b/src/tools/rust-analyzer/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, local_name: Option, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs index ce35ab135f3c3..f86af6cdcb7d8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs +++ b/src/tools/rust-analyzer/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, 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, 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, bool)> { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index d326098f94071..67ca9db2304fb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/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> { + pub(crate) fn imports(&self, ctx: &CompletionContext<'_, '_>) -> Option> { import_edits(ctx, &self.requires) } @@ -163,7 +163,10 @@ impl Snippet { } } -fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { +fn import_edits( + ctx: &CompletionContext<'_, '_>, + requires: &[ModPath], +) -> Option> { let import_cfg = ctx.config.find_path_config(ctx.is_nightly); let resolve = |import| { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 02e299b2a9c19..e574d4de0ac75 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 131911be91558..6dcd4f9cdd80a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 294434297eccb..c1205f9e189eb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/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 @@ -1149,6 +1149,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( @@ -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 @@ -3161,6 +3177,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( @@ -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::AssocTy>: SuperTrait {} + +fn tryme(param: impl SubTrait) { + param$0 +} + "#, + expect![[r#" + fn tryme(…) fn(impl SubTrait<::AssocTy> + ?Sized as SuperTrait>::AssocTy> + ?Sized) + lc param impl SubTrait<::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/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 60ae077d01426..231623a42fc65 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/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: Src) -> Dst; - } + #[rustc_intrinsic] + pub unsafe fn transmute(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: Src) -> Dst; - } + #[rustc_intrinsic] + pub unsafe fn transmute(src: Src) -> Dst; } pub mod mem { pub use crate::intrinsics::transmute; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs index 45024ad21638c..bb79af7e98df1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs +++ b/src/tools/rust-analyzer/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 @@ -134,6 +134,12 @@ fn completes_where() { kw 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#" @@ -154,6 +160,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( @@ -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/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs index 0b2be0265f367..430c61887a20a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs index 682b8904e5501..9826a8ed7b33a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs +++ b/src/tools/rust-analyzer/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 where T: $0 {} pub trait Trait {} "#, expect![[r#" - md std + md std:: kw crate:: kw self:: "#]], @@ -169,7 +169,7 @@ struct Foo where T: $0 {} pub trait Trait {} "#, expect![[r#" - md std + md std:: tt Trait kw crate:: kw self:: diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs index c1274f6640274..ddb9294469007 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 55059a4035e73..0454f4e3504d4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 7d4a7fe6b8872..24080334ae9b9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/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, 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 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 { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 52a5a95450974..264bb4fa814dc 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -257,6 +257,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ warn_since: None, 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"##, @@ -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, }, @@ -1237,6 +1244,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[ warn_since: None, 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"##, @@ -4313,11 +4327,27 @@ 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::(); + sum += args.next_arg::(); } sum } ``` +"##, + default_severity: Severity::Allow, + warn_since: None, + 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, @@ -4799,22 +4829,6 @@ extern { 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: "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, @@ -5545,6 +5559,20 @@ The tracking issue for this feature is: [#95174] [#95174]: https://github.com/rust-lang/rust/issues/95174 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + 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, @@ -5997,6 +6025,22 @@ This feature is internal to the Rust compiler and is not intended for general us 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_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, @@ -6013,6 +6057,20 @@ The tracking issue for this feature is: [#117693] [#117693]: https://github.com/rust-lang/rust/issues/117693 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + 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, @@ -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, @@ -7009,6 +7150,66 @@ The tracking issue for this feature is: [#152900] [#152900]: https://github.com/rust-lang/rust/issues/152900 ------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + 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 ", +)] +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 +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -8070,6 +8271,22 @@ The tracking issue for this feature is: [#99842] [#99842]: https://github.com/rust-lang/rust/issues/99842 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + 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, @@ -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 + + +```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: usize = const { N + 1 }; + +type const INC: usize = ADD1::; + +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 +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(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 { + arr: [usize; N], +} + +impl Grow { + 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: 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: usize = if ARG > 0 { ARG + 1 } else { panic!("0 value") }; + +// Will fail to compile if the `Copy` derive is removed. +const COPY_MARKER: () = (); + +#[derive(Clone, Copy)] +struct Foo; + +const FOO_IS_COPY: () = COPY_MARKER::; +``` "##, default_severity: Severity::Allow, warn_since: None, @@ -8664,6 +9018,22 @@ The tracking issue for this feature is: [#125119] 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: "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, @@ -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 { + 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 +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, @@ -11795,6 +12282,22 @@ The tracking issue for this feature is: [#142503] [#142503]: https://github.com/rust-lang/rust/issues/142503 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + 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, @@ -11951,6 +12454,20 @@ The tracking issue for this feature is: [#130494] [#130494]: https://github.com/rust-lang/rust/issues/130494 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + 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, @@ -14435,6 +14952,22 @@ The tracking issue for this feature is: [#119639] [#119639]: https://github.com/rust-lang/rust/issues/119639 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + 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, @@ -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; println!("{:?}", b); @@ -16258,6 +16791,22 @@ The tracking issue for this feature is: [#146954] [#146954]: https://github.com/rust-lang/rust/issues/146954 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + 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, @@ -16438,6 +16987,22 @@ This feature is internal to the Rust compiler and is not intended for general us This feature is internal to the Rust compiler and is not intended for general use. +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + 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, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs index 08cf1eeed33ba..838aac2283f12 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 9018552afb4d7..e0501b5e44ea3 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index fe30a4dc5cc92..c3949f871314f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/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, + cfg: &InsertUseConfig, + syntax_editor: &SyntaxEditor, +) { + let paths = paths.into_iter().collect::>(); + 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, -) { - 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> { - // 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/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs index 6c7b97458d208..4fa05c4603461 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index 76645464ddf5d..bbd351a4cc14c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 6b72a3033990b..6180e3186cab2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/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 { &"" } 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()` and not `MiniCore(""). + d.field(&format_args!("")); + } else { + d.field(&self.0); + }; + d.finish() } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 2d4a6b8b5b1ba..661f0cff8e135 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 12a48d65ac8c5..fb7edb1acd2f2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index b18ed69d80fec..ff4d5a28866c4 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/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::>>()?; + + let edition = ctor.krate(db).edition(db); + let locals = renamed_fields.iter().map(|&(_, local)| local).collect::>(); + 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:: + }); +} + 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/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index f41e29307007d..d59df3601fbbb 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/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, @@ -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, EditionedFileId, TextRange)>, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 81b679ead233c..766d7251a1046 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/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, + mutated_tree: Option, /// Keeps track of where to place snippets pub snippet_builder: Option, } -pub struct TreeMutator { +struct TreeMutator { immutable: SyntaxNode, mutable_clone: SyntaxNode, } @@ -245,23 +244,6 @@ pub struct SnippetBuilder { places: Vec, } -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(&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) -> SourceChangeBuilder { SourceChangeBuilder { @@ -366,34 +348,6 @@ impl SourceChangeBuilder { } } - pub fn make_mut(&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) { - 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), } 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/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 09e6115320664..76fea5c2623ca 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs b/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs index d2a73710d58bb..2dd558b0b721e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/text_edit.rs +++ b/src/tools/rust-analyzer/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 { 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/src/tools/rust-analyzer/crates/ide-db/src/traits.rs b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs index 60bdc2d82c164..d38d9b67080fb 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs index a91d436afcfbb..0b94a1fa5aad9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs +++ b/src/tools/rust-analyzer/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 { + 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs index 2a7b0098edfd9..e6adac9da74fa 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs index ae42a88c313ce..c84b29dbe242d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/bad_rtn.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index cbcaab6c74777..b7265c47b6fb2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/duplicate_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/duplicate_field.rs new file mode 100644 index 0000000000000..08748bf8af957 --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs index b284d9b351031..8df99598590fa 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs new file mode 100644 index 0000000000000..ab2c3ccd1215b --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs index afd1687ae0736..25e9dc09eb034 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/expected_function.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs index 1dc6a7bf9cae7..0c77fbbd555b8 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/field_shorthand.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs new file mode 100644 index 0000000000000..8b5a235bfb4e8 --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs index 9ae6f013c70d5..515878fd47adf 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs +++ b/src/tools/rust-analyzer/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> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::GenericArgsProhibited) -> Option> { 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs new file mode 100644 index 0000000000000..926c517bc9bb6 --- /dev/null +++ b/src/tools/rust-analyzer/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); + // ^^^^ error: generic parameters cannot use `Self` in their defaults +"#, + ); + } + + #[test] + fn self_as_generic() { + check_diagnostics( + r#" +struct Wrapper(T); +struct Foo>(T); + // ^^^^ error: generic parameters cannot use `Self` in their defaults +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs index be4fe763a0549..09f3e8bfb319b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/src/tools/rust-analyzer/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 { // If there's inactive code somewhere in a macro, don't propagate to the call-site. diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs index a0c364b00108e..bd8b804af4368 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incoherent_impl.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 5410f8b58a9ac..bda3f9bf0ad04 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/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> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::IncorrectCase) -> Option> { 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs index 25220704e04dd..5ee02279a20dd 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs index b71586d6be0b6..c2b70a204e45b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index 405d8df6854df..bd8fa69e28707 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/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(_: *mut T) -> *mut () { +fn to_raw(_: *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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs index 9aa7aed16964d..8522041b52985 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs new file mode 100644 index 0000000000000..225d3e0b46b2c --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 20bfcc2deecee..24f1e3ad836a0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index a44b043f433c6..b6571e02efbe6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs index 7d0c71f4fa7c1..c7d8991f45a87 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/malformed_derive.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 4c0985c7ae965..f6293e35d0c37 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/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>>, 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs new file mode 100644 index 0000000000000..8cae405c92eef --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index 85368cc09f915..607f0cbd233db 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/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> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields) -> Option> { // 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| 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 { 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 { @@ -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); struct S { s: Box } fn x(a: S) { let S { box s } = a; } -", +"#, ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs index b10cdaa14ee61..760bb7309d68b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index d52fc73870530..7bc7955c4ec85 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 6a37702fc50e2..b4ddb239c8cc4 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/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> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingUnsafe) -> Option> { // 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; @@ -261,25 +264,6 @@ pub fn bitreverse(x: u32) -> u32; // Safe intrinsic #[rustc_intrinsic] pub unsafe 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_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); @@ -413,30 +397,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( @@ -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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 6331090d9c90a..e61719acf50fd 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 18280a4addec9..31becd1d74960 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/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 { +pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NeedMut) -> Option { 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 { +pub(crate) fn unused_mut( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnusedMut, +) -> Option { 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index 944622bb1d586..7959fddc757f4 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/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> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NoSuchField) -> Option> { // 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index bc10e82854f5c..ee2f6bf3192d4 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs new file mode 100644 index 0000000000000..be9c07b1ac6c2 --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs index 68f2b1965702c..44fc9f482b15e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs new file mode 100644 index 0000000000000..459ec175b158f --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs index 6d33ae0cf9bea..92f3c6961e363 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs index 90c27bdcef794..9515afed763f3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index 2ec41d0528496..b5a47e508e14d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/src/tools/rust-analyzer/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 { 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> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &RemoveTrailingReturn) -> Option> { 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs index 04f48ae3db170..aa7b57e2928f8 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs +++ b/src/tools/rust-analyzer/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 { 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> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &RemoveUnnecessaryElse) -> Option> { 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index 37ce5f583f93a..f974c55023135 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/src/tools/rust-analyzer/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> { let root = ctx.sema.db.parse_or_expand(d.file); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs index c5b2f499d3067..9e7393c89c154 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs index 2c05544701872..5f5e155bd79ea 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs index 96911d4781b8a..a9dc0d5d72841 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs index 6a380481d4c13..ee972f2d1dc69 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 98a4474ef1e94..250c692d16f80 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/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 { 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> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option> { let mut fixes = Vec::new(); if let Some(expr_ptr) = d.expr_or_pat.value.cast::() { @@ -80,7 +80,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Option, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, acc: &mut Vec, @@ -102,7 +102,7 @@ fn add_reference( } fn add_missing_ok_or_some( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, acc: &mut Vec, @@ -197,7 +197,7 @@ fn add_missing_ok_or_some( } fn remove_unnecessary_wrapper( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, acc: &mut Vec, @@ -279,7 +279,7 @@ fn remove_unnecessary_wrapper( } fn remove_semicolon( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, acc: &mut Vec, @@ -310,7 +310,7 @@ fn remove_semicolon( } fn str_ref_to_owned( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, acc: &mut Vec, @@ -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(x: T) -> T { @@ -938,12 +942,13 @@ fn div(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; @@ -965,12 +970,13 @@ fn div(x: i32, y: i32) -> MyResult { 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) -> (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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_must_be_known.rs new file mode 100644 index 0000000000000..a03352fe31d07 --- /dev/null +++ b/src/tools/rust-analyzer/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 { loop {} } +fn foo() { + let _x = any(); + // ^^^^^ error: type annotations needed +} + "#, + ); + } + + #[test] + fn struct_with_generic() { + check_diagnostics( + r#" +struct X(T); +fn any() -> X { 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(_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); +fn spawn_in(_f: AsyncFn) -> Task +where + R: 'static, + AsyncFn: AsyncFnOnce(&()) -> R + 'static, +{ + loop {} +} + +fn foo() { + spawn_in(async move |cx| {}); +} + "#, + ); + } + + #[test] + fn regression_22263() { + check_diagnostics( + r#" +trait From {} +impl From for T {} +#[rustc_reservation_impl = "blah blah"] +impl From for T {} + +fn any() -> T { + loop {} +} +fn foo>(_: T) -> U { + loop {} +} +fn bar() { + let _: () = foo(any()); +} + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index fd1674e2a47bf..e000d6388ae69 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/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> { +fn fixes<'db>(ctx: &DiagnosticsContext<'_, 'db>, d: &hir::TypedHole<'db>) -> Option> { 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 { loop {} } r#" fn main() { let _x = [(); _]; + // ^ error: type annotations needed + // | full type: `[(); _]` // FIXME: This should trigger error // let _y: [(); 10] = [(); _]; _ = 0; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs index f81d34377da49..7efc8a713619d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs index 5627393f31818..b652456c09e95 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs new file mode 100644 index 0000000000000..d94ceef642f77 --- /dev/null +++ b/src/tools/rust-analyzer/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 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(_f: AsyncFn) +where + AsyncFn: AsyncFnOnce(), +{ +} + +fn foo() { + spawn_in(async move || {}); +} + + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs new file mode 100644 index 0000000000000..7f1b2da4829a3 --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 570319c347d49..dc6ae6f08ba5e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/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, 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> { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs index 0c9e0d6ce440c..52138b7cd5184 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs index f181021bdc5cd..7797c665fd0af 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs index 7c3eacf7e3ad4..2c1f1e72835d2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 59ec259adf36e..78e13677cfb1d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/src/tools/rust-analyzer/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> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>) -> Option> { 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, d: &hir::UnresolvedField<'_>) -> Option { +fn field_fix(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>) -> Option { // 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>>, ) -> Option { let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs index 801023dabd96f..6ecf0be825e09 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_ident.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs index 0da535d11b4fe..f9a125de13f4a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_import.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 030c82ca0ba79..9be7ef6fe7181 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index bd5d134348e27..93caf281f035f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/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> { +fn fixes( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::UnresolvedMethodCall<'_>, +) -> Option> { 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 { @@ -108,7 +111,7 @@ fn field_fix( } fn assoc_func_fix( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedMethodCall<'_>, ) -> Option { if let Some(f) = d.assoc_func_with_same_name { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 1a409d7e76a2f..1e0e9105d88b5 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/src/tools/rust-analyzer/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> { +fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedModule) -> Option> { 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_must_use.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_must_use.rs new file mode 100644 index 0000000000000..e8d0717c91c2f --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index 52a2f44fd0f8c..afc74445f4297 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/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 { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 09c9f8eab0a0d..49b3234a11d8e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/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, node: InFile, @@ -218,6 +231,25 @@ impl Diagnostic { self.unused = unused; self } + + fn main_node(&self, sema: &Semantics<'_, RootDatabase>) -> Option> { + 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::>(); // 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( - ctx: &DiagnosticsContext<'_>, + ctx: &DiagnosticsContext<'_, '_>, diag_ptr: InFile>, adj: &dyn Fn(N) -> Option, ) -> FileRange { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index fc49542e3ccdd..4b9535ca061b9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/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, + 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/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs index 9883bcc84ff8c..301613e920191 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index fb885c2ad11f5..c2b3a3d8d6893 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 4cdf0eac7568f..4890badcbf5b5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/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> { + 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, @@ -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/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index e6ef7b894913e..12ce457ea6bac 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -695,14 +695,14 @@ fn merge_map(res: &mut HighlightMap, new: Option) { /// 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/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 4d712bf0f0e0c..e08bbc5c21b65 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/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, - layout: impl FnOnce() -> Result, - offset: impl FnOnce(&Layout) -> Option, - padding: impl FnOnce(&Layout) -> Option<(&str, u64)>, - tag: impl FnOnce(&Layout) -> Option, + layout: impl FnOnce() -> Result, LayoutError>, + offset: impl FnOnce(&Layout<'db>) -> Option, + padding: impl for<'a> FnOnce(&'a Layout<'db>) -> Option<(&'a str, u64)>, + tag: impl FnOnce(&Layout<'db>) -> Option, ) -> Option { let config = config?; let layout = layout().ok()?; diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 491471428fc61..bf5e0be37420d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/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(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/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 0d2239c71fe9e..a15366fea9621 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/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>, } -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 { - fn rec( - sema: &Semantics<'_, RootDatabase>, - famous_defs: &FamousDefs<'_, '_>, + fn rec<'db>( + sema: &Semantics<'db, RootDatabase>, + famous_defs: &FamousDefs<'_, 'db>, mut max_length: Option, - 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/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index f194bb183e18d..57b723cbd8720 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -1336,7 +1336,7 @@ where { fn f(&self) { let x = self.field.foo(); - //^ impl Baz<<::Assoc as Bar>::Target> + Bar + //^ impl Baz<<<… as Foo>::Assoc as Bar>::Target> + Bar } } "#, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index e8d305afb3b96..63a83ea33528f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index f4ac9c42f459c..df2c42a68c9f3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/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; }; } + "#, ); } @@ -161,6 +203,19 @@ fn main() { foo; }; } +"#, + ); + 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/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 3af529e8c56d3..57aba51b4e859 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 0af2a1f82039e..e131e7bdd17dd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/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) -> (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/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 99f8634bcb05a..f70bb3353fd5d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/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)> { 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/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 4ed3d1c7d7e4a..3eb7867a3ab35 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 900a885a64de8..b664187932efa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/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 { + 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(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 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/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index cf796b27150ec..edcf0dc22b231 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 6afe5681a9b5c..76bb06328b7cf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index fa7f7b1cbafbe..6b63edf563131 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -54,7 +54,15 @@ // This is another normal comment #[derive(Copy, Unresolved)] // The reason for these being here is to test AttrIds +#[default] enum Foo { #[default] - Bar -} \ No newline at end of file + Bar { + #[default] + field: i32 + } +} + +#[derive(Default)] +#[default] +struct Bar(#[default] i32); \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index d687cb40a9697..ab69578ed9d0f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 47ca616f3199b..1b9df9722b07e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -108,7 +108,7 @@ pub(crate) fn view_memory_layout( nodes: &mut Vec, db: &RootDatabase, ty: &Type<'_>, - layout: &Layout, + layout: &Layout<'_>, parent_idx: usize, display_target: DisplayTarget, ) { diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index 39320ebd1cfef..1e6e8ff32f041 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index 8b2d6e8717d23..72d32d1017747 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/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>>> { + unsafe fn try_as_arc_owned(self) -> Option>>> { // 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::>()) @@ -86,6 +85,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/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 614411598b2fd..ac6daaf00681f 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/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 { + 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::, &&str>(elem_ref) }; + Self::LIST.element_offset(elem_ref) + } else { + Self::as_uint_cold(sym) + } + } + + #[cold] + fn as_uint_cold(sym: &Symbol) -> Option { + 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::::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 = { @@ -31,6 +94,14 @@ macro_rules! define_symbols { let hasher_ = dashmap_.hasher().clone(); 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); @@ -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 = "" + [ + 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/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 839df181597ba..801eaeaea9f1f 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/macros/src/lib.rs b/src/tools/rust-analyzer/crates/macros/src/lib.rs index de8c3f2e55f14..9088efeca4fb3 100644 --- a/src/tools/rust-analyzer/crates/macros/src/lib.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index e8e7928c263f0..e135291d89dfe 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 936cf178f015a..76fdac097ff71 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -374,6 +374,13 @@ impl ValueResult { 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(self, f: impl FnOnce(E) -> E2) -> ValueResult { ValueResult { value: self.value, err: self.err.map(f) } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs index 4a1af31656ad5..271dfc877b475 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/src/event.rs b/src/tools/rust-analyzer/crates/parser/src/event.rs index 5be9cb2a24699..001adfb780936 100644 --- a/src/tools/rust-analyzer/crates/parser/src/event.rs +++ b/src/tools/rust-analyzer/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, - }, + Start { kind: SyntaxKind, forward_parent: Option }, /// 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) -> 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, mut errors: Vec) -> 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) -> 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) -> 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/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs b/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs index 2747db4327c56..1edcad64cf38a 100644 --- a/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs +++ b/src/tools/rust-analyzer/crates/parser/src/frontmatter.rs @@ -263,7 +263,7 @@ pub fn strip_ws_lines(input: &str) -> Option { /// 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/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 76d26c1ecdfc2..3f341c2ab846e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 3214fd90f29a9..cabdd74b09ce8 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs index c5c6e04dd49aa..88a43972329ba 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs index 4dd44c030f305..5726f085a03e9 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs +++ b/src/tools/rust-analyzer/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 { + 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 { + 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/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs index 4478bf4e37333..5900d7cfeda9c 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lib.rs +++ b/src/tools/rust-analyzer/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 . +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/src/tools/rust-analyzer/crates/parser/src/output.rs b/src/tools/rust-analyzer/crates/parser/src/output.rs index 0ea15a656c1bb..2f09b1121891b 100644 --- a/src/tools/rust-analyzer/crates/parser/src/output.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs index 4557078de9916..57c501eda224e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/parser.rs +++ b/src/tools/rust-analyzer/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, + /// Side table of error messages. `Event::Error { err }` carries an index + /// into this vec, keeping `Event` itself a flat 8-byte enum. + errors: Vec, steps: Cell, } @@ -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 { - self.events + pub(crate) fn finish(self) -> (Vec, Vec) { + (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>(&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/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 59fa3ee773882..b1867275cebfa 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 6dfb78b12878b..7aaf270a77bf7 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast new file mode 100644 index 0000000000000..2ccd04c321e13 --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rs similarity index 61% rename from src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs rename to src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rs index 12fab59a77656..37f756ffa6db0 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rs @@ -1,3 +1,4 @@ fn foo() { (,); + (a, , b); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast deleted file mode 100644 index 3fbc0da4002e4..0000000000000 --- a/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/deref_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/deref_pat.rast new file mode 100644 index 0000000000000..ab4073c0818ff --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/deref_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/deref_pat.rs new file mode 100644 index 0000000000000..c5c9d9f05939a --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/deref_pat.rs @@ -0,0 +1 @@ +fn foo() { let builtin # deref(_) = 1; } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast index 5f2680cbaa921..bf7b5c5a34f08 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs index 0a46b158affc9..429ac93ad50fd 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rast new file mode 100644 index 0000000000000..d5dcdf04479e7 --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rs new file mode 100644 index 0000000000000..dc8bd1a79747a --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/return_attr.rs @@ -0,0 +1,3 @@ +fn foo() { + return #[attr] 1; +} diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 8377e94c8d608..048d3776ca33f 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index aff5391697414..e44af96f36435 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs index 9ce88484f781f..ae36deb71f02a 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs b/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs index 277cc0b269d71..6e89e0875e749 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/src/lib.rs +++ b/src/tools/rust-analyzer/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 = 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::>(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![;](sig.span())), - default: None, - } -} - struct RemoveAttrsFromTraitMethods; impl VisitMut for RemoveAttrsFromTraitMethods { diff --git a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs index 83ce8902d0c9d..4221068828af8 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs +++ b/src/tools/rust-analyzer/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, - 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, - 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/src/tools/rust-analyzer/crates/query-group-macro/tests/interned.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/interned.rs deleted file mode 100644 index f738185b1fe7c..0000000000000 --- a/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index da2ec740197ea..27d9576e29d0a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index bf9a66bf3fc29..61e0b1e611cfa 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/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::() + drop_whitespace(&original_text).chars().take(50).collect::() ) }; 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::>(); - signatures - .par_iter() - .map_with(db.clone(), |snap, &signatures| { - InferenceResult::of(snap, signatures); - }) - .count(); - let variants = variants.iter().copied().map(Into::into).collect::>(); - 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::>(); + // 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::>() + }); 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/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/highlight.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/highlight.rs index 84607b9fd5d52..5aba532f576ee 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/highlight.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs index d7af56d3e15be..3d7d712326a48 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 3a88a8fe84807..f4c3f24ce6306 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 0c1c067ffa919..ca6a2e70b09f5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index af449c473a85e..bd3574ae0aaa3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 5d0d9209de2f8..754d6e65fea9f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/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, - 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/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index d857f23b6703c..eff54779697ce 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index f5b3658ea90ca..a9ce6f728b6a5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 1832275eb38df..74fd0e653398b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs index e5d4d7c88e803..386edb82f4e93 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 0a81cef52ec5a..f475de93e0582 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/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::()]; fn lookup_ingredient_index(aux: &zalsa_::Zalsa) -> salsa::plumbing::IngredientIndices { aux.lookup_jar_by_type::>().into() diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index 2c19f00f0822f..a9d19a1347f3d 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml index b0fd40ff59f4b..c928f23e02e66 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index 2f932e0458324..648119ed7013c 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/src/tools/rust-analyzer/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 = 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 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 (){} + }; + "#]], + ); + } + + #[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/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 768cf2013d650..6bcf8ba743bc8 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -748,6 +748,10 @@ Pat = | TuplePat | TupleStructPat | ConstBlockPat +| DerefPat + +DerefPat = + 'builtin' '#' 'deref' '(' Pat ')' LiteralPat = '-'? Literal diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs index b20aa90d06f23..2e3a4016ee89f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/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 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 AttrsOwnerEdit for T {} + impl ast::IdentPat { pub fn set_pat(&self, pat: Option, 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 { + 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/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 46ea4daba82ea..4a8c9d450c45c 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/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 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 { - 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 = 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 Indent for N {} - -#[cfg(test)] -mod tests { - use parser::Edition; - - use crate::SourceFile; - - use super::*; - - fn ast_mut_from_text(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::( - "fn foo() { - ; - ; -}", - ); - arm_list.indent(IndentLevel(2)); - assert_eq!( - arm_list.to_string(), - "fn foo() { - ; - ; - }", - ); } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 9a2bba9ebf0dd..e3e5c499d4ea0 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/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 { support::child(&self.syntax) } + #[inline] + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } + #[inline] + pub fn deref_token(&self) -> Option { 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 { + 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(&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 for Pat { #[inline] fn from(node: ConstBlockPat) -> Pat { Pat::ConstBlockPat(node) } } +impl From for Pat { + #[inline] + fn from(node: DerefPat) -> Pat { Pat::DerefPat(node) } +} impl From 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/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index ac02cc9e43fdf..95ff3aebd8db5 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 751f8d7e1cbe1..1eb658f4b8d06 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 0f3b3d301c544..1070af65e7598 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/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, + 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) -> 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, @@ -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, + tail_expr: Option, + ) -> 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, + tail_expr: Option, + ) -> ast::BlockExpr { + let elements = elements.into_iter().collect::>(); + 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, expr: Option) -> ast::BreakExpr { let ast::Expr::BreakExpr(ast) = make::expr_break(label.clone(), expr.clone()).clone_for_update() diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index c510b2831e12b..cda3e69b7c625 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax/src/ptr.rs b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs index c4979b8e3ae80..ed8a68fb8afd5 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs +++ b/src/tools/rust-analyzer/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; -/// Like `SyntaxNodePtr`, but remembers the type of node. +/// Like [`SyntaxNodePtr`], but remembers the type of node. pub struct AstPtr { raw: SyntaxNodePtr, _ty: PhantomData N>, @@ -90,7 +90,7 @@ impl AstPtr { 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> { N::can_cast(raw.kind()).then_some(AstPtr { raw, _ty: PhantomData }) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index edd063ffd4617..7d15195c6f1f7 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/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) { @@ -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) { @@ -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 { + 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)] @@ -701,6 +746,62 @@ mod tests { expect.assert_eq(&edit.new_root.to_string()); } + #[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( diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs index 27ea03ec09e7d..36f50e39186c6 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 28e8ceed708f3..0338d976b0d58 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/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, + ) { + 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, + ) { + 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, + l_curly: Option, + fields: impl Iterator, +) { + let fields = fields.collect::>(); + 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 { + 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/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index a51698aca8074..8975fa56d7272 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/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 DerefPure for &T {} + unsafe impl 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 SliceIndex<[T]> for usize { type Output = T; } + + macro_rules! impl_index_range { + ( $($range:ty,)* ) => { + $( + unsafe impl SliceIndex<[T]> for $range { + type Output = [T]; + } + )* + } + } + + // region:range + impl_index_range!( + crate::ops::RangeFull, + crate::ops::Range, + crate::ops::RangeFrom, + crate::ops::RangeTo, + crate::ops::RangeInclusive, + crate::ops::RangeToInclusive, + ); + // endregion:range + + // region:new_range + impl_index_range!( + crate::range::Range, + crate::range::RangeFrom, + crate::range::RangeInclusive, + crate::range::RangeToInclusive, + ); + // endregion:new_range + // endregion:slice } pub use self::index::{Index, IndexMut}; @@ -735,6 +780,30 @@ pub mod ops { pub struct RangeToInclusive { 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 Iterator for $range { + type Item = Idx; + fn next(&mut self) -> Option { 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 Debug for &T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + T::fmt(&**self, f) + } + } + impl 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 { #[lang = "None"] None, @@ -1678,6 +1780,7 @@ pub mod future { } } pub mod task { + #[lang = "Poll"] pub enum Poll { #[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>; + } +} +// endregion:async_iterator + // region:iterator pub mod iter { // region:iterators @@ -1725,6 +1844,22 @@ pub mod iter { } } + pub struct Map { + iter: I, + f: F, + } + impl Iterator for Map + where + F: FnMut(I::Item) -> B, + { + type Item = B; + + #[inline] + fn next(&mut self) -> B { + loop {} + } + } + pub struct FilterMap { iter: I, f: F, @@ -1799,6 +1934,13 @@ pub mod iter { { loop {} } + fn map(self, _f: F) -> crate::iter::Map + where + Self: Sized, + F: FnMut(Self::Item) -> B, + { + loop {} + } fn filter_map(self, _f: F) -> crate::iter::FilterMap 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/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index da37fc1582342..069c8211dbb6a 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -375,6 +375,15 @@ If false, `-p ` 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/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md index 8ba6f6ab531e6..b74c40c422469 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md @@ -1,5 +1,5 @@