Merge pull request #20390 from A4-Tacks/if-else-comp-in-args-or-let Add if..else completions in LetStmt and ArgList
diff --git a/.github/workflows/rustc-pull.yml b/.github/workflows/rustc-pull.yml index 2a842f3..37cf5f3 100644 --- a/.github/workflows/rustc-pull.yml +++ b/.github/workflows/rustc-pull.yml
@@ -11,10 +11,11 @@ if: github.repository == 'rust-lang/rust-analyzer' uses: rust-lang/josh-sync/.github/workflows/rustc-pull.yml@main with: + github-app-id: ${{ vars.APP_CLIENT_ID }} zulip-stream-id: 185405 zulip-bot-email: "rust-analyzer-ci-bot@rust-lang.zulipchat.com" pr-base-branch: master branch-name: rustc-pull secrets: zulip-api-token: ${{ secrets.ZULIP_API_TOKEN }} - token: ${{ secrets.GITHUB_TOKEN }} + github-app-secret: ${{ secrets.APP_PRIVATE_KEY }}
diff --git a/Cargo.lock b/Cargo.lock index 1e7d449..f72e698 100644 --- a/Cargo.lock +++ b/Cargo.lock
@@ -24,6 +24,12 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] name = "anyhow" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -45,6 +51,15 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -120,6 +135,12 @@ checksum = "26c4925bc979b677330a8c7fe7a8c94af2dbb4a2d37b4a20a80d884400f46baa" [[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] name = "camino" version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -238,9 +259,9 @@ [[package]] name = "chalk-derive" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb4899682de915ca7c0b025bdd0a3d34c75fe12184122fda6805a7baddaa293c" +checksum = "9ea9b1e80910f66ae87c772247591432032ef3f6a67367ff17f8343db05beafa" dependencies = [ "proc-macro2", "quote", @@ -250,9 +271,9 @@ [[package]] name = "chalk-ir" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90a37d2ab99352b4caca135061e7b4ac67024b648c28ed0b787feec4bea4caed" +checksum = "7047a516de16226cd17344d41a319d0ea1064bf9e60bd612ab341ab4a34bbfa8" dependencies = [ "bitflags 2.9.1", "chalk-derive", @@ -260,9 +281,9 @@ [[package]] name = "chalk-recursive" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c855be60e646664bc37c2496d3dc81ca5ef60520930e5e0f0057a0575aff6c19" +checksum = "882959c242558cc686de7ff0aa59860295598d119e84a4b100215f44c3d606c4" dependencies = [ "chalk-derive", "chalk-ir", @@ -273,9 +294,9 @@ [[package]] name = "chalk-solve" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "477ac6cdfd2013e9f93b09b036c2b607a67b2e728f4777b8422d55a79e9e3a34" +checksum = "72860086494ccfa05bbd3779a74babb8ace27da9a0cbabffa1315223c7290927" dependencies = [ "chalk-derive", "chalk-ir", @@ -288,6 +309,40 @@ ] [[package]] +name = "clap" +version = "4.5.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.12", +] + +[[package]] name = "countme" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -309,6 +364,12 @@ ] [[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] name = "crossbeam-channel" version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -385,6 +446,17 @@ ] [[package]] +name = "derive-where" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "derive_arbitrary" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -566,6 +638,15 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -592,6 +673,20 @@ ] [[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] name = "hermit-abi" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -612,6 +707,7 @@ "indexmap", "intern", "itertools 0.14.0", + "ra-ap-rustc_type_ir", "rustc-hash 2.1.1", "smallvec", "span", @@ -621,6 +717,8 @@ "test-fixture", "test-utils", "tracing", + "tracing-subscriber", + "tracing-tree", "triomphe", "tt", ] @@ -717,8 +815,11 @@ "project-model", "query-group-macro", "ra-ap-rustc_abi", + "ra-ap-rustc_ast_ir", "ra-ap-rustc_index", + "ra-ap-rustc_next_trait_solver", "ra-ap-rustc_pattern_analysis", + "ra-ap-rustc_type_ir", "rustc-hash 2.1.1", "rustc_apfloat", "salsa", @@ -1210,7 +1311,7 @@ [[package]] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" dependencies = [ "anyhow", "crossbeam-channel", @@ -1226,9 +1327,9 @@ [[package]] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9462c4dc73e17f971ec1f171d44bfffb72e65a130117233388a0ebc7ec5656f9" +checksum = "7d6ada348dbc2703cbe7637b2dda05cff84d3da2819c24abcb305dd613e0ba2e" dependencies = [ "crossbeam-channel", "log", @@ -1259,7 +1360,7 @@ "expect-test", "intern", "parser", - "ra-ap-rustc_lexer 0.122.0", + "ra-ap-rustc_lexer 0.123.0", "rustc-hash 2.1.1", "smallvec", "span", @@ -1495,7 +1596,7 @@ "drop_bomb", "edition", "expect-test", - "ra-ap-rustc_lexer 0.122.0", + "ra-ap-rustc_lexer 0.123.0", "rustc-literal-escaper", "stdx", "tracing", @@ -1562,6 +1663,17 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "heapless", + "serde", +] + +[[package]] name = "potential_utf" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1605,9 +1717,10 @@ "object", "paths", "proc-macro-test", - "ra-ap-rustc_lexer 0.122.0", + "ra-ap-rustc_lexer 0.123.0", "span", "syntax-bridge", + "temp-dir", "tt", ] @@ -1615,6 +1728,8 @@ name = "proc-macro-srv-cli" version = "0.0.0" dependencies = [ + "clap", + "postcard", "proc-macro-api", "proc-macro-srv", "tt", @@ -1679,6 +1794,7 @@ "serde_json", "span", "stdx", + "temp-dir", "toolchain", "tracing", "triomphe", @@ -1747,9 +1863,9 @@ [[package]] name = "ra-ap-rustc_abi" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb01e1fec578003c85481c1cad4ff8cd8195b07c2dc85ae3f716108507ae15d5" +checksum = "f18c877575c259d127072e9bfc41d985202262fb4d6bfdae3d1252147c2562c2" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1758,19 +1874,25 @@ ] [[package]] -name = "ra-ap-rustc_hashes" -version = "0.122.0" +name = "ra-ap-rustc_ast_ir" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec056e72a472ffef8761ce96ece6c626eb07368c09d0105b6df30d27d07673" +checksum = "87cc17e8ce797f2a8d03b838fbf166749b876164432ce81e37d283bf69e3cf80" + +[[package]] +name = "ra-ap-rustc_hashes" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439ed1df3472443133b66949f81080dff88089b42f825761455463709ee1cad" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcdd1001db0295e59052e9f53aeda588bbe81e362534f4687d41bd44777b5a7" +checksum = "57a24fe0be21be1f8ebc21dcb40129214fb4cefb0f2753f3d46b6dbe656a1a45" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1778,9 +1900,9 @@ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728d64dd98e25530b32e3f7c7c1e844e52722b269360daa1cdeba9dff9727a26" +checksum = "844a27ddcad0116facae2df8e741fd788662cf93dc13029cd864f2b8013b81f9" dependencies = [ "proc-macro2", "quote", @@ -1800,9 +1922,9 @@ [[package]] name = "ra-ap-rustc_lexer" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415f0821f512608d825b3215489a6a6a2c18ed9f0045953d514e7ec23d4b90ab" +checksum = "2b734cfcb577d09877799a22742f1bd398be6c00bc428d9de56d48d11ece5771" dependencies = [ "memchr", "unicode-properties", @@ -1810,6 +1932,19 @@ ] [[package]] +name = "ra-ap-rustc_next_trait_solver" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7dfbdf1d045ff4e385e1efdfc3799379895e9c3f3b9b379a0bef4cb238441" +dependencies = [ + "derive-where", + "ra-ap-rustc_index", + "ra-ap-rustc_type_ir", + "ra-ap-rustc_type_ir_macros", + "tracing", +] + +[[package]] name = "ra-ap-rustc_parse_format" version = "0.121.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1821,9 +1956,9 @@ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4657fcfdfe06e2a02ec8180d4e7c95aecf4811ba50367e363d1a2300b7623284" +checksum = "75b0ee1f059b9dea0818c6c7267478926eee95ba4c7dcf89c8db32fa165d3904" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1833,6 +1968,37 @@ ] [[package]] +name = "ra-ap-rustc_type_ir" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc59fb10a922c38a24cb8a1494f799b0af30bd041acbea689378d3bf330534b" +dependencies = [ + "bitflags 2.9.1", + "derive-where", + "ena", + "indexmap", + "ra-ap-rustc_ast_ir", + "ra-ap-rustc_index", + "ra-ap-rustc_type_ir_macros", + "rustc-hash 2.1.1", + "smallvec", + "thin-vec", + "tracing", +] + +[[package]] +name = "ra-ap-rustc_type_ir_macros" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58878914b6dac7499baeecc8dbb4b9d9dda88030e4ab90cd3b4e87523fbedafe" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1908,7 +2074,7 @@ "intern", "itertools 0.14.0", "load-cargo", - "lsp-server 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-server 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "memchr", "mimalloc", @@ -1991,6 +2157,15 @@ ] [[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2208,6 +2383,15 @@ ] [[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2285,6 +2469,12 @@ ] [[package]] +name = "temp-dir" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964" + +[[package]] name = "tenthash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2583,7 +2773,7 @@ dependencies = [ "arrayvec", "intern", - "ra-ap-rustc_lexer 0.122.0", + "ra-ap-rustc_lexer 0.123.0", "stdx", "text-size", ]
diff --git a/Cargo.toml b/Cargo.toml index 700c116..bb8444e 100644 --- a/Cargo.toml +++ b/Cargo.toml
@@ -89,18 +89,21 @@ vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.122", default-features = false } +ra-ap-rustc_lexer = { version = "0.123", default-features = false } ra-ap-rustc_parse_format = { version = "0.121", default-features = false } -ra-ap-rustc_index = { version = "0.122", default-features = false } -ra-ap-rustc_abi = { version = "0.122", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.122", default-features = false } +ra-ap-rustc_index = { version = "0.123", default-features = false } +ra-ap-rustc_abi = { version = "0.123", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.123", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.123", default-features = false } +ra-ap-rustc_type_ir = { version = "0.123", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.123", default-features = false } # local crates that aren't published to crates.io. These should not have versions. # in-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.2" } la-arena = { version = "0.3.1" } -lsp-server = { version = "0.7.8" } +lsp-server = { version = "0.7.9" } # non-local crates anyhow = "1.0.98" @@ -108,10 +111,10 @@ bitflags = "2.9.1" cargo_metadata = "0.21.0" camino = "1.1.10" -chalk-solve = { version = "0.103.0", default-features = false } -chalk-ir = "0.103.0" -chalk-recursive = { version = "0.103.0", default-features = false } -chalk-derive = "0.103.0" +chalk-solve = { version = "0.104.0", default-features = false } +chalk-ir = "0.104.0" +chalk-recursive = { version = "0.104.0", default-features = false } +chalk-derive = "0.104.0" crossbeam-channel = "0.5.15" dissimilar = "1.0.10" dot = "0.1.4" @@ -125,11 +128,11 @@ nohash-hasher = "0.2.0" oorandom = "11.1.5" object = { version = "0.36.7", default-features = false, features = [ - "std", - "read_core", - "elf", - "macho", - "pe", + "std", + "read_core", + "elf", + "macho", + "pe", ] } process-wrap = { version = "8.2.1", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" @@ -139,9 +142,9 @@ # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it salsa = { version = "0.23.0", default-features = true, features = [ - "rayon", - "salsa_unstable", - "macros", + "rayon", + "salsa_unstable", + "macros", ] } salsa-macros = "0.23.0" semver = "1.0.26" @@ -151,21 +154,22 @@ rustc-hash = "2.1.1" rustc-literal-escaper = "0.0.4" smallvec = { version = "1.15.1", features = [ - "const_new", - "union", - "const_generics", + "const_new", + "union", + "const_generics", ] } smol_str = "0.3.2" +temp-dir = "0.1.16" text-size = "1.1.1" tracing = "0.1.41" tracing-tree = "0.4.0" tracing-subscriber = { version = "0.3.19", default-features = false, features = [ - "registry", - "fmt", - "local-time", - "std", - "time", - "tracing-log", + "registry", + "fmt", + "local-time", + "std", + "time", + "tracing-log", ] } triomphe = { version = "0.1.14", default-features = false, features = ["std"] } url = "2.5.4" @@ -175,7 +179,7 @@ dashmap = { version = "=6.1.0", features = ["raw-api", "inline"] } # We need to freeze the version of the crate, as it needs to match with dashmap hashbrown = { version = "0.14.*", features = [ - "inline-more", + "inline-more", ], default-features = false } [workspace.lints.rust]
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 8c9393b..0bf4fbd 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs
@@ -30,6 +30,7 @@ pub enum ProcMacroLoadingError { Disabled, FailedToBuild, + ExpectedProcMacroArtifact, MissingDylibPath, NotYetBuilt, NoProcMacros, @@ -39,7 +40,8 @@ pub fn is_hard_error(&self) -> bool { match self { ProcMacroLoadingError::Disabled | ProcMacroLoadingError::NotYetBuilt => false, - ProcMacroLoadingError::FailedToBuild + ProcMacroLoadingError::ExpectedProcMacroArtifact + | ProcMacroLoadingError::FailedToBuild | ProcMacroLoadingError::MissingDylibPath | ProcMacroLoadingError::NoProcMacros | ProcMacroLoadingError::ProcMacroSrvError(_) => true, @@ -51,10 +53,16 @@ impl fmt::Display for ProcMacroLoadingError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + ProcMacroLoadingError::ExpectedProcMacroArtifact => { + write!(f, "proc-macro crate did not build proc-macro artifact") + } ProcMacroLoadingError::Disabled => write!(f, "proc-macro expansion is disabled"), ProcMacroLoadingError::FailedToBuild => write!(f, "proc-macro failed to build"), ProcMacroLoadingError::MissingDylibPath => { - write!(f, "proc-macro crate build data is missing a dylib path") + write!( + f, + "proc-macro crate built but the dylib path is missing, this indicates a problem with your build system." + ) } ProcMacroLoadingError::NotYetBuilt => write!(f, "proc-macro not yet built"), ProcMacroLoadingError::NoProcMacros => {
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index ad17f17..b8eadb6 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs
@@ -206,6 +206,7 @@ #[salsa_macros::input(debug)] pub struct FileText { + #[returns(ref)] pub text: Arc<str>, pub file_id: vfs::FileId, } @@ -357,7 +358,7 @@ let _p = tracing::info_span!("parse", ?file_id).entered(); let (file_id, edition) = file_id.unpack(db.as_dyn_database()); let text = db.file_text(file_id).text(db); - ast::SourceFile::parse(&text, edition) + ast::SourceFile::parse(text, edition) } fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<&[SyntaxError]> {
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs index aed00aa..f83c21e 100644 --- a/crates/cfg/src/cfg_expr.rs +++ b/crates/cfg/src/cfg_expr.rs
@@ -134,10 +134,10 @@ }; // Eat comma separator - if let Some(TtElement::Leaf(tt::Leaf::Punct(punct))) = it.peek() { - if punct.char == ',' { - it.next(); - } + if let Some(TtElement::Leaf(tt::Leaf::Punct(punct))) = it.peek() + && punct.char == ',' + { + it.next(); } Some(ret) }
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index b509e69..b4fcfa1 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs
@@ -377,10 +377,10 @@ let mut align = None; if let Some(TtElement::Subtree(_, mut tt_iter)) = tts.peek() { tts.next(); - if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() { - if let Ok(a) = lit.symbol.as_str().parse() { - align = Align::from_bytes(a).ok(); - } + if let Some(TtElement::Leaf(tt::Leaf::Literal(lit))) = tt_iter.next() + && let Ok(a) = lit.symbol.as_str().parse() + { + align = Align::from_bytes(a).ok(); } } ReprOptions { align, ..Default::default() } @@ -554,7 +554,6 @@ AdtId::UnionId(it) => attrs_from_ast_id_loc(db, it), }, AttrDefId::TraitId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::TraitAliasId(it) => attrs_from_ast_id_loc(db, it), AttrDefId::MacroId(it) => match it { MacroId::Macro2Id(it) => attrs_from_ast_id_loc(db, it), MacroId::MacroRulesId(it) => attrs_from_ast_id_loc(db, it), @@ -659,7 +658,6 @@ AttrDefId::StaticId(id) => any_has_attrs(db, id), AttrDefId::ConstId(id) => any_has_attrs(db, id), AttrDefId::TraitId(id) => any_has_attrs(db, id), - AttrDefId::TraitAliasId(id) => any_has_attrs(db, id), AttrDefId::TypeAliasId(id) => any_has_attrs(db, id), AttrDefId::MacroId(id) => match id { MacroId::Macro2Id(id) => any_has_attrs(db, id),
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index c67bb24..4e1d598 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs
@@ -15,8 +15,8 @@ EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, - ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, - TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId, + ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, + TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId, attr::{Attrs, AttrsWithOwner}, expr_store::{ Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, scope::ExprScopes, @@ -28,7 +28,7 @@ nameres::crate_def_map, signatures::{ ConstSignature, EnumSignature, FunctionSignature, ImplSignature, StaticSignature, - StructSignature, TraitAliasSignature, TraitSignature, TypeAliasSignature, UnionSignature, + StructSignature, TraitSignature, TypeAliasSignature, UnionSignature, }, tt, visibility::{self, Visibility}, @@ -70,9 +70,6 @@ fn intern_trait(&self, loc: TraitLoc) -> TraitId; #[salsa::interned] - fn intern_trait_alias(&self, loc: TraitAliasLoc) -> TraitAliasId; - - #[salsa::interned] fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId; #[salsa::interned] @@ -153,11 +150,6 @@ } #[salsa::tracked] - fn trait_alias_signature(&self, e: TraitAliasId) -> Arc<TraitAliasSignature> { - self.trait_alias_signature_with_source_map(e).0 - } - - #[salsa::tracked] fn type_alias_signature(&self, e: TypeAliasId) -> Arc<TypeAliasSignature> { self.type_alias_signature_with_source_map(e).0 } @@ -210,12 +202,6 @@ e: FunctionId, ) -> (Arc<FunctionSignature>, Arc<ExpressionStoreSourceMap>); - #[salsa::invoke(TraitAliasSignature::query)] - fn trait_alias_signature_with_source_map( - &self, - e: TraitAliasId, - ) -> (Arc<TraitAliasSignature>, Arc<ExpressionStoreSourceMap>); - #[salsa::invoke(TypeAliasSignature::query)] fn type_alias_signature_with_source_map( &self,
diff --git a/crates/hir-def/src/dyn_map.rs b/crates/hir-def/src/dyn_map.rs index 20018b6..7d3a94b 100644 --- a/crates/hir-def/src/dyn_map.rs +++ b/crates/hir-def/src/dyn_map.rs
@@ -33,8 +33,8 @@ use crate::{ BlockId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, - ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, - TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId, + ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitId, + TypeAliasId, TypeOrConstParamId, UnionId, UseId, dyn_map::{DynMap, Policy}, }; @@ -48,7 +48,6 @@ pub const IMPL: Key<ast::Impl, ImplId> = Key::new(); pub const EXTERN_BLOCK: Key<ast::ExternBlock, ExternBlockId> = Key::new(); pub const TRAIT: Key<ast::Trait, TraitId> = Key::new(); - pub const TRAIT_ALIAS: Key<ast::TraitAlias, TraitAliasId> = Key::new(); pub const STRUCT: Key<ast::Struct, StructId> = Key::new(); pub const UNION: Key<ast::Union, UnionId> = Key::new(); pub const ENUM: Key<ast::Enum, EnumId> = Key::new();
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index abd1382..3794cb1 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs
@@ -33,7 +33,7 @@ use crate::{ AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId, - ModuleDefId, ModuleId, TraitAliasId, TraitId, TypeAliasId, UnresolvedMacro, + ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, builtin_type::BuiltinUint, db::DefDatabase, expr_store::{ @@ -252,28 +252,6 @@ (store, source_map, params) } -pub(crate) fn lower_trait_alias( - db: &dyn DefDatabase, - module: ModuleId, - trait_syntax: InFile<ast::TraitAlias>, - trait_id: TraitAliasId, -) -> (ExpressionStore, ExpressionStoreSourceMap, Arc<GenericParams>) { - 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(), - trait_syntax.value.type_bound_list(), - ); - collector.lower( - &mut expr_collector, - trait_syntax.value.generic_param_list(), - trait_syntax.value.where_clause(), - ); - let params = collector.finish(); - let (store, source_map) = expr_collector.store.finish(); - (store, source_map, params) -} - pub(crate) fn lower_type_alias( db: &dyn DefDatabase, module: ModuleId, @@ -1487,13 +1465,13 @@ ast::Expr::UnderscoreExpr(_) => self.alloc_pat_from_expr(Pat::Wild, syntax_ptr), ast::Expr::ParenExpr(e) => { // We special-case `(..)` for consistency with patterns. - if let Some(ast::Expr::RangeExpr(range)) = e.expr() { - if range.is_range_full() { - return Some(self.alloc_pat_from_expr( - Pat::Tuple { args: Box::default(), ellipsis: Some(0) }, - syntax_ptr, - )); - } + if let Some(ast::Expr::RangeExpr(range)) = e.expr() + && range.is_range_full() + { + return Some(self.alloc_pat_from_expr( + Pat::Tuple { args: Box::default(), ellipsis: Some(0) }, + syntax_ptr, + )); } return e.expr().and_then(|expr| self.maybe_collect_expr_as_pat(&expr)); } @@ -2569,19 +2547,18 @@ } } RibKind::MacroDef(macro_id) => { - if let Some((parent_ctx, label_macro_id)) = hygiene_info { - if label_macro_id == **macro_id { - // A macro is allowed to refer to labels from before its declaration. - // Therefore, if we got to the rib of its declaration, give up its hygiene - // and use its parent expansion. + if let Some((parent_ctx, label_macro_id)) = hygiene_info + && label_macro_id == **macro_id + { + // A macro is allowed to refer to labels from before its declaration. + // Therefore, if we got to the rib of its declaration, give up its hygiene + // and use its parent expansion. - hygiene_id = - HygieneId::new(parent_ctx.opaque_and_semitransparent(self.db)); - hygiene_info = parent_ctx.outer_expn(self.db).map(|expansion| { - let expansion = self.db.lookup_intern_macro_call(expansion.into()); - (parent_ctx.parent(self.db), expansion.def) - }); - } + hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent(self.db)); + hygiene_info = parent_ctx.outer_expn(self.db).map(|expansion| { + let expansion = self.db.lookup_intern_macro_call(expansion.into()); + (parent_ctx.parent(self.db), expansion.def) + }); } } _ => {}
diff --git a/crates/hir-def/src/expr_store/lower/asm.rs b/crates/hir-def/src/expr_store/lower/asm.rs index 3bc4afb..230d1c9 100644 --- a/crates/hir-def/src/expr_store/lower/asm.rs +++ b/crates/hir-def/src/expr_store/lower/asm.rs
@@ -259,10 +259,10 @@ } }; - if let Some(operand_idx) = operand_idx { - if let Some(position_span) = to_span(arg.position_span) { - mappings.push((position_span, operand_idx)); - } + if let Some(operand_idx) = operand_idx + && let Some(position_span) = to_span(arg.position_span) + { + mappings.push((position_span, operand_idx)); } } }
diff --git a/crates/hir-def/src/expr_store/lower/path.rs b/crates/hir-def/src/expr_store/lower/path.rs index be006c9..579465e 100644 --- a/crates/hir-def/src/expr_store/lower/path.rs +++ b/crates/hir-def/src/expr_store/lower/path.rs
@@ -211,16 +211,17 @@ // Basically, even in rustc it is quite hacky: // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456 // We follow what it did anyway :) - if segments.len() == 1 && kind == PathKind::Plain { - if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { - 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) { - if collector.db.lookup_intern_macro_call(macro_call_id.into()).def.local_inner { - kind = match resolve_crate_root(collector.db, syn_ctxt) { - Some(crate_root) => PathKind::DollarCrate(crate_root), - None => PathKind::Crate, - } - } + if segments.len() == 1 + && kind == PathKind::Plain + && let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) + { + 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 + { + kind = match resolve_crate_root(collector.db, syn_ctxt) { + Some(crate_root) => PathKind::DollarCrate(crate_root), + None => PathKind::Crate, } } }
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index f1b0113..5b9da3c 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -183,7 +183,6 @@ } GenericDefId::ImplId(id) => format!("unimplemented {id:?}"), GenericDefId::StaticId(id) => format!("unimplemented {id:?}"), - GenericDefId::TraitAliasId(id) => format!("unimplemented {id:?}"), GenericDefId::TraitId(id) => format!("unimplemented {id:?}"), GenericDefId::TypeAliasId(id) => format!("unimplemented {id:?}"), } @@ -900,14 +899,12 @@ let field_name = arg.name.display(self.db, edition).to_string(); let mut same_name = false; - if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] { - if let Binding { name, mode: BindingAnnotation::Unannotated, .. } = + if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] + && let Binding { name, mode: BindingAnnotation::Unannotated, .. } = &self.store.assert_expr_only().bindings[*id] - { - if name.as_str() == field_name { - same_name = true; - } - } + && name.as_str() == field_name + { + same_name = true; } w!(p, "{}", field_name);
diff --git a/crates/hir-def/src/expr_store/tests/signatures.rs b/crates/hir-def/src/expr_store/tests/signatures.rs index efb558a..b68674c 100644 --- a/crates/hir-def/src/expr_store/tests/signatures.rs +++ b/crates/hir-def/src/expr_store/tests/signatures.rs
@@ -24,7 +24,6 @@ ModuleDefId::ConstId(id) => id.into(), ModuleDefId::StaticId(id) => id.into(), ModuleDefId::TraitId(id) => id.into(), - ModuleDefId::TraitAliasId(id) => id.into(), ModuleDefId::TypeAliasId(id) => id.into(), ModuleDefId::EnumVariantId(_) => continue, ModuleDefId::BuiltinType(_) => continue, @@ -51,7 +50,6 @@ GenericDefId::ImplId(_id) => (), GenericDefId::StaticId(_id) => (), - GenericDefId::TraitAliasId(_id) => (), GenericDefId::TraitId(_id) => (), GenericDefId::TypeAliasId(_id) => (), }
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index dccfff0..faa0ef8 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs
@@ -107,11 +107,11 @@ /// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Option<ModPath> { // - if the item is a module, jump straight to module search - if !ctx.is_std_item { - if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { - return find_path_for_module(ctx, &mut FxHashSet::default(), module_id, true, max_len) - .map(|choice| choice.path); - } + if !ctx.is_std_item + && let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item + { + return find_path_for_module(ctx, &mut FxHashSet::default(), module_id, true, max_len) + .map(|choice| choice.path); } let may_be_in_scope = match ctx.prefix { @@ -226,15 +226,15 @@ } // - if the module can be referenced as self, super or crate, do that - if let Some(kind) = is_kw_kind_relative_to_from(ctx.from_def_map, module_id, ctx.from) { - if ctx.prefix != PrefixKind::ByCrate || kind == PathKind::Crate { - return Some(Choice { - path: ModPath::from_segments(kind, None), - path_text_len: path_kind_len(kind), - stability: Stable, - prefer_due_to_prelude: false, - }); - } + if let Some(kind) = is_kw_kind_relative_to_from(ctx.from_def_map, module_id, ctx.from) + && (ctx.prefix != PrefixKind::ByCrate || kind == PathKind::Crate) + { + return Some(Choice { + path: ModPath::from_segments(kind, None), + path_text_len: path_kind_len(kind), + stability: Stable, + prefer_due_to_prelude: false, + }); } // - if the module is in the prelude, return it by that path @@ -604,29 +604,29 @@ &def_map[module.local_id] }; - if let Some((name, vis, declared)) = data.scope.name_of(item) { - if vis.is_visible_from(db, from) { - let is_pub_or_explicit = match vis { - Visibility::Module(_, VisibilityExplicitness::Explicit) => { - cov_mark::hit!(explicit_private_imports); - true - } - Visibility::Module(_, VisibilityExplicitness::Implicit) => { - cov_mark::hit!(discount_private_imports); - false - } - Visibility::PubCrate(_) => true, - Visibility::Public => true, - }; - - // Ignore private imports unless they are explicit. these could be used if we are - // in a submodule of this module, but that's usually not - // what the user wants; and if this module can import - // the item and we're a submodule of it, so can we. - // Also this keeps the cached data smaller. - if declared || is_pub_or_explicit { - cb(visited_modules, name, module); + if let Some((name, vis, declared)) = data.scope.name_of(item) + && vis.is_visible_from(db, from) + { + let is_pub_or_explicit = match vis { + Visibility::Module(_, VisibilityExplicitness::Explicit) => { + cov_mark::hit!(explicit_private_imports); + true } + Visibility::Module(_, VisibilityExplicitness::Implicit) => { + cov_mark::hit!(discount_private_imports); + false + } + Visibility::PubCrate(_) => true, + Visibility::Public => true, + }; + + // Ignore private imports unless they are explicit. these could be used if we are + // in a submodule of this module, but that's usually not + // what the user wants; and if this module can import + // the item and we're a submodule of it, so can we. + // Also this keeps the cached data smaller. + if declared || is_pub_or_explicit { + cb(visited_modules, name, module); } }
diff --git a/crates/hir-def/src/hir/generics.rs b/crates/hir-def/src/hir/generics.rs index 94e683c..60cd66b 100644 --- a/crates/hir-def/src/hir/generics.rs +++ b/crates/hir-def/src/hir/generics.rs
@@ -203,9 +203,6 @@ } GenericDefId::ImplId(impl_id) => db.impl_signature(impl_id).generic_params.clone(), GenericDefId::StaticId(_) => EMPTY.clone(), - GenericDefId::TraitAliasId(trait_alias_id) => { - db.trait_alias_signature(trait_alias_id).generic_params.clone() - } GenericDefId::TraitId(trait_id) => db.trait_signature(trait_id).generic_params.clone(), GenericDefId::TypeAliasId(type_alias_id) => { db.type_alias_signature(type_alias_id).generic_params.clone() @@ -246,10 +243,6 @@ let sig = db.static_signature(id); (EMPTY.clone(), sig.store.clone()) } - GenericDefId::TraitAliasId(id) => { - let sig = db.trait_alias_signature(id); - (sig.generic_params.clone(), sig.store.clone()) - } GenericDefId::TraitId(id) => { let sig = db.trait_signature(id); (sig.generic_params.clone(), sig.store.clone()) @@ -294,10 +287,6 @@ let (sig, sm) = db.static_signature_with_source_map(id); (EMPTY.clone(), sig.store.clone(), sm) } - GenericDefId::TraitAliasId(id) => { - let (sig, sm) = db.trait_alias_signature_with_source_map(id); - (sig.generic_params.clone(), sig.store.clone(), sm) - } GenericDefId::TraitId(id) => { let (sig, sm) = db.trait_signature_with_source_map(id); (sig.generic_params.clone(), sig.store.clone(), sm)
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index efa4399..77ed664 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs
@@ -510,12 +510,11 @@ id: AttrId, idx: usize, ) { - if let Some(derives) = self.derive_macros.get_mut(&adt) { - if let Some(DeriveMacroInvocation { derive_call_ids, .. }) = + if let Some(derives) = self.derive_macros.get_mut(&adt) + && let Some(DeriveMacroInvocation { derive_call_ids, .. }) = derives.iter_mut().find(|&&mut DeriveMacroInvocation { attr_id, .. }| id == attr_id) - { - derive_call_ids[idx] = Some(call); - } + { + derive_call_ids[idx] = Some(call); } } @@ -873,7 +872,6 @@ PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::TraitId(_) => PerNs::types(def, v, import), - ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import), ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import), ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import), ModuleDefId::MacroId(mac) => PerNs::macros(mac, v, import),
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index c633339..f35df8d 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs
@@ -276,7 +276,6 @@ Static(Static), Struct(Struct), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), Union(Union), } @@ -404,7 +403,6 @@ Static in small_data -> ast::Static, Struct in small_data -> ast::Struct, Trait in small_data -> ast::Trait, - TraitAlias in small_data -> ast::TraitAlias, TypeAlias in small_data -> ast::TypeAlias, Union in small_data -> ast::Union, Use in big_data -> ast::Use, @@ -584,12 +582,6 @@ } #[derive(Debug, Clone, Eq, PartialEq)] -pub struct TraitAlias { - pub name: Name, - pub(crate) visibility: RawVisibilityId, -} - -#[derive(Debug, Clone, Eq, PartialEq)] pub struct Impl {} #[derive(Debug, Clone, PartialEq, Eq)]
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 5ab61c8..454e063 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs
@@ -23,7 +23,7 @@ BigModItem, Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ImportAlias, Interned, ItemTree, ItemTreeAstId, Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, ModPath, RawAttrs, RawVisibility, RawVisibilityId, SmallModItem, - Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind, + Static, Struct, StructKind, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, VisibilityExplicitness, }, }; @@ -83,12 +83,12 @@ .flat_map(|item| self.lower_mod_item(&item)) .collect(); - if let Some(ast::Expr::MacroExpr(tail_macro)) = stmts.expr() { - if let Some(call) = tail_macro.macro_call() { - cov_mark::hit!(macro_stmt_with_trailing_macro_expr); - if let Some(mod_item) = self.lower_mod_item(&call.into()) { - self.top_level.push(mod_item); - } + if let Some(ast::Expr::MacroExpr(tail_macro)) = stmts.expr() + && let Some(call) = tail_macro.macro_call() + { + cov_mark::hit!(macro_stmt_with_trailing_macro_expr); + if let Some(mod_item) = self.lower_mod_item(&call.into()) { + self.top_level.push(mod_item); } } @@ -112,12 +112,11 @@ _ => None, }) .collect(); - if let Some(ast::Expr::MacroExpr(expr)) = block.tail_expr() { - if let Some(call) = expr.macro_call() { - if let Some(mod_item) = self.lower_mod_item(&call.into()) { - self.top_level.push(mod_item); - } - } + if let Some(ast::Expr::MacroExpr(expr)) = block.tail_expr() + && let Some(call) = expr.macro_call() + && let Some(mod_item) = self.lower_mod_item(&call.into()) + { + self.top_level.push(mod_item); } self.tree.vis.arena = self.visibilities.into_iter().collect(); self.tree.top_level = self.top_level.into_boxed_slice(); @@ -135,7 +134,6 @@ ast::Item::Const(ast) => self.lower_const(ast).into(), ast::Item::Module(ast) => self.lower_module(ast)?.into(), ast::Item::Trait(ast) => self.lower_trait(ast)?.into(), - ast::Item::TraitAlias(ast) => self.lower_trait_alias(ast)?.into(), ast::Item::Impl(ast) => self.lower_impl(ast).into(), ast::Item::Use(ast) => self.lower_use(ast)?.into(), ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(), @@ -268,19 +266,6 @@ Some(ast_id) } - fn lower_trait_alias( - &mut self, - trait_alias_def: &ast::TraitAlias, - ) -> Option<ItemTreeAstId<TraitAlias>> { - let name = trait_alias_def.name()?.as_name(); - let visibility = self.lower_visibility(trait_alias_def); - let ast_id = self.source_ast_id_map.ast_id(trait_alias_def); - - let alias = TraitAlias { name, visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::TraitAlias(alias)); - Some(ast_id) - } - fn lower_impl(&mut self, impl_def: &ast::Impl) -> ItemTreeAstId<Impl> { let ast_id = self.source_ast_id_map.ast_id(impl_def); // Note that trait impls don't get implicit `Self` unlike traits, because here they are a
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index 696174c..94a6cce 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -8,7 +8,7 @@ item_tree::{ Const, DefDatabase, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, RawAttrs, RawVisibilityId, Static, - Struct, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind, + Struct, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, }, visibility::RawVisibility, }; @@ -250,12 +250,6 @@ self.print_visibility(*visibility); w!(self, "trait {} {{ ... }}", name.display(self.db, self.edition)); } - ModItemId::TraitAlias(ast_id) => { - let TraitAlias { name, visibility } = &self.tree[ast_id]; - self.print_ast_id(ast_id.erase()); - self.print_visibility(*visibility); - wln!(self, "trait {} = ..;", name.display(self.db, self.edition)); - } ModItemId::Impl(ast_id) => { let Impl {} = &self.tree[ast_id]; self.print_ast_id(ast_id.erase());
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 7503080..a0be69c 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs
@@ -218,10 +218,10 @@ for (_, module_data) in crate_def_map.modules() { for def in module_data.scope.declarations() { - if let ModuleDefId::TraitId(trait_) = def { - if db.attrs(trait_.into()).has_doc_notable_trait() { - traits.push(trait_); - } + if let ModuleDefId::TraitId(trait_) = def + && db.attrs(trait_.into()).has_doc_notable_trait() + { + traits.push(trait_); } } } @@ -383,12 +383,17 @@ AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1); - AsyncFnOnceOutput, sym::async_fn_once_output,async_fn_once_output, Target::AssocTy, GenericRequirement::None; + CallRefFuture, sym::call_ref_future, call_ref_future_ty, Target::AssocTy, GenericRequirement::None; + CallOnceFuture, sym::call_once_future, call_once_future_ty, Target::AssocTy, GenericRequirement::None; + AsyncFnOnceOutput, sym::async_fn_once_output, async_fn_once_output_ty, Target::AssocTy, GenericRequirement::None; + FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); CoroutineState, sym::coroutine_state, coroutine_state, Target::Enum, GenericRequirement::None; Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Minimum(1); + CoroutineReturn, sym::coroutine_return, coroutine_return_ty, Target::AssocTy, GenericRequirement::None; + CoroutineYield, sym::coroutine_yield, coroutine_yield_ty, Target::AssocTy, GenericRequirement::None; Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None;
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index bdf8b45..1f5b1e0 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs
@@ -318,9 +318,6 @@ } } -pub type TraitAliasLoc = ItemLoc<ast::TraitAlias>; -impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias); - type TypeAliasLoc = AssocItemLoc<ast::TypeAlias>; impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); @@ -742,7 +739,6 @@ ConstId(ConstId), StaticId(StaticId), TraitId(TraitId), - TraitAliasId(TraitAliasId), TypeAliasId(TypeAliasId), BuiltinType(BuiltinType), MacroId(MacroId), @@ -756,7 +752,6 @@ ConstId, StaticId, TraitId, - TraitAliasId, TypeAliasId, BuiltinType for ModuleDefId @@ -862,7 +857,6 @@ // More importantly, this completes the set of items that contain type references // which is to be used by the signature expression store in the future. StaticId(StaticId), - TraitAliasId(TraitAliasId), TraitId(TraitId), TypeAliasId(TypeAliasId), } @@ -872,7 +866,6 @@ FunctionId, ImplId, StaticId, - TraitAliasId, TraitId, TypeAliasId for GenericDefId @@ -902,7 +895,6 @@ GenericDefId::AdtId(AdtId::UnionId(it)) => file_id_and_params_of_item_loc(db, it), GenericDefId::AdtId(AdtId::EnumId(it)) => file_id_and_params_of_item_loc(db, it), GenericDefId::TraitId(it) => file_id_and_params_of_item_loc(db, it), - GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it), GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it), GenericDefId::ConstId(it) => (it.lookup(db).id.file_id, None), GenericDefId::StaticId(it) => (it.lookup(db).id.file_id, None), @@ -978,7 +970,6 @@ StaticId(StaticId), ConstId(ConstId), TraitId(TraitId), - TraitAliasId(TraitAliasId), TypeAliasId(TypeAliasId), MacroId(MacroId), ImplId(ImplId), @@ -997,7 +988,6 @@ ConstId, FunctionId, TraitId, - TraitAliasId, TypeAliasId, MacroId(Macro2Id, MacroRulesId, ProcMacroId), ImplId, @@ -1020,7 +1010,6 @@ ModuleDefId::StaticId(it) => Ok(it.into()), ModuleDefId::TraitId(it) => Ok(it.into()), ModuleDefId::TypeAliasId(it) => Ok(it.into()), - ModuleDefId::TraitAliasId(id) => Ok(id.into()), ModuleDefId::MacroId(id) => Ok(id.into()), ModuleDefId::BuiltinType(_) => Err(()), } @@ -1266,7 +1255,6 @@ GenericDefId::FunctionId(it) => it.module(db), GenericDefId::AdtId(it) => it.module(db), GenericDefId::TraitId(it) => it.module(db), - GenericDefId::TraitAliasId(it) => it.module(db), GenericDefId::TypeAliasId(it) => it.module(db), GenericDefId::ImplId(it) => it.module(db), GenericDefId::ConstId(it) => it.module(db), @@ -1286,7 +1274,6 @@ AttrDefId::StaticId(it) => it.module(db), AttrDefId::ConstId(it) => it.module(db), AttrDefId::TraitId(it) => it.module(db), - AttrDefId::TraitAliasId(it) => it.module(db), AttrDefId::TypeAliasId(it) => it.module(db), AttrDefId::ImplId(it) => it.module(db), AttrDefId::ExternBlockId(it) => it.module(db), @@ -1316,7 +1303,6 @@ ModuleDefId::ConstId(id) => id.module(db), ModuleDefId::StaticId(id) => id.module(db), ModuleDefId::TraitId(id) => id.module(db), - ModuleDefId::TraitAliasId(id) => id.module(db), ModuleDefId::TypeAliasId(id) => id.module(db), ModuleDefId::MacroId(id) => id.module(db), ModuleDefId::BuiltinType(_) => return None,
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index 5e95b061..e8ae499 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -221,46 +221,42 @@ _ => None, }; - if let Some(src) = src { - if let Some(file_id) = src.file_id.macro_file() { - if let MacroKind::Derive - | MacroKind::DeriveBuiltIn - | MacroKind::Attr - | MacroKind::AttrBuiltIn = file_id.kind(&db) - { - let call = file_id.call_node(&db); - let mut show_spans = false; - let mut show_ctxt = false; - for comment in - call.value.children_with_tokens().filter(|it| it.kind() == COMMENT) - { - show_spans |= comment.to_string().contains("+spans"); - show_ctxt |= comment.to_string().contains("+syntaxctxt"); - } - let pp = pretty_print_macro_expansion( - src.value, - db.span_map(src.file_id).as_ref(), - show_spans, - show_ctxt, - ); - format_to!(expanded_text, "\n{}", pp) - } + if let Some(src) = src + && let Some(file_id) = src.file_id.macro_file() + && let MacroKind::Derive + | MacroKind::DeriveBuiltIn + | MacroKind::Attr + | MacroKind::AttrBuiltIn = file_id.kind(&db) + { + let call = file_id.call_node(&db); + let mut show_spans = false; + let mut show_ctxt = false; + for comment in call.value.children_with_tokens().filter(|it| it.kind() == COMMENT) { + show_spans |= comment.to_string().contains("+spans"); + show_ctxt |= comment.to_string().contains("+syntaxctxt"); } + let pp = pretty_print_macro_expansion( + src.value, + db.span_map(src.file_id).as_ref(), + show_spans, + show_ctxt, + ); + format_to!(expanded_text, "\n{}", pp) } } for impl_id in def_map[local_id].scope.impls() { let src = impl_id.lookup(&db).source(&db); - if let Some(macro_file) = src.file_id.macro_file() { - if let MacroKind::DeriveBuiltIn | MacroKind::Derive = macro_file.kind(&db) { - let pp = pretty_print_macro_expansion( - src.value.syntax().clone(), - db.span_map(macro_file.into()).as_ref(), - false, - false, - ); - format_to!(expanded_text, "\n{}", pp) - } + if let Some(macro_file) = src.file_id.macro_file() + && let MacroKind::DeriveBuiltIn | MacroKind::Derive = macro_file.kind(&db) + { + let pp = pretty_print_macro_expansion( + src.value.syntax().clone(), + db.span_map(macro_file.into()).as_ref(), + false, + false, + ); + format_to!(expanded_text, "\n{}", pp) } }
diff --git a/crates/hir-def/src/nameres/assoc.rs b/crates/hir-def/src/nameres/assoc.rs index 07210df..8d2a386 100644 --- a/crates/hir-def/src/nameres/assoc.rs +++ b/crates/hir-def/src/nameres/assoc.rs
@@ -51,10 +51,18 @@ tr: TraitId, ) -> (TraitItems, DefDiagnostics) { let ItemLoc { container: module_id, id: ast_id } = tr.lookup(db); + let ast_id_map = db.ast_id_map(ast_id.file_id); + let source = ast_id.with_value(ast_id_map.get(ast_id.value)).to_node(db); + if source.eq_token().is_some() { + // FIXME(trait-alias) probably needs special handling here + return ( + TraitItems { macro_calls: ThinVec::new(), items: Box::default() }, + DefDiagnostics::new(vec![]), + ); + } let collector = AssocItemCollector::new(db, module_id, ItemContainerId::TraitId(tr), ast_id.file_id); - let source = ast_id.with_value(collector.ast_id_map.get(ast_id.value)).to_node(db); let (items, macro_calls, diagnostics) = collector.collect(source.assoc_item_list()); (TraitItems { macro_calls, items }, DefDiagnostics::new(diagnostics))
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 0c3274d..07e6171 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs
@@ -30,7 +30,7 @@ ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, - StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, + StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, attr::Attrs, db::DefDatabase, item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, @@ -261,20 +261,20 @@ // Process other crate-level attributes. for attr in &*attrs { - if let Some(cfg) = attr.cfg() { - if self.cfg_options.check(&cfg) == Some(false) { - process = false; - break; - } + if let Some(cfg) = attr.cfg() + && self.cfg_options.check(&cfg) == Some(false) + { + process = false; + break; } let Some(attr_name) = attr.path.as_ident() else { continue }; match () { () if *attr_name == sym::recursion_limit => { - if let Some(limit) = attr.string_value() { - if let Ok(limit) = limit.as_str().parse() { - crate_data.recursion_limit = Some(limit); - } + if let Some(limit) = attr.string_value() + && let Ok(limit) = limit.as_str().parse() + { + crate_data.recursion_limit = Some(limit); } } () if *attr_name == sym::crate_type => { @@ -1188,56 +1188,44 @@ // Multiple globs may import the same item and they may override visibility from // previously resolved globs. Handle overrides here and leave the rest to // `ItemScope::push_res_with_import()`. - if let Some(def) = defs.types { - if let Some(prev_def) = prev_defs.types { - if def.def == prev_def.def - && self.from_glob_import.contains_type(module_id, name.clone()) - && def.vis != prev_def.vis - && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis) - { - changed = true; - // This import is being handled here, don't pass it down to - // `ItemScope::push_res_with_import()`. - defs.types = None; - self.def_map.modules[module_id] - .scope - .update_visibility_types(name, def.vis); - } - } + if let Some(def) = defs.types + && let Some(prev_def) = prev_defs.types + && def.def == prev_def.def + && self.from_glob_import.contains_type(module_id, name.clone()) + && def.vis != prev_def.vis + && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis) + { + changed = true; + // This import is being handled here, don't pass it down to + // `ItemScope::push_res_with_import()`. + defs.types = None; + self.def_map.modules[module_id].scope.update_visibility_types(name, def.vis); } - if let Some(def) = defs.values { - if let Some(prev_def) = prev_defs.values { - if def.def == prev_def.def - && self.from_glob_import.contains_value(module_id, name.clone()) - && def.vis != prev_def.vis - && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis) - { - changed = true; - // See comment above. - defs.values = None; - self.def_map.modules[module_id] - .scope - .update_visibility_values(name, def.vis); - } - } + if let Some(def) = defs.values + && let Some(prev_def) = prev_defs.values + && def.def == prev_def.def + && self.from_glob_import.contains_value(module_id, name.clone()) + && def.vis != prev_def.vis + && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis) + { + changed = true; + // See comment above. + defs.values = None; + self.def_map.modules[module_id].scope.update_visibility_values(name, def.vis); } - if let Some(def) = defs.macros { - if let Some(prev_def) = prev_defs.macros { - if def.def == prev_def.def - && self.from_glob_import.contains_macro(module_id, name.clone()) - && def.vis != prev_def.vis - && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis) - { - changed = true; - // See comment above. - defs.macros = None; - self.def_map.modules[module_id] - .scope - .update_visibility_macros(name, def.vis); - } - } + if let Some(def) = defs.macros + && let Some(prev_def) = prev_defs.macros + && def.def == prev_def.def + && self.from_glob_import.contains_macro(module_id, name.clone()) + && def.vis != prev_def.vis + && def.vis.max(prev_def.vis, &self.def_map) == Some(def.vis) + { + changed = true; + // See comment above. + defs.macros = None; + self.def_map.modules[module_id].scope.update_visibility_macros(name, def.vis); } } @@ -1392,15 +1380,14 @@ Resolved::Yes }; - if let Some(ident) = path.as_ident() { - if let Some(helpers) = self.def_map.derive_helpers_in_scope.get(&ast_id) { - if helpers.iter().any(|(it, ..)| it == ident) { - cov_mark::hit!(resolved_derive_helper); - // Resolved to derive helper. Collect the item's attributes again, - // starting after the derive helper. - return recollect_without(self); - } - } + if let Some(ident) = path.as_ident() + && let Some(helpers) = self.def_map.derive_helpers_in_scope.get(&ast_id) + && helpers.iter().any(|(it, ..)| it == ident) + { + cov_mark::hit!(resolved_derive_helper); + // Resolved to derive helper. Collect the item's attributes again, + // starting after the derive helper. + return recollect_without(self); } let def = match resolver_def_id(path) { @@ -1729,12 +1716,12 @@ let mut process_mod_item = |item: ModItemId| { let attrs = self.item_tree.attrs(db, krate, item.ast_id()); - if let Some(cfg) = attrs.cfg() { - if !self.is_cfg_enabled(&cfg) { - let ast_id = item.ast_id().erase(); - self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg); - return; - } + if let Some(cfg) = attrs.cfg() + && !self.is_cfg_enabled(&cfg) + { + let ast_id = item.ast_id().erase(); + self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg); + return; } if let Err(()) = self.resolve_attributes(&attrs, item, container) { @@ -1871,14 +1858,13 @@ if self.def_collector.def_map.block.is_none() && self.def_collector.is_proc_macro && self.module_id == DefMap::ROOT + && let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) { - if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) { - self.def_collector.export_proc_macro( - proc_macro, - InFile::new(self.file_id(), id), - fn_id, - ); - } + self.def_collector.export_proc_macro( + proc_macro, + InFile::new(self.file_id(), id), + fn_id, + ); } update_def(self.def_collector, fn_id.into(), &it.name, vis, false); @@ -1968,20 +1954,6 @@ false, ); } - ModItemId::TraitAlias(id) => { - let it = &self.item_tree[id]; - - let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); - update_def( - self.def_collector, - TraitAliasLoc { container: module, id: InFile::new(self.file_id(), id) } - .intern(db) - .into(), - &it.name, - vis, - false, - ); - } ModItemId::TypeAlias(id) => { let it = &self.item_tree[id]; @@ -2419,13 +2391,13 @@ macro_id, &self.item_tree[mac.visibility], ); - if let Some(helpers) = helpers_opt { - if self.def_collector.def_map.block.is_none() { - Arc::get_mut(&mut self.def_collector.def_map.data) - .unwrap() - .exported_derives - .insert(macro_id.into(), helpers); - } + if let Some(helpers) = helpers_opt + && self.def_collector.def_map.block.is_none() + { + Arc::get_mut(&mut self.def_collector.def_map.data) + .unwrap() + .exported_derives + .insert(macro_id.into(), helpers); } }
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 316ad5d..698292c 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs
@@ -20,7 +20,7 @@ EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, - TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UseId, VariantId, + TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UseId, VariantId, builtin_type::BuiltinType, db::DefDatabase, expr_store::{ @@ -105,7 +105,6 @@ TypeAliasId(TypeAliasId), BuiltinType(BuiltinType), TraitId(TraitId), - TraitAliasId(TraitAliasId), ModuleId(ModuleId), } @@ -228,15 +227,15 @@ ResolvePathResultPrefixInfo::default(), )); } - } else if let &GenericDefId::AdtId(adt) = def { - if *first_name == sym::Self_ { - return Some(( - TypeNs::AdtSelfType(adt), - remaining_idx(), - None, - ResolvePathResultPrefixInfo::default(), - )); - } + } else if let &GenericDefId::AdtId(adt) = def + && *first_name == sym::Self_ + { + return Some(( + TypeNs::AdtSelfType(adt), + remaining_idx(), + None, + ResolvePathResultPrefixInfo::default(), + )); } if let Some(id) = params.find_type_by_name(first_name, *def) { return Some(( @@ -401,13 +400,13 @@ handle_macro_def_scope(db, &mut hygiene_id, &mut hygiene_info, macro_id) } Scope::GenericParams { params, def } => { - if let &GenericDefId::ImplId(impl_) = def { - if *first_name == sym::Self_ { - return Some(( - ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None), - ResolvePathResultPrefixInfo::default(), - )); - } + if let &GenericDefId::ImplId(impl_) = def + && *first_name == sym::Self_ + { + return Some(( + ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None), + ResolvePathResultPrefixInfo::default(), + )); } if let Some(id) = params.find_const_by_name(first_name, *def) { let val = ValueNs::GenericParam(id); @@ -436,14 +435,14 @@ ResolvePathResultPrefixInfo::default(), )); } - } else if let &GenericDefId::AdtId(adt) = def { - if *first_name == sym::Self_ { - let ty = TypeNs::AdtSelfType(adt); - return Some(( - ResolveValueResult::Partial(ty, 1, None), - ResolvePathResultPrefixInfo::default(), - )); - } + } else if let &GenericDefId::AdtId(adt) = def + && *first_name == sym::Self_ + { + let ty = TypeNs::AdtSelfType(adt); + return Some(( + ResolveValueResult::Partial(ty, 1, None), + ResolvePathResultPrefixInfo::default(), + )); } if let Some(id) = params.find_type_by_name(first_name, *def) { let ty = TypeNs::GenericParam(id); @@ -469,13 +468,14 @@ // If a path of the shape `u16::from_le_bytes` failed to resolve at all, then we fall back // to resolving to the primitive type, to allow this to still work in the presence of // `use core::u16;`. - if path.kind == PathKind::Plain && n_segments > 1 { - if let Some(builtin) = BuiltinType::by_name(first_name) { - return Some(( - ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None), - ResolvePathResultPrefixInfo::default(), - )); - } + if path.kind == PathKind::Plain + && n_segments > 1 + && let Some(builtin) = BuiltinType::by_name(first_name) + { + return Some(( + ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None), + ResolvePathResultPrefixInfo::default(), + )); } None @@ -660,12 +660,11 @@ Scope::BlockScope(m) => traits.extend(m.def_map[m.module_id].scope.traits()), &Scope::GenericParams { def: GenericDefId::ImplId(impl_), .. } => { let impl_data = db.impl_signature(impl_); - if let Some(target_trait) = impl_data.target_trait { - if let Some(TypeNs::TraitId(trait_)) = self + if let Some(target_trait) = impl_data.target_trait + && let Some(TypeNs::TraitId(trait_)) = self .resolve_path_in_type_ns_fully(db, &impl_data.store[target_trait.path]) - { - traits.insert(trait_); - } + { + traits.insert(trait_); } } _ => (), @@ -918,17 +917,17 @@ hygiene_info: &mut Option<(SyntaxContext, MacroDefId)>, macro_id: &MacroDefId, ) { - if let Some((parent_ctx, label_macro_id)) = hygiene_info { - if label_macro_id == macro_id { - // A macro is allowed to refer to variables from before its declaration. - // Therefore, if we got to the rib of its declaration, give up its hygiene - // and use its parent expansion. - *hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent(db)); - *hygiene_info = parent_ctx.outer_expn(db).map(|expansion| { - let expansion = db.lookup_intern_macro_call(expansion.into()); - (parent_ctx.parent(db), expansion.def) - }); - } + if let Some((parent_ctx, label_macro_id)) = hygiene_info + && label_macro_id == macro_id + { + // A macro is allowed to refer to variables from before its declaration. + // Therefore, if we got to the rib of its declaration, give up its hygiene + // and use its parent expansion. + *hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent(db)); + *hygiene_info = parent_ctx.outer_expn(db).map(|expansion| { + let expansion = db.lookup_intern_macro_call(expansion.into()); + (parent_ctx.parent(db), expansion.def) + }); } } @@ -1150,7 +1149,6 @@ let ty = match def.def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it), - ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it), ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), @@ -1195,7 +1193,6 @@ ModuleDefId::AdtId(AdtId::EnumId(_) | AdtId::UnionId(_)) | ModuleDefId::TraitId(_) - | ModuleDefId::TraitAliasId(_) | ModuleDefId::TypeAliasId(_) | ModuleDefId::BuiltinType(_) | ModuleDefId::MacroId(_) @@ -1214,7 +1211,6 @@ ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it), - ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it), ModuleDefId::ModuleId(it) => TypeNs::ModuleId(it), @@ -1320,12 +1316,6 @@ } } -impl HasResolver for TraitAliasId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { - lookup_resolver(db, self).push_generic_params_scope(db, self.into()) - } -} - impl<T: Into<AdtId> + Copy> HasResolver for T { fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { let def = self.into(); @@ -1410,7 +1400,6 @@ GenericDefId::FunctionId(inner) => inner.resolver(db), GenericDefId::AdtId(adt) => adt.resolver(db), GenericDefId::TraitId(inner) => inner.resolver(db), - GenericDefId::TraitAliasId(inner) => inner.resolver(db), GenericDefId::TypeAliasId(inner) => inner.resolver(db), GenericDefId::ImplId(inner) => inner.resolver(db), GenericDefId::ConstId(inner) => inner.resolver(db),
diff --git a/crates/hir-def/src/signatures.rs b/crates/hir-def/src/signatures.rs index 92e610b..bf72faf 100644 --- a/crates/hir-def/src/signatures.rs +++ b/crates/hir-def/src/signatures.rs
@@ -20,15 +20,13 @@ use crate::{ ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId, - ItemContainerId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, UnionId, - VariantId, + ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, attr::Attrs, db::DefDatabase, expr_store::{ ExpressionStore, ExpressionStoreSourceMap, lower::{ - ExprCollector, lower_function, lower_generic_params, lower_trait, lower_trait_alias, - lower_type_alias, + ExprCollector, lower_function, lower_generic_params, lower_trait, lower_type_alias, }, }, hir::{ExprId, PatId, generics::GenericParams}, @@ -395,7 +393,7 @@ bitflags::bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] - pub struct TraitFlags: u8 { + pub struct TraitFlags: u16 { const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1; const FUNDAMENTAL = 1 << 2; const UNSAFE = 1 << 3; @@ -403,6 +401,8 @@ const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 5; const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6; const RUSTC_PAREN_SUGAR = 1 << 7; + const COINDUCTIVE = 1 << 8; + const ALIAS = 1 << 9; } } @@ -427,6 +427,9 @@ if source.value.unsafe_token().is_some() { flags.insert(TraitFlags::UNSAFE); } + if source.value.eq_token().is_some() { + flags.insert(TraitFlags::ALIAS); + } if attrs.by_key(sym::fundamental).exists() { flags |= TraitFlags::FUNDAMENTAL; } @@ -436,6 +439,9 @@ if attrs.by_key(sym::rustc_paren_sugar).exists() { flags |= TraitFlags::RUSTC_PAREN_SUGAR; } + if attrs.by_key(sym::rustc_coinductive).exists() { + flags |= TraitFlags::COINDUCTIVE; + } let mut skip_array_during_method_dispatch = attrs.by_key(sym::rustc_skip_array_during_method_dispatch).exists(); let mut skip_boxed_slice_during_method_dispatch = false; @@ -465,31 +471,6 @@ } } -#[derive(Debug, PartialEq, Eq)] -pub struct TraitAliasSignature { - pub name: Name, - pub generic_params: Arc<GenericParams>, - pub store: Arc<ExpressionStore>, -} - -impl TraitAliasSignature { - pub fn query( - db: &dyn DefDatabase, - id: TraitAliasId, - ) -> (Arc<Self>, Arc<ExpressionStoreSourceMap>) { - let loc = id.lookup(db); - - let source = loc.source(db); - let name = as_name_opt(source.value.name()); - let (store, source_map, generic_params) = lower_trait_alias(db, loc.container, source, id); - - ( - Arc::new(TraitAliasSignature { generic_params, store: Arc::new(store), name }), - Arc::new(source_map), - ) - } -} - bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct FnFlags: u16 {
diff --git a/crates/hir-def/src/src.rs b/crates/hir-def/src/src.rs index aa373a2..367b543 100644 --- a/crates/hir-def/src/src.rs +++ b/crates/hir-def/src/src.rs
@@ -71,7 +71,7 @@ } impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId { - type Value = Either<ast::TypeOrConstParam, ast::TraitOrAlias>; + type Value = Either<ast::TypeOrConstParam, ast::Trait>; fn child_source( &self, db: &dyn DefDatabase, @@ -89,12 +89,7 @@ GenericDefId::TraitId(id) => { let trait_ref = id.lookup(db).source(db).value; let idx = idx_iter.next().unwrap(); - params.insert(idx, Either::Right(ast::TraitOrAlias::Trait(trait_ref))); - } - GenericDefId::TraitAliasId(id) => { - let alias = id.lookup(db).source(db).value; - let idx = idx_iter.next().unwrap(); - params.insert(idx, Either::Right(ast::TraitOrAlias::TraitAlias(alias))); + params.insert(idx, Either::Right(trait_ref)); } _ => {} }
diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs index 4a9af01..ec34461 100644 --- a/crates/hir-expand/src/builtin/fn_macro.rs +++ b/crates/hir-expand/src/builtin/fn_macro.rs
@@ -555,12 +555,11 @@ // FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses // to ensure the right parsing order, so skip the parentheses here. Ideally we'd // implement rustc's model. cc https://github.com/rust-lang/rust-analyzer/pull/10623 - if let TtElement::Subtree(subtree, subtree_iter) = &t { - if let [tt::TokenTree::Leaf(tt)] = subtree_iter.remaining().flat_tokens() { - if subtree.delimiter.kind == tt::DelimiterKind::Parenthesis { - t = TtElement::Leaf(tt); - } - } + if let TtElement::Subtree(subtree, subtree_iter) = &t + && let [tt::TokenTree::Leaf(tt)] = subtree_iter.remaining().flat_tokens() + && subtree.delimiter.kind == tt::DelimiterKind::Parenthesis + { + t = TtElement::Leaf(tt); } match t { TtElement::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => { @@ -891,7 +890,7 @@ }; let text = db.file_text(file_id.file_id(db)); - let text = &*text.text(db); + let text = &**text.text(db); ExpandResult::ok(quote!(call_site =>#text)) }
diff --git a/crates/hir-expand/src/cfg_process.rs b/crates/hir-expand/src/cfg_process.rs index c6ea4a3..d5ebd6e 100644 --- a/crates/hir-expand/src/cfg_process.rs +++ b/crates/hir-expand/src/cfg_process.rs
@@ -334,10 +334,10 @@ _ => Some(CfgExpr::Atom(CfgAtom::Flag(name))), }, }; - if let Some(NodeOrToken::Token(element)) = iter.peek() { - if element.kind() == syntax::T![,] { - iter.next(); - } + if let Some(NodeOrToken::Token(element)) = iter.peek() + && element.kind() == syntax::T![,] + { + iter.next(); } result }
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs index 6730b33..a7f3e27 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs
@@ -99,6 +99,16 @@ pub fn into_file_id(self, db: &dyn ExpandDatabase) -> FileRangeWrapper<FileId> { FileRangeWrapper { file_id: self.file_id.file_id(db), range: self.range } } + + #[inline] + pub fn file_text(self, db: &dyn ExpandDatabase) -> &triomphe::Arc<str> { + db.file_text(self.file_id.file_id(db)).text(db) + } + + #[inline] + pub fn text(self, db: &dyn ExpandDatabase) -> &str { + &self.file_text(db)[self.range] + } } /// `AstId` points to an AST node in any file.
diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 4a4a3e5..fe77e15 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs
@@ -280,8 +280,8 @@ } }, ast::RecordExprField(it) => { - if let Some(colon) = it.colon_token() { - if it.name_ref().is_some() && it.expr().is_none() { + if let Some(colon) = it.colon_token() + && it.name_ref().is_some() && it.expr().is_none() { append.insert(colon.into(), vec![ Leaf::Ident(Ident { sym: sym::__ra_fixup, @@ -290,11 +290,10 @@ }) ]); } - } }, ast::Path(it) => { - if let Some(colon) = it.coloncolon_token() { - if it.segment().is_none() { + if let Some(colon) = it.coloncolon_token() + && it.segment().is_none() { append.insert(colon.into(), vec![ Leaf::Ident(Ident { sym: sym::__ra_fixup, @@ -303,7 +302,6 @@ }) ]); } - } }, ast::ClosureExpr(it) => { if it.body().is_none() {
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index ac61b22..472ec83 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs
@@ -365,12 +365,11 @@ HirFileId::FileId(id) => break id, HirFileId::MacroFile(file) => { let loc = db.lookup_intern_macro_call(file); - if loc.def.is_include() { - if let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind { - if let Ok(it) = include_input_to_file_id(db, file, &eager.arg) { - break it; - } - } + 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) + { + break it; } self = loc.kind.file_id(); } @@ -648,12 +647,11 @@ db: &dyn ExpandDatabase, macro_call_id: MacroCallId, ) -> Option<EditionedFileId> { - if self.def.is_include() { - if let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind { - if let Ok(it) = include_input_to_file_id(db, macro_call_id, &eager.arg) { - return Some(it); - } - } + if self.def.is_include() + && let MacroCallKind::FnLike { eager: Some(eager), .. } = &self.kind + && let Ok(it) = include_input_to_file_id(db, macro_call_id, &eager.arg) + { + return Some(it); } None
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index 9f1e387..d84d978 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs
@@ -273,16 +273,17 @@ // Basically, even in rustc it is quite hacky: // https://github.com/rust-lang/rust/blob/614f273e9388ddd7804d5cbc80b8865068a3744e/src/librustc_resolve/macros.rs#L456 // We follow what it did anyway :) - if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain { - if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { - let syn_ctx = span_for_range(segment.syntax().text_range()); - if let Some(macro_call_id) = syn_ctx.outer_expn(db) { - if db.lookup_intern_macro_call(macro_call_id.into()).def.local_inner { - mod_path.kind = match resolve_crate_root(db, syn_ctx) { - Some(crate_root) => PathKind::DollarCrate(crate_root), - None => PathKind::Crate, - } - } + if mod_path.segments.len() == 1 + && mod_path.kind == PathKind::Plain + && let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) + { + 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 + { + mod_path.kind = match resolve_crate_root(db, syn_ctx) { + Some(crate_root) => PathKind::DollarCrate(crate_root), + None => PathKind::Crate, } } }
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index 7cc0a26..6418211 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml
@@ -40,7 +40,14 @@ ra-ap-rustc_abi.workspace = true ra-ap-rustc_index.workspace = true ra-ap-rustc_pattern_analysis.workspace = true +ra-ap-rustc_ast_ir.workspace = true +ra-ap-rustc_type_ir.workspace = true +ra-ap-rustc_next_trait_solver.workspace = true +# These moved to dev deps if `setup_tracing` was a macro and dependents also +# included these +tracing-subscriber.workspace = true +tracing-tree.workspace = true # local deps stdx.workspace = true @@ -53,9 +60,6 @@ [dev-dependencies] expect-test = "1.5.1" -tracing.workspace = true -tracing-subscriber.workspace = true -tracing-tree.workspace = true project-model.workspace = true # local deps
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs index cc8f7bf..4ea0156 100644 --- a/crates/hir-ty/src/autoderef.rs +++ b/crates/hir-ty/src/autoderef.rs
@@ -197,10 +197,11 @@ // effectively bump the MSRV of rust-analyzer to 1.84 due to 1.83 and below lacking the // blanked impl on `Deref`. #[expect(clippy::overly_complex_bool_expr)] - if use_receiver_trait && false { - if let Some(receiver) = LangItem::Receiver.resolve_trait(db, table.trait_env.krate) { - return Some(receiver); - } + if use_receiver_trait + && false + && let Some(receiver) = LangItem::Receiver.resolve_trait(db, table.trait_env.krate) + { + return Some(receiver); } // Old rustc versions might not have `Receiver` trait. // Fallback to `Deref` if they don't @@ -224,7 +225,9 @@ // Check that the type implements Deref at all let trait_ref = projection.trait_ref(db); let implements_goal: Goal = trait_ref.cast(Interner); - table.try_obligation(implements_goal.clone())?; + if table.try_obligation(implements_goal.clone()).no_solution() { + return None; + } table.register_obligation(implements_goal);
diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs index 77d15a7..8af8fb7 100644 --- a/crates/hir-ty/src/builder.rs +++ b/crates/hir-ty/src/builder.rs
@@ -309,11 +309,11 @@ if let Some(defaults) = defaults.get(self.vec.len()..) { for default_ty in defaults { // NOTE(skip_binders): we only check if the arg type is error type. - if let Some(x) = default_ty.skip_binders().ty(Interner) { - if x.is_unknown() { - self.vec.push(fallback().cast(Interner)); - continue; - } + if let Some(x) = default_ty.skip_binders().ty(Interner) + && x.is_unknown() + { + self.vec.push(fallback().cast(Interner)); + continue; } // Each default can only depend on the previous parameters. self.vec.push(default_ty.clone().substitute(Interner, &*self.vec).cast(Interner));
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index 26b6352..f523a5e 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs
@@ -1,47 +1,36 @@ //! The implementation of `RustIrDatabase` for Chalk, which provides information //! about the code that Chalk needs. -use core::ops; -use std::{iter, ops::ControlFlow, sync::Arc}; +use std::sync::Arc; -use hir_expand::name::Name; -use intern::sym; -use span::Edition; use tracing::debug; -use chalk_ir::{CanonicalVarKinds, cast::Caster, fold::shift::Shift}; -use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; +use chalk_ir::{cast::Caster, fold::shift::Shift}; +use chalk_solve::rust_ir::{self, WellKnownTrait}; use base_db::Crate; use hir_def::{ - AssocItemId, BlockId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, - TypeAliasId, VariantId, - hir::Movability, + AssocItemId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, + VariantId, lang_item::LangItem, signatures::{ImplFlags, StructFlags, TraitFlags}, }; use crate::{ - AliasEq, AliasTy, BoundVar, DebruijnIndex, Interner, ProjectionTy, ProjectionTyExt, - QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, - WhereClause, - db::{HirDatabase, InternedCoroutine}, - from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, + AliasEq, AliasTy, DebruijnIndex, Interner, ProjectionTyExt, QuantifiedWhereClause, + Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause, + db::HirDatabase, + from_assoc_type_id, from_chalk_trait_id, generics::generics, lower::LifetimeElisionKind, - make_binders, make_single_type_binders, + make_binders, mapping::{ToChalk, TypeAliasAsValue, from_chalk}, - method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TraitImpls, TyFingerprint}, to_assoc_type_id, to_chalk_trait_id, - traits::ChalkContext, - utils::ClosureSubst, - wrap_empty_binders, }; pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>; pub(crate) type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>; pub(crate) type AdtDatum = chalk_solve::rust_ir::AdtDatum<Interner>; pub(crate) type ImplDatum = chalk_solve::rust_ir::ImplDatum<Interner>; -pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>; pub(crate) type AssocTypeId = chalk_ir::AssocTypeId<Interner>; pub(crate) type TraitId = chalk_ir::TraitId<Interner>; @@ -52,551 +41,6 @@ pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; pub(crate) type Variances = chalk_ir::Variances<Interner>; -impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> { - fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> { - self.db.associated_ty_data(from_assoc_type_id(id)) - } - fn associated_ty_from_impl( - &self, - impl_id: chalk_ir::ImplId<Interner>, - assoc_type_id: chalk_ir::AssocTypeId<Interner>, - ) -> Option<rust_ir::AssociatedTyValueId<Interner>> { - let alias_id = from_assoc_type_id(assoc_type_id); - let trait_sig = self.db.type_alias_signature(alias_id); - hir_def::ImplId::from_chalk(self.db, impl_id).impl_items(self.db).items.iter().find_map( - |(name, item)| match item { - AssocItemId::TypeAliasId(alias) if &trait_sig.name == name => { - Some(TypeAliasAsValue(*alias).to_chalk(self.db)) - } - _ => None, - }, - ) - } - fn trait_datum(&self, trait_id: TraitId) -> Arc<TraitDatum> { - self.db.trait_datum(self.krate, trait_id) - } - fn adt_datum(&self, struct_id: AdtId) -> Arc<AdtDatum> { - self.db.adt_datum(self.krate, struct_id) - } - fn adt_repr(&self, _struct_id: AdtId) -> Arc<rust_ir::AdtRepr<Interner>> { - // FIXME: keep track of these - Arc::new(rust_ir::AdtRepr { c: false, packed: false, int: None }) - } - fn discriminant_type(&self, ty: chalk_ir::Ty<Interner>) -> chalk_ir::Ty<Interner> { - if let chalk_ir::TyKind::Adt(id, _) = ty.kind(Interner) { - if let hir_def::AdtId::EnumId(e) = id.0 { - let enum_data = self.db.enum_signature(e); - let ty = enum_data.repr.unwrap_or_default().discr_type(); - return chalk_ir::TyKind::Scalar(match ty { - hir_def::layout::IntegerType::Pointer(is_signed) => match is_signed { - true => chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize), - false => chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize), - }, - hir_def::layout::IntegerType::Fixed(size, is_signed) => match is_signed { - true => chalk_ir::Scalar::Int(match size { - hir_def::layout::Integer::I8 => chalk_ir::IntTy::I8, - hir_def::layout::Integer::I16 => chalk_ir::IntTy::I16, - hir_def::layout::Integer::I32 => chalk_ir::IntTy::I32, - hir_def::layout::Integer::I64 => chalk_ir::IntTy::I64, - hir_def::layout::Integer::I128 => chalk_ir::IntTy::I128, - }), - false => chalk_ir::Scalar::Uint(match size { - hir_def::layout::Integer::I8 => chalk_ir::UintTy::U8, - hir_def::layout::Integer::I16 => chalk_ir::UintTy::U16, - hir_def::layout::Integer::I32 => chalk_ir::UintTy::U32, - hir_def::layout::Integer::I64 => chalk_ir::UintTy::U64, - hir_def::layout::Integer::I128 => chalk_ir::UintTy::U128, - }), - }, - }) - .intern(Interner); - } - } - chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8)).intern(Interner) - } - fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> { - self.db.impl_datum(self.krate, impl_id) - } - - fn fn_def_datum( - &self, - fn_def_id: chalk_ir::FnDefId<Interner>, - ) -> Arc<rust_ir::FnDefDatum<Interner>> { - self.db.fn_def_datum(from_chalk(self.db, fn_def_id)) - } - - fn impls_for_trait( - &self, - trait_id: TraitId, - parameters: &[chalk_ir::GenericArg<Interner>], - binders: &CanonicalVarKinds<Interner>, - ) -> Vec<ImplId> { - debug!("impls_for_trait {:?}", trait_id); - let trait_: hir_def::TraitId = from_chalk_trait_id(trait_id); - - let ty: Ty = parameters[0].assert_ty_ref(Interner).clone(); - - fn binder_kind( - ty: &Ty, - binders: &CanonicalVarKinds<Interner>, - ) -> Option<chalk_ir::TyVariableKind> { - if let TyKind::BoundVar(bv) = ty.kind(Interner) { - let binders = binders.as_slice(Interner); - if bv.debruijn == DebruijnIndex::INNERMOST { - if let chalk_ir::VariableKind::Ty(tk) = binders[bv.index].kind { - return Some(tk); - } - } - } - None - } - - let self_ty_fp = TyFingerprint::for_trait_impl(&ty); - let fps: &[TyFingerprint] = match binder_kind(&ty, binders) { - Some(chalk_ir::TyVariableKind::Integer) => &ALL_INT_FPS, - Some(chalk_ir::TyVariableKind::Float) => &ALL_FLOAT_FPS, - _ => self_ty_fp.as_slice(), - }; - - let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); - - let mut result = vec![]; - if fps.is_empty() { - debug!("Unrestricted search for {:?} impls...", trait_); - _ = self.for_trait_impls(trait_, self_ty_fp, |impls| { - result.extend(impls.for_trait(trait_).map(id_to_chalk)); - ControlFlow::Continue(()) - }); - } else { - _ = - self.for_trait_impls(trait_, self_ty_fp, |impls| { - result.extend(fps.iter().flat_map(move |fp| { - impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) - })); - ControlFlow::Continue(()) - }); - }; - - debug!("impls_for_trait returned {} impls", result.len()); - result - } - - fn impl_provided_for(&self, auto_trait_id: TraitId, kind: &chalk_ir::TyKind<Interner>) -> bool { - debug!("impl_provided_for {:?}, {:?}", auto_trait_id, kind); - - let trait_id = from_chalk_trait_id(auto_trait_id); - let self_ty = kind.clone().intern(Interner); - // We cannot filter impls by `TyFingerprint` for the following types: - let self_ty_fp = match kind { - // because we need to find any impl whose Self type is a ref with the same mutability - // (we don't care about the inner type). - TyKind::Ref(..) => None, - // because we need to find any impl whose Self type is a tuple with the same arity. - TyKind::Tuple(..) => None, - _ => TyFingerprint::for_trait_impl(&self_ty), - }; - - let check_kind = |impl_id| { - let impl_self_ty = self.db.impl_self_ty(impl_id); - // NOTE(skip_binders): it's safe to skip binders here as we don't check substitutions. - let impl_self_kind = impl_self_ty.skip_binders().kind(Interner); - - match (kind, impl_self_kind) { - (TyKind::Adt(id_a, _), TyKind::Adt(id_b, _)) => id_a == id_b, - (TyKind::AssociatedType(id_a, _), TyKind::AssociatedType(id_b, _)) => id_a == id_b, - (TyKind::Scalar(scalar_a), TyKind::Scalar(scalar_b)) => scalar_a == scalar_b, - (TyKind::Error, TyKind::Error) - | (TyKind::Str, TyKind::Str) - | (TyKind::Slice(_), TyKind::Slice(_)) - | (TyKind::Never, TyKind::Never) - | (TyKind::Array(_, _), TyKind::Array(_, _)) => true, - (TyKind::Tuple(arity_a, _), TyKind::Tuple(arity_b, _)) => arity_a == arity_b, - (TyKind::OpaqueType(id_a, _), TyKind::OpaqueType(id_b, _)) => id_a == id_b, - (TyKind::FnDef(id_a, _), TyKind::FnDef(id_b, _)) => id_a == id_b, - (TyKind::Ref(id_a, _, _), TyKind::Ref(id_b, _, _)) - | (TyKind::Raw(id_a, _), TyKind::Raw(id_b, _)) => id_a == id_b, - (TyKind::Closure(id_a, _), TyKind::Closure(id_b, _)) => id_a == id_b, - (TyKind::Coroutine(id_a, _), TyKind::Coroutine(id_b, _)) - | (TyKind::CoroutineWitness(id_a, _), TyKind::CoroutineWitness(id_b, _)) => { - id_a == id_b - } - (TyKind::Foreign(id_a), TyKind::Foreign(id_b)) => id_a == id_b, - (_, _) => false, - } - }; - - if let Some(fp) = self_ty_fp { - self.for_trait_impls(trait_id, self_ty_fp, |impls| { - match impls.for_trait_and_self_ty(trait_id, fp).any(check_kind) { - true => ControlFlow::Break(()), - false => ControlFlow::Continue(()), - } - }) - } else { - self.for_trait_impls(trait_id, self_ty_fp, |impls| { - match impls.for_trait(trait_id).any(check_kind) { - true => ControlFlow::Break(()), - false => ControlFlow::Continue(()), - } - }) - } - .is_break() - } - - fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { - self.db.associated_ty_value(self.krate, id) - } - - fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> { - vec![] - } - fn local_impls_to_coherence_check(&self, _trait_id: TraitId) -> Vec<ImplId> { - // We don't do coherence checking (yet) - unimplemented!() - } - fn interner(&self) -> Interner { - Interner - } - fn well_known_trait_id( - &self, - well_known_trait: WellKnownTrait, - ) -> Option<chalk_ir::TraitId<Interner>> { - let lang_item = lang_item_from_well_known_trait(well_known_trait); - let trait_ = lang_item.resolve_trait(self.db, self.krate)?; - Some(to_chalk_trait_id(trait_)) - } - fn well_known_assoc_type_id( - &self, - assoc_type: rust_ir::WellKnownAssocType, - ) -> Option<chalk_ir::AssocTypeId<Interner>> { - let lang_item = match assoc_type { - rust_ir::WellKnownAssocType::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput, - }; - let alias = lang_item.resolve_type_alias(self.db, self.krate)?; - Some(to_assoc_type_id(alias)) - } - - fn program_clauses_for_env( - &self, - environment: &chalk_ir::Environment<Interner>, - ) -> chalk_ir::ProgramClauses<Interner> { - self.db.program_clauses_for_chalk_env(self.krate, self.block, environment.clone()) - } - - fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId<Interner>) -> Arc<OpaqueTyDatum> { - let full_id = self.db.lookup_intern_impl_trait_id(id.into()); - let bound = match full_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let datas = self - .db - .return_type_impl_traits(func) - .expect("impl trait id without impl traits"); - let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); - let data = &datas.impl_traits[idx]; - let bound = OpaqueTyDatumBound { - bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - chalk_ir::Binders::new(binders, bound) - } - crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => { - let datas = self - .db - .type_alias_impl_traits(alias) - .expect("impl trait id without impl traits"); - let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); - let data = &datas.impl_traits[idx]; - let bound = OpaqueTyDatumBound { - bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - chalk_ir::Binders::new(binders, bound) - } - crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { - if let Some((future_trait, future_output)) = - LangItem::Future.resolve_trait(self.db, self.krate).and_then(|trait_| { - let alias = trait_ - .trait_items(self.db) - .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; - Some((trait_, alias)) - }) - { - // Making up Symbol’s value as variable is void: AsyncBlock<T>: - // - // |--------------------OpaqueTyDatum-------------------| - // |-------------OpaqueTyDatumBound--------------| - // for<T> <Self> [Future<Self>, Future::Output<Self> = T] - // ^1 ^0 ^0 ^0 ^1 - let impl_bound = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(future_trait), - // Self type as the first parameter. - substitution: Substitution::from1( - Interner, - TyKind::BoundVar(BoundVar { - debruijn: DebruijnIndex::INNERMOST, - index: 0, - }) - .intern(Interner), - ), - }); - let mut binder = vec![]; - binder.push(crate::wrap_empty_binders(impl_bound)); - let sized_trait = LangItem::Sized.resolve_trait(self.db, self.krate); - if let Some(sized_trait_) = sized_trait { - let sized_bound = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(sized_trait_), - // Self type as the first parameter. - substitution: Substitution::from1( - Interner, - TyKind::BoundVar(BoundVar { - debruijn: DebruijnIndex::INNERMOST, - index: 0, - }) - .intern(Interner), - ), - }); - binder.push(crate::wrap_empty_binders(sized_bound)); - } - let proj_bound = WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(ProjectionTy { - associated_ty_id: to_assoc_type_id(future_output), - // Self type as the first parameter. - substitution: Substitution::from1( - Interner, - TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) - .intern(Interner), - ), - }), - // The parameter of the opaque type. - ty: TyKind::BoundVar(BoundVar { debruijn: DebruijnIndex::ONE, index: 0 }) - .intern(Interner), - }); - binder.push(crate::wrap_empty_binders(proj_bound)); - let bound = OpaqueTyDatumBound { - bounds: make_single_type_binders(binder), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - // The opaque type has 1 parameter. - make_single_type_binders(bound) - } else { - // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. - let bound = OpaqueTyDatumBound { - bounds: chalk_ir::Binders::empty(Interner, vec![]), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - // The opaque type has 1 parameter. - make_single_type_binders(bound) - } - } - }; - - Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound }) - } - - fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> { - // FIXME: actually provide the hidden type; it is relevant for auto traits - TyKind::Error.intern(Interner) - } - - // object safety was renamed to dyn-compatibility but still remains here in chalk. - // This will be removed since we are going to migrate to next-gen trait solver. - fn is_object_safe(&self, trait_id: chalk_ir::TraitId<Interner>) -> bool { - let trait_ = from_chalk_trait_id(trait_id); - crate::dyn_compatibility::dyn_compatibility(self.db, trait_).is_none() - } - - fn closure_kind( - &self, - _closure_id: chalk_ir::ClosureId<Interner>, - _substs: &chalk_ir::Substitution<Interner>, - ) -> rust_ir::ClosureKind { - // Fn is the closure kind that implements all three traits - rust_ir::ClosureKind::Fn - } - fn closure_inputs_and_output( - &self, - _closure_id: chalk_ir::ClosureId<Interner>, - substs: &chalk_ir::Substitution<Interner>, - ) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> { - let sig_ty = ClosureSubst(substs).sig_ty(); - let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr"); - let io = rust_ir::FnDefInputsAndOutputDatum { - argument_types: sig.params().to_vec(), - return_type: sig.ret().clone(), - }; - chalk_ir::Binders::empty(Interner, io.shifted_in(Interner)) - } - fn closure_upvars( - &self, - _closure_id: chalk_ir::ClosureId<Interner>, - _substs: &chalk_ir::Substitution<Interner>, - ) -> chalk_ir::Binders<chalk_ir::Ty<Interner>> { - let ty = TyBuilder::unit(); - chalk_ir::Binders::empty(Interner, ty) - } - fn closure_fn_substitution( - &self, - _closure_id: chalk_ir::ClosureId<Interner>, - _substs: &chalk_ir::Substitution<Interner>, - ) -> chalk_ir::Substitution<Interner> { - Substitution::empty(Interner) - } - - fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String { - let id = from_chalk_trait_id(trait_id); - self.db.trait_signature(id).name.display(self.db, self.edition()).to_string() - } - fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String { - let edition = self.edition(); - match adt_id { - hir_def::AdtId::StructId(id) => { - self.db.struct_signature(id).name.display(self.db, edition).to_string() - } - hir_def::AdtId::EnumId(id) => { - self.db.enum_signature(id).name.display(self.db, edition).to_string() - } - hir_def::AdtId::UnionId(id) => { - self.db.union_signature(id).name.display(self.db, edition).to_string() - } - } - } - fn adt_size_align(&self, _id: chalk_ir::AdtId<Interner>) -> Arc<rust_ir::AdtSizeAlign> { - // FIXME - Arc::new(rust_ir::AdtSizeAlign::from_one_zst(false)) - } - fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId<Interner>) -> String { - let id = self.db.associated_ty_data(from_assoc_type_id(assoc_ty_id)).name; - self.db.type_alias_signature(id).name.display(self.db, self.edition()).to_string() - } - fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId<Interner>) -> String { - format!("Opaque_{:?}", opaque_ty_id.0) - } - fn fn_def_name(&self, fn_def_id: chalk_ir::FnDefId<Interner>) -> String { - format!("fn_{:?}", fn_def_id.0) - } - fn coroutine_datum( - &self, - id: chalk_ir::CoroutineId<Interner>, - ) -> Arc<chalk_solve::rust_ir::CoroutineDatum<Interner>> { - let InternedCoroutine(parent, expr) = self.db.lookup_intern_coroutine(id.into()); - - // We fill substitution with unknown type, because we only need to know whether the generic - // params are types or consts to build `Binders` and those being filled up are for - // `resume_type`, `yield_type`, and `return_type` of the coroutine in question. - let subst = TyBuilder::subst_for_coroutine(self.db, parent).fill_with_unknown().build(); - - let len = subst.len(Interner); - let input_output = rust_ir::CoroutineInputOutputDatum { - resume_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 3)) - .intern(Interner), - yield_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 2)) - .intern(Interner), - return_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 1)) - .intern(Interner), - // FIXME: calculate upvars - upvars: vec![], - }; - - let it = subst - .iter(Interner) - .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone())); - let input_output = crate::make_type_and_const_binders(it, input_output); - - let movability = match self.db.body(parent)[expr] { - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine(movability), - .. - } => movability, - _ => unreachable!("non coroutine expression interned as coroutine"), - }; - let movability = match movability { - Movability::Static => rust_ir::Movability::Static, - Movability::Movable => rust_ir::Movability::Movable, - }; - - Arc::new(rust_ir::CoroutineDatum { movability, input_output }) - } - fn coroutine_witness_datum( - &self, - id: chalk_ir::CoroutineId<Interner>, - ) -> Arc<chalk_solve::rust_ir::CoroutineWitnessDatum<Interner>> { - // FIXME: calculate inner types - let inner_types = - rust_ir::CoroutineWitnessExistential { types: wrap_empty_binders(vec![]) }; - - let InternedCoroutine(parent, _) = self.db.lookup_intern_coroutine(id.into()); - // See the comment in `coroutine_datum()` for unknown types. - let subst = TyBuilder::subst_for_coroutine(self.db, parent).fill_with_unknown().build(); - let it = subst - .iter(Interner) - .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone())); - let inner_types = crate::make_type_and_const_binders(it, inner_types); - - Arc::new(rust_ir::CoroutineWitnessDatum { inner_types }) - } - - fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> { - &self.db - } -} - -impl ChalkContext<'_> { - fn edition(&self) -> Edition { - self.krate.data(self.db).edition - } - - fn for_trait_impls( - &self, - trait_id: hir_def::TraitId, - self_ty_fp: Option<TyFingerprint>, - mut f: impl FnMut(&TraitImpls) -> ControlFlow<()>, - ) -> ControlFlow<()> { - // Note: Since we're using `impls_for_trait` and `impl_provided_for`, - // only impls where the trait can be resolved should ever reach Chalk. - // `impl_datum` relies on that and will panic if the trait can't be resolved. - let in_deps = self.db.trait_impls_in_deps(self.krate); - let in_self = self.db.trait_impls_in_crate(self.krate); - let trait_module = trait_id.module(self.db); - let type_module = match self_ty_fp { - Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db)), - Some(TyFingerprint::ForeignType(type_id)) => { - Some(from_foreign_def_id(type_id).module(self.db)) - } - Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db)), - _ => None, - }; - - let mut def_blocks = - [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())]; - - let block_impls = iter::successors(self.block, |&block_id| { - cov_mark::hit!(block_local_impls); - block_id.loc(self.db).module.containing_block() - }) - .inspect(|&block_id| { - // make sure we don't search the same block twice - def_blocks.iter_mut().for_each(|block| { - if *block == Some(block_id) { - *block = None; - } - }); - }) - .filter_map(|block_id| self.db.trait_impls_in_block(block_id)); - f(&in_self)?; - for it in in_deps.iter().map(ops::Deref::deref) { - f(it)?; - } - for it in block_impls { - f(&it)?; - } - for it in def_blocks.into_iter().flatten().filter_map(|it| self.db.trait_impls_in_block(it)) - { - f(&it)?; - } - ControlFlow::Continue(()) - } -} - impl chalk_ir::UnificationDatabase<Interner> for &dyn HirDatabase { fn fn_def_variance( &self, @@ -610,15 +54,6 @@ } } -pub(crate) fn program_clauses_for_chalk_env_query( - db: &dyn HirDatabase, - krate: Crate, - block: Option<BlockId>, - environment: chalk_ir::Environment<Interner>, -) -> chalk_ir::ProgramClauses<Interner> { - chalk_solve::program_clauses_for_env(&ChalkContext { db, krate, block }, &environment) -} - pub(crate) fn associated_ty_data_query( db: &dyn HirDatabase, type_alias: TypeAliasId, @@ -749,31 +184,6 @@ }) } -fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem { - match trait_ { - WellKnownTrait::Clone => LangItem::Clone, - WellKnownTrait::CoerceUnsized => LangItem::CoerceUnsized, - WellKnownTrait::Copy => LangItem::Copy, - WellKnownTrait::DiscriminantKind => LangItem::DiscriminantKind, - WellKnownTrait::DispatchFromDyn => LangItem::DispatchFromDyn, - WellKnownTrait::Drop => LangItem::Drop, - WellKnownTrait::Fn => LangItem::Fn, - WellKnownTrait::FnMut => LangItem::FnMut, - WellKnownTrait::FnOnce => LangItem::FnOnce, - WellKnownTrait::AsyncFn => LangItem::AsyncFn, - WellKnownTrait::AsyncFnMut => LangItem::AsyncFnMut, - WellKnownTrait::AsyncFnOnce => LangItem::AsyncFnOnce, - WellKnownTrait::Coroutine => LangItem::Coroutine, - WellKnownTrait::Sized => LangItem::Sized, - WellKnownTrait::Tuple => LangItem::Tuple, - WellKnownTrait::Unpin => LangItem::Unpin, - WellKnownTrait::Unsize => LangItem::Unsize, - WellKnownTrait::Pointee => LangItem::PointeeTrait, - WellKnownTrait::FnPtr => LangItem::FnPtrTrait, - WellKnownTrait::Future => LangItem::Future, - } -} - pub(crate) fn adt_datum_query( db: &dyn HirDatabase, krate: Crate,
diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 836cc96..8fa1aff 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs
@@ -14,10 +14,9 @@ use crate::{ AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy, - QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause, - db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, - from_placeholder_idx, generics::generics, mapping::ToChalk, to_chalk_trait_id, - utils::ClosureSubst, + QuantifiedWhereClause, Substitution, ToChalk, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, + WhereClause, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, + from_placeholder_idx, generics::generics, to_chalk_trait_id, utils::ClosureSubst, }; pub trait TyExt { @@ -371,7 +370,7 @@ value: InEnvironment::new(&env.env, trait_ref.cast(Interner)), binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(crate_id, None, goal).is_some() + !db.trait_solve(crate_id, None, goal).no_solution() } fn equals_ctor(&self, other: &Ty) -> bool {
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 14b9cd2..f30ec83 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs
@@ -342,10 +342,10 @@ return c; } } - if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) { - if let Ok((Ok(result), _)) = interpret_mir(db, Arc::new(mir_body), true, None) { - return result; - } + if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) + && let Ok((Ok(result), _)) = interpret_mir(db, Arc::new(mir_body), true, None) + { + return result; } unknown_const(infer[expr].clone()) }
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 6449a4d..22b152f 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs
@@ -11,7 +11,7 @@ use crate::{ Const, ConstScalar, Interner, MemoryMap, consteval::try_const_usize, db::HirDatabase, - display::DisplayTarget, mir::pad16, test_db::TestDB, + display::DisplayTarget, mir::pad16, setup_tracing, test_db::TestDB, }; use super::{ @@ -116,6 +116,7 @@ } fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Const, ConstEvalError> { + let _tracing = setup_tracing(); let module_id = db.module_for_file(file_id.file_id(db)); let def_map = module_id.def_map(db); let scope = &def_map[module_id.local_id].scope;
diff --git a/crates/hir-ty/src/consteval_nextsolver.rs b/crates/hir-ty/src/consteval_nextsolver.rs new file mode 100644 index 0000000..da4aff5 --- /dev/null +++ b/crates/hir-ty/src/consteval_nextsolver.rs
@@ -0,0 +1,256 @@ +//! Constant evaluation details +// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir +#![allow(unused)] + +use base_db::Crate; +use hir_def::{ + EnumVariantId, GeneralConstId, + expr_store::{Body, HygieneId, path::Path}, + hir::{Expr, ExprId}, + resolver::{Resolver, ValueNs}, + type_ref::LiteralConstRef, +}; +use hir_expand::Lookup; +use rustc_type_ir::{ + UnevaluatedConst, + inherent::{IntoKind, SliceLike}, +}; +use stdx::never; +use triomphe::Arc; + +use crate::{ + ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment, + consteval::ConstEvalError, + db::HirDatabase, + generics::Generics, + infer::InferenceContext, + next_solver::{ + Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, + ParamConst, SolverDefId, Ty, ValueConst, + mapping::{ChalkToNextSolver, convert_args_for_result, convert_binder_to_early_binder}, + }, +}; + +use super::mir::{interpret_mir, lower_to_mir, pad16}; + +pub(crate) fn path_to_const<'a, 'g>( + db: &'a dyn HirDatabase, + resolver: &Resolver<'a>, + path: &Path, + args: impl FnOnce() -> &'g Generics, + expected_ty: Ty<'a>, +) -> Option<Const<'a>> { + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { + Some(ValueNs::GenericParam(p)) => { + let args = args(); + match args + .type_or_const_param(p.into()) + .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone()))) + { + Some((idx, _param)) => { + Some(Const::new_param(interner, ParamConst { index: idx as u32 })) + } + None => { + never!( + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", + args, + path, + p + ); + None + } + } + } + Some(ValueNs::ConstId(c)) => { + let args = GenericArgs::new_from_iter(interner, []); + Some(Const::new( + interner, + rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( + SolverDefId::ConstId(c), + args, + )), + )) + } + _ => None, + } +} + +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() +} + +/// Interns a constant scalar with the given type +pub fn intern_const_ref<'a>( + db: &'a dyn HirDatabase, + value: &LiteralConstRef, + ty: Ty<'a>, + krate: Crate, +) -> Const<'a> { + let interner = DbInterner::new_with(db, Some(krate), None); + let layout = db.layout_of_ty_ns(ty, TraitEnvironment::empty(krate)); + let kind = match value { + LiteralConstRef::Int(i) => { + // FIXME: We should handle failure of layout better. + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()), + )) + } + LiteralConstRef::UInt(i) => { + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()), + )) + } + LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(Box::new([*b as u8]), MemoryMap::default()), + )), + LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes((*c as u32).to_le_bytes().into(), MemoryMap::default()), + )), + LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + }; + Const::new(interner, kind) +} + +/// Interns a possibly-unknown target usize +pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const<'db> { + intern_const_ref( + db, + &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt), + Ty::new_uint(DbInterner::new_with(db, Some(krate), None), rustc_type_ir::UintTy::Usize), + krate, + ) +} + +pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<u128> { + let interner = DbInterner::new_with(db, None, None); + match (*c).kind() { + ConstKind::Param(_) => None, + ConstKind::Infer(_) => None, + ConstKind::Bound(_, _) => None, + ConstKind::Placeholder(_) => None, + ConstKind::Unevaluated(unevaluated_const) => { + let c = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); + try_const_usize(db, &ec) + } + ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().0, false))), + ConstKind::Error(_) => None, + ConstKind::Expr(_) => None, + } +} + +pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<i128> { + let interner = DbInterner::new_with(db, None, None); + match (*c).kind() { + ConstKind::Param(_) => None, + ConstKind::Infer(_) => None, + ConstKind::Bound(_, _) => None, + ConstKind::Placeholder(_) => None, + ConstKind::Unevaluated(unevaluated_const) => { + let c = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); + try_const_isize(db, &ec) + } + ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().0, true))), + ConstKind::Error(_) => None, + ConstKind::Expr(_) => None, + } +} + +pub(crate) fn const_eval_discriminant_variant( + db: &dyn HirDatabase, + variant_id: EnumVariantId, +) -> Result<i128, ConstEvalError> { + let interner = DbInterner::new_with(db, None, None); + let def = variant_id.into(); + let body = db.body(def); + let loc = variant_id.lookup(db); + if matches!(body[body.body_expr], Expr::Missing) { + let prev_idx = loc.index.checked_sub(1); + let value = match prev_idx { + Some(prev_idx) => { + 1 + db.const_eval_discriminant( + loc.parent.enum_variants(db).variants[prev_idx as usize].0, + )? + } + _ => 0, + }; + return Ok(value); + } + + let repr = db.enum_signature(loc.parent).repr; + let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); + + let mir_body = db.monomorphized_mir_body( + def, + Substitution::empty(Interner), + db.trait_environment_for_body(def), + )?; + let c = interpret_mir(db, mir_body, false, None)?.0?; + let c = c.to_nextsolver(interner); + let c = if is_signed { + try_const_isize(db, &c).unwrap() + } else { + try_const_usize(db, &c).unwrap() as i128 + }; + 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 interner = DbInterner::new_with(ctx.db, None, None); + let infer = ctx.clone().resolve_all(); + fn has_closure(body: &Body, expr: ExprId) -> bool { + if matches!(body[expr], Expr::Closure { .. }) { + return true; + } + let mut r = false; + body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx)); + r + } + if has_closure(ctx.body, expr) { + // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. + return unknown_const(infer[expr].clone().to_nextsolver(interner)); + } + if let Expr::Path(p) = &ctx.body[expr] { + let resolver = &ctx.resolver; + if let Some(c) = path_to_const( + ctx.db, + resolver, + p, + || ctx.generics(), + infer[expr].to_nextsolver(interner), + ) { + return c; + } + } + if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) + && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None) + { + return result.to_nextsolver(interner); + } + unknown_const(infer[expr].to_nextsolver(interner)) +}
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index b3d4684..6e24aea 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs
@@ -3,7 +3,7 @@ use std::sync; -use base_db::{Crate, impl_intern_key}; +use base_db::Crate; use hir_def::{ AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, @@ -17,7 +17,7 @@ use triomphe::Arc; use crate::{ - Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Interner, PolyFnSig, Substitution, + Binders, Const, ImplTraitId, ImplTraits, InferenceResult, PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, chalk_db, consteval::ConstEvalError, drop::DropGlue, @@ -26,6 +26,7 @@ lower::{Diagnostics, GenericDefaults, GenericPredicates}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, + traits::NextTraitSolveResult, }; #[query_group::query_group] @@ -295,24 +296,162 @@ ) -> Ty; #[salsa::invoke(crate::traits::trait_solve_query)] + #[salsa::transparent] fn trait_solve( &self, krate: Crate, block: Option<BlockId>, goal: crate::Canonical<crate::InEnvironment<crate::Goal>>, - ) -> Option<crate::Solution>; - - #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] - fn program_clauses_for_chalk_env( - &self, - krate: Crate, - block: Option<BlockId>, - env: chalk_ir::Environment<Interner>, - ) -> chalk_ir::ProgramClauses<Interner>; + ) -> NextTraitSolveResult; #[salsa::invoke(crate::drop::has_drop_glue)] #[salsa::cycle(cycle_result = crate::drop::has_drop_glue_cycle_result)] fn has_drop_glue(&self, ty: Ty, env: Arc<TraitEnvironment>) -> DropGlue; + + // next trait solver + + #[salsa::invoke(crate::layout::layout_of_adt_ns_query)] + #[salsa::cycle(cycle_result = crate::layout::layout_of_adt_ns_cycle_result)] + fn layout_of_adt_ns<'db>( + &'db self, + def: AdtId, + args: crate::next_solver::GenericArgs<'db>, + trait_env: Arc<TraitEnvironment>, + ) -> Result<Arc<Layout>, LayoutError>; + + #[salsa::invoke(crate::layout::layout_of_ty_ns_query)] + #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_ns_cycle_result)] + fn layout_of_ty_ns<'db>( + &'db self, + ty: crate::next_solver::Ty<'db>, + env: Arc<TraitEnvironment>, + ) -> Result<Arc<Layout>, LayoutError>; + + #[salsa::invoke(crate::lower_nextsolver::ty_query)] + #[salsa::transparent] + fn ty_ns<'db>( + &'db self, + def: TyDefId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; + + #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)] + fn type_for_type_alias_with_diagnostics_ns<'db>( + &'db self, + def: TypeAliasId, + ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::impl_self_ty_with_diagnostics_cycle_result)] + fn impl_self_ty_with_diagnostics_ns<'db>( + &'db self, + def: ImplId, + ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_query)] + #[salsa::transparent] + fn impl_self_ty_ns<'db>( + &'db self, + def: ImplId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; + + // FIXME: Make this a non-interned query. + #[salsa::invoke_interned(crate::lower_nextsolver::const_param_ty_with_diagnostics_query)] + fn const_param_ty_with_diagnostics_ns<'db>( + &'db self, + def: ConstParamId, + ) -> (crate::next_solver::Ty<'db>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::const_param_ty_query)] + #[salsa::transparent] + fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> crate::next_solver::Ty<'db>; + + #[salsa::invoke(crate::lower_nextsolver::impl_trait_with_diagnostics_query)] + fn impl_trait_with_diagnostics_ns<'db>( + &'db self, + def: ImplId, + ) -> Option<( + crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>, + Diagnostics, + )>; + + #[salsa::invoke(crate::lower_nextsolver::impl_trait_query)] + #[salsa::transparent] + fn impl_trait_ns<'db>( + &'db self, + def: ImplId, + ) -> Option<crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>>; + + #[salsa::invoke(crate::lower_nextsolver::field_types_with_diagnostics_query)] + fn field_types_with_diagnostics_ns<'db>( + &'db self, + var: VariantId, + ) -> ( + Arc< + ArenaMap< + LocalFieldId, + crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, + >, + >, + Diagnostics, + ); + + #[salsa::invoke(crate::lower_nextsolver::field_types_query)] + #[salsa::transparent] + fn field_types_ns<'db>( + &'db self, + var: VariantId, + ) -> Arc< + ArenaMap<LocalFieldId, crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>>, + >; + + #[salsa::invoke(crate::lower_nextsolver::callable_item_signature_query)] + fn callable_item_signature_ns<'db>( + &'db self, + def: CallableDefId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::PolyFnSig<'db>>; + + #[salsa::invoke(crate::lower_nextsolver::return_type_impl_traits)] + fn return_type_impl_traits_ns<'db>( + &'db self, + def: FunctionId, + ) -> Option<Arc<crate::next_solver::EarlyBinder<'db, crate::lower_nextsolver::ImplTraits<'db>>>>; + + #[salsa::invoke(crate::lower_nextsolver::type_alias_impl_traits)] + fn type_alias_impl_traits_ns<'db>( + &'db self, + def: TypeAliasId, + ) -> Option<Arc<crate::next_solver::EarlyBinder<'db, crate::lower_nextsolver::ImplTraits<'db>>>>; + + #[salsa::invoke(crate::lower_nextsolver::generic_predicates_for_param_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::generic_predicates_for_param_cycle_result)] + fn generic_predicates_for_param_ns<'db>( + &'db self, + def: GenericDefId, + param_id: TypeOrConstParamId, + assoc_name: Option<Name>, + ) -> crate::lower_nextsolver::GenericPredicates<'db>; + + #[salsa::invoke(crate::lower_nextsolver::generic_predicates_query)] + fn generic_predicates_ns<'db>( + &'db self, + def: GenericDefId, + ) -> crate::lower_nextsolver::GenericPredicates<'db>; + + #[salsa::invoke( + crate::lower_nextsolver::generic_predicates_without_parent_with_diagnostics_query + )] + fn generic_predicates_without_parent_with_diagnostics_ns<'db>( + &'db self, + def: GenericDefId, + ) -> (crate::lower_nextsolver::GenericPredicates<'db>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::generic_predicates_without_parent_query)] + #[salsa::transparent] + fn generic_predicates_without_parent_ns<'db>( + &'db self, + def: GenericDefId, + ) -> crate::lower_nextsolver::GenericPredicates<'db>; } #[test] @@ -320,40 +459,44 @@ fn _assert_dyn_compatible(_: &dyn HirDatabase) {} } -#[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedTypeOrConstParamId { pub loc: TypeOrConstParamId, } -impl ::std::fmt::Debug for InternedTypeOrConstParamId { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - f.debug_tuple(stringify!(InternedTypeOrConstParamId)) - .field(&format_args!("{:04x}", self.0.index())) - .finish() - } -} -#[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedLifetimeParamId { pub loc: LifetimeParamId, } -impl ::std::fmt::Debug for InternedLifetimeParamId { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - f.debug_tuple(stringify!(InternedLifetimeParamId)) - .field(&format_args!("{:04x}", self.0.index())) - .finish() - } + +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedConstParamId { + pub loc: ConstParamId, } -impl_intern_key!(InternedConstParamId, ConstParamId); - -impl_intern_key!(InternedOpaqueTyId, ImplTraitId); +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedOpaqueTyId { + pub loc: ImplTraitId, +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct InternedClosure(pub DefWithBodyId, pub ExprId); -impl_intern_key!(InternedClosureId, InternedClosure); + +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedClosureId { + pub loc: InternedClosure, +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct InternedCoroutine(pub DefWithBodyId, pub ExprId); -impl_intern_key!(InternedCoroutineId, InternedCoroutine); + +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedCoroutineId { + pub loc: InternedCoroutine, +}
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index 40fe307..0815e62 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -657,10 +657,10 @@ } fn is_trait_impl_container(&self, container_id: ItemContainerId) -> bool { - if let ItemContainerId::ImplId(impl_id) = container_id { - if self.db.impl_trait(impl_id).is_some() { - return true; - } + if let ItemContainerId::ImplId(impl_id) = container_id + && self.db.impl_trait(impl_id).is_some() + { + return true; } false }
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index cc531f0..b26bd2b 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -528,15 +528,15 @@ return None; } - if *function_id == self.next_function_id? { - if let Some(prev_filter_map_expr_id) = self.prev_filter_map_expr_id { - let is_dyn_trait = self - .prev_receiver_ty - .as_ref() - .is_some_and(|it| it.strip_references().dyn_trait().is_some()); - if *receiver_expr_id == prev_filter_map_expr_id && !is_dyn_trait { - return Some(()); - } + if *function_id == self.next_function_id? + && let Some(prev_filter_map_expr_id) = self.prev_filter_map_expr_id + { + let is_dyn_trait = self + .prev_receiver_ty + .as_ref() + .is_some_and(|it| it.strip_references().dyn_trait().is_some()); + if *receiver_expr_id == prev_filter_map_expr_id && !is_dyn_trait { + return Some(()); } }
diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs index ca132fb..e803b56 100644 --- a/crates/hir-ty/src/diagnostics/match_check.rs +++ b/crates/hir-ty/src/diagnostics/match_check.rs
@@ -382,10 +382,10 @@ let subpats = (0..num_fields).map(|i| { WriteWith(move |f| { let fid = LocalFieldId::from_raw((i as u32).into()); - if let Some(p) = subpatterns.get(i) { - if p.field == fid { - return p.pattern.hir_fmt(f); - } + if let Some(p) = subpatterns.get(i) + && p.field == fid + { + return p.pattern.hir_fmt(f); } if let Some(p) = subpatterns.iter().find(|p| p.field == fid) { p.pattern.hir_fmt(f)
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index f6ad3c7..827585e 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -272,10 +272,10 @@ if let Some(func) = callee.as_fn_def(self.db) { self.check_call(current, func); } - if let TyKind::Function(fn_ptr) = callee.kind(Interner) { - if fn_ptr.sig.safety == chalk_ir::Safety::Unsafe { - self.on_unsafe_op(current.into(), UnsafetyReason::UnsafeFnCall); - } + if let TyKind::Function(fn_ptr) = callee.kind(Interner) + && fn_ptr.sig.safety == chalk_ir::Safety::Unsafe + { + self.on_unsafe_op(current.into(), UnsafetyReason::UnsafeFnCall); } } Expr::Path(path) => { @@ -346,12 +346,11 @@ Expr::Cast { .. } => self.inside_assignment = inside_assignment, Expr::Field { .. } => { self.inside_assignment = inside_assignment; - if !inside_assignment { - if let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) = + if !inside_assignment + && let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) = self.infer.field_resolution(current) - { - self.on_unsafe_op(current.into(), UnsafetyReason::UnionField); - } + { + self.on_unsafe_op(current.into(), UnsafetyReason::UnionField); } } Expr::Unsafe { statements, .. } => {
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index b3760e3..8f35a3c 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs
@@ -608,48 +608,46 @@ // if we are projection on a type parameter, check if the projection target has bounds // itself, if so, we render them directly as `impl Bound` instead of the less useful // `<Param as Trait>::Assoc` - if !f.display_kind.is_source_code() { - if let TyKind::Placeholder(idx) = self_ty.kind(Interner) { - if !f.bounds_formatting_ctx.contains(self) { - let db = f.db; - let id = from_placeholder_idx(db, *idx); - let generics = generics(db, id.parent); + if !f.display_kind.is_source_code() + && let TyKind::Placeholder(idx) = self_ty.kind(Interner) + && !f.bounds_formatting_ctx.contains(self) + { + let db = f.db; + let id = from_placeholder_idx(db, *idx); + let generics = generics(db, id.parent); - let substs = generics.placeholder_subst(db); - let bounds = db - .generic_predicates(id.parent) - .iter() - .map(|pred| pred.clone().substitute(Interner, &substs)) - .filter(|wc| match wc.skip_binders() { - WhereClause::Implemented(tr) => { - matches!( - tr.self_type_parameter(Interner).kind(Interner), - TyKind::Alias(_) - ) - } - WhereClause::TypeOutlives(t) => { - matches!(t.ty.kind(Interner), TyKind::Alias(_)) - } - // We shouldn't be here if these exist - WhereClause::AliasEq(_) => false, - WhereClause::LifetimeOutlives(_) => false, - }) - .collect::<Vec<_>>(); - if !bounds.is_empty() { - return f.format_bounds_with(self.clone(), |f| { - write_bounds_like_dyn_trait_with_prefix( - f, - "impl", - Either::Left( - &TyKind::Alias(AliasTy::Projection(self.clone())) - .intern(Interner), - ), - &bounds, - SizedByDefault::NotSized, - ) - }); - } - } + let substs = generics.placeholder_subst(db); + let bounds = db + .generic_predicates(id.parent) + .iter() + .map(|pred| pred.clone().substitute(Interner, &substs)) + .filter(|wc| { + let ty = match wc.skip_binders() { + WhereClause::Implemented(tr) => tr.self_type_parameter(Interner), + WhereClause::TypeOutlives(t) => t.ty.clone(), + // We shouldn't be here if these exist + WhereClause::AliasEq(_) | WhereClause::LifetimeOutlives(_) => { + return false; + } + }; + let TyKind::Alias(AliasTy::Projection(proj)) = ty.kind(Interner) else { + return false; + }; + proj == self + }) + .collect::<Vec<_>>(); + if !bounds.is_empty() { + return f.format_bounds_with(self.clone(), |f| { + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + Either::Left( + &TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner), + ), + &bounds, + SizedByDefault::NotSized, + ) + }); } } @@ -1860,18 +1858,13 @@ write!(f, "{}", f.db.trait_signature(trait_).name.display(f.db, f.edition()))?; f.end_location_link(); if is_fn_trait { - if let [self_, params @ ..] = trait_ref.substitution.as_slice(Interner) { - if let Some(args) = + if let [self_, params @ ..] = trait_ref.substitution.as_slice(Interner) + && let Some(args) = params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple()) - { - write!(f, "(")?; - hir_fmt_generic_arguments( - f, - args.as_slice(Interner), - self_.ty(Interner), - )?; - write!(f, ")")?; - } + { + write!(f, "(")?; + hir_fmt_generic_arguments(f, args.as_slice(Interner), self_.ty(Interner))?; + write!(f, ")")?; } } else { let params = generic_args_sans_defaults( @@ -1879,13 +1872,13 @@ Some(trait_.into()), trait_ref.substitution.as_slice(Interner), ); - if let [self_, params @ ..] = params { - if !params.is_empty() { - write!(f, "<")?; - hir_fmt_generic_arguments(f, params, self_.ty(Interner))?; - // there might be assoc type bindings, so we leave the angle brackets open - angle_open = true; - } + if let [self_, params @ ..] = params + && !params.is_empty() + { + write!(f, "<")?; + hir_fmt_generic_arguments(f, params, self_.ty(Interner))?; + // there might be assoc type bindings, so we leave the angle brackets open + angle_open = true; } } } @@ -2443,11 +2436,11 @@ generic_args.args[0].hir_fmt(f, store)?; } } - if let Some(ret) = generic_args.bindings[0].type_ref { - if !matches!(&store[ret], TypeRef::Tuple(v) if v.is_empty()) { - write!(f, " -> ")?; - ret.hir_fmt(f, store)?; - } + if let Some(ret) = generic_args.bindings[0].type_ref + && !matches!(&store[ret], TypeRef::Tuple(v) if v.is_empty()) + { + write!(f, " -> ")?; + ret.hir_fmt(f, store)?; } } hir_def::expr_store::path::GenericArgsParentheses::No => {
diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs index 5577be8..a7e942d 100644 --- a/crates/hir-ty/src/drop.rs +++ b/crates/hir-ty/src/drop.rs
@@ -187,7 +187,7 @@ value: InEnvironment::new(&env.env, trait_ref.cast(Interner)), binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(env.krate, env.block, goal).is_some() + db.trait_solve(env.krate, env.block, goal).certain() } pub(crate) fn has_drop_glue_cycle_result(
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs index 30949c8..2d21947 100644 --- a/crates/hir-ty/src/dyn_compatibility.rs +++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -16,8 +16,8 @@ use smallvec::SmallVec; use crate::{ - AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId, - ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits, + AliasEq, AliasTy, Binders, BoundVar, CallableSig, DomainGoal, GoalData, ImplTraitId, Interner, + OpaqueTyId, ProjectionTyExt, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, generics::{generics, trait_self_param_idx}, @@ -136,16 +136,15 @@ let predicates = predicates.iter().map(|p| p.skip_binders().skip_binders().clone()); elaborate_clause_supertraits(db, predicates).any(|pred| match pred { WhereClause::Implemented(trait_ref) => { - if from_chalk_trait_id(trait_ref.trait_id) == sized { - if let TyKind::BoundVar(it) = + if from_chalk_trait_id(trait_ref.trait_id) == sized + && let TyKind::BoundVar(it) = *trait_ref.self_type_parameter(Interner).kind(Interner) - { - // Since `generic_predicates` is `Binder<Binder<..>>`, the `DebrujinIndex` of - // self-parameter is `1` - return it - .index_if_bound_at(DebruijnIndex::ONE) - .is_some_and(|idx| idx == trait_self_param_idx); - } + { + // Since `generic_predicates` is `Binder<Binder<..>>`, the `DebrujinIndex` of + // self-parameter is `1` + return it + .index_if_bound_at(DebruijnIndex::ONE) + .is_some_and(|idx| idx == trait_self_param_idx); } false } @@ -401,10 +400,10 @@ cb(MethodViolationCode::ReferencesSelfOutput)?; } - if !func_data.is_async() { - if let Some(mvc) = contains_illegal_impl_trait_in_trait(db, &sig) { - cb(mvc)?; - } + if !func_data.is_async() + && let Some(mvc) = contains_illegal_impl_trait_in_trait(db, &sig) + { + cb(mvc)?; } let generic_params = db.generic_params(func.into()); @@ -511,6 +510,8 @@ trait_id: to_chalk_trait_id(unsize_did), substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]), }); + let unsized_predicate = + Binders::empty(Interner, unsized_predicate.cast::<DomainGoal>(Interner)); let trait_predicate = WhereClause::Implemented(TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: Substitution::from_iter( @@ -519,18 +520,32 @@ .chain(placeholder_subst.iter(Interner).skip(1).cloned()), ), }); + let trait_predicate = Binders::empty(Interner, trait_predicate.cast::<DomainGoal>(Interner)); let generic_predicates = &*db.generic_predicates(func.into()); - let clauses = std::iter::once(unsized_predicate) - .chain(std::iter::once(trait_predicate)) - .chain(generic_predicates.iter().map(|pred| { - pred.clone().substitute(Interner, &placeholder_subst).into_value_and_skipped_binders().0 - })) - .map(|pred| { - pred.cast::<chalk_ir::ProgramClause<Interner>>(Interner).into_from_env_clause(Interner) - }); - let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); + let goals = std::iter::once(unsized_predicate).chain(std::iter::once(trait_predicate)).chain( + generic_predicates.iter().map(|pred| { + pred.clone() + .substitute(Interner, &placeholder_subst) + .map(|g| g.cast::<DomainGoal>(Interner)) + }), + ); + let clauses = chalk_ir::ProgramClauses::from_iter( + Interner, + goals.into_iter().map(|g| { + chalk_ir::ProgramClause::new( + Interner, + chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication { + consequence: g, + conditions: chalk_ir::Goals::empty(Interner), + constraints: chalk_ir::Constraints::empty(Interner), + priority: chalk_ir::ClausePriority::High, + })), + ) + }), + ); + let env: chalk_ir::Environment<Interner> = chalk_ir::Environment { clauses }; let obligation = WhereClause::Implemented(TraitRef { trait_id: to_chalk_trait_id(dispatch_from_dyn_did), @@ -542,9 +557,8 @@ let mut table = chalk_solve::infer::InferenceTable::<Interner>::new(); let canonicalized = table.canonicalize(Interner, in_env); - let solution = db.trait_solve(krate, None, canonicalized.quantified); - matches!(solution, Some(Solution::Unique(_))) + db.trait_solve(krate, None, canonicalized.quantified).certain() } fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option<Ty> {
diff --git a/crates/hir-ty/src/dyn_compatibility/tests.rs b/crates/hir-ty/src/dyn_compatibility/tests.rs index 5078e8c..4ffa455 100644 --- a/crates/hir-ty/src/dyn_compatibility/tests.rs +++ b/crates/hir-ty/src/dyn_compatibility/tests.rs
@@ -56,18 +56,21 @@ continue; }; let mut osvs = FxHashSet::default(); - _ = dyn_compatibility_with_callback(&db, trait_id, &mut |osv| { - osvs.insert(match osv { - DynCompatibilityViolation::SizedSelf => SizedSelf, - DynCompatibilityViolation::SelfReferential => SelfReferential, - DynCompatibilityViolation::Method(_, mvc) => Method(mvc), - DynCompatibilityViolation::AssocConst(_) => AssocConst, - DynCompatibilityViolation::GAT(_) => GAT, - DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => { - HasNonCompatibleSuperTrait - } + let db = &db; + salsa::attach(db, || { + _ = dyn_compatibility_with_callback(db, trait_id, &mut |osv| { + osvs.insert(match osv { + DynCompatibilityViolation::SizedSelf => SizedSelf, + DynCompatibilityViolation::SelfReferential => SelfReferential, + DynCompatibilityViolation::Method(_, mvc) => Method(mvc), + DynCompatibilityViolation::AssocConst(_) => AssocConst, + DynCompatibilityViolation::GAT(_) => GAT, + DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => { + HasNonCompatibleSuperTrait + } + }); + ControlFlow::Continue(()) }); - ControlFlow::Continue(()) }); assert_eq!(osvs, expected, "dyn-compatibility violations for `{name}` do not match;"); }
diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs index f14872e..412b3ac 100644 --- a/crates/hir-ty/src/generics.rs +++ b/crates/hir-ty/src/generics.rs
@@ -165,6 +165,21 @@ (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)) + }) + } + pub fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> { self.find_type_or_const_param(param) } @@ -258,7 +273,7 @@ pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> Option<usize> { match def { - GenericDefId::TraitId(_) | GenericDefId::TraitAliasId(_) => { + GenericDefId::TraitId(_) => { let params = db.generic_params(def); params.trait_self_param().map(|idx| idx.into_raw().into_u32() as usize) } @@ -272,7 +287,7 @@ } } -fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> { +pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, GenericDefId::TypeAliasId(it) => it.lookup(db).container, @@ -280,8 +295,7 @@ GenericDefId::StaticId(_) | GenericDefId::AdtId(_) | GenericDefId::TraitId(_) - | GenericDefId::ImplId(_) - | GenericDefId::TraitAliasId(_) => return None, + | GenericDefId::ImplId(_) => return None, }; match container {
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 7c39afa..d778cc0 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs
@@ -88,52 +88,54 @@ /// The entry point of type inference. pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { - let _p = tracing::info_span!("infer_query").entered(); - let resolver = def.resolver(db); - let body = db.body(def); - let mut ctx = InferenceContext::new(db, def, &body, resolver); + crate::next_solver::with_new_cache(|| { + let _p = tracing::info_span!("infer_query").entered(); + let resolver = def.resolver(db); + let body = db.body(def); + let mut ctx = InferenceContext::new(db, def, &body, resolver); - match def { - DefWithBodyId::FunctionId(f) => { - ctx.collect_fn(f); - } - DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), - DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), - DefWithBodyId::VariantId(v) => { - ctx.return_ty = TyBuilder::builtin( - match db.enum_signature(v.lookup(db).parent).variant_body_type() { - hir_def::layout::IntegerType::Pointer(signed) => match signed { - true => BuiltinType::Int(BuiltinInt::Isize), - false => BuiltinType::Uint(BuiltinUint::Usize), + match def { + DefWithBodyId::FunctionId(f) => { + ctx.collect_fn(f); + } + DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), + DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), + DefWithBodyId::VariantId(v) => { + ctx.return_ty = TyBuilder::builtin( + match db.enum_signature(v.lookup(db).parent).variant_body_type() { + hir_def::layout::IntegerType::Pointer(signed) => match signed { + true => BuiltinType::Int(BuiltinInt::Isize), + false => BuiltinType::Uint(BuiltinUint::Usize), + }, + hir_def::layout::IntegerType::Fixed(size, signed) => match signed { + true => BuiltinType::Int(match size { + Integer::I8 => BuiltinInt::I8, + Integer::I16 => BuiltinInt::I16, + Integer::I32 => BuiltinInt::I32, + Integer::I64 => BuiltinInt::I64, + Integer::I128 => BuiltinInt::I128, + }), + false => BuiltinType::Uint(match size { + Integer::I8 => BuiltinUint::U8, + Integer::I16 => BuiltinUint::U16, + Integer::I32 => BuiltinUint::U32, + Integer::I64 => BuiltinUint::U64, + Integer::I128 => BuiltinUint::U128, + }), + }, }, - hir_def::layout::IntegerType::Fixed(size, signed) => match signed { - true => BuiltinType::Int(match size { - Integer::I8 => BuiltinInt::I8, - Integer::I16 => BuiltinInt::I16, - Integer::I32 => BuiltinInt::I32, - Integer::I64 => BuiltinInt::I64, - Integer::I128 => BuiltinInt::I128, - }), - false => BuiltinType::Uint(match size { - Integer::I8 => BuiltinUint::U8, - Integer::I16 => BuiltinUint::U16, - Integer::I32 => BuiltinUint::U32, - Integer::I64 => BuiltinUint::U64, - Integer::I128 => BuiltinUint::U128, - }), - }, - }, - ); + ); + } } - } - ctx.infer_body(); + ctx.infer_body(); - ctx.infer_mut_body(); + ctx.infer_mut_body(); - ctx.infer_closures(); + ctx.infer_closures(); - Arc::new(ctx.resolve_all()) + Arc::new(ctx.resolve_all()) + }) } pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc<InferenceResult> { @@ -144,6 +146,7 @@ /// /// This is appropriate to use only after type-check: it assumes /// that normalization will succeed, for example. +#[tracing::instrument(level = "debug", skip(db))] pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment>, ty: Ty) -> Ty { // FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only // works for the type case, so we check array unconditionally. Remove the array part @@ -902,12 +905,12 @@ return false; } - if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic { - if let Some(ty) = field_with_same_name { - *ty = table.resolve_completely(ty.clone()); - if ty.contains_unknown() { - *field_with_same_name = None; - } + if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic + && let Some(ty) = field_with_same_name + { + *ty = table.resolve_completely(ty.clone()); + if ty.contains_unknown() { + *field_with_same_name = None; } } } @@ -1010,12 +1013,12 @@ param_tys.push(va_list_ty); } let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.new_type_var())); - if let Some(self_param) = self.body.self_param { - if let Some(ty) = param_tys.next() { - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); - self.write_binding_ty(self_param, ty); - } + if let Some(self_param) = self.body.self_param + && let Some(ty) = param_tys.next() + { + let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); + self.write_binding_ty(self_param, ty); } let mut tait_candidates = FxHashSet::default(); for (ty, pat) in param_tys.zip(&*self.body.params) { @@ -1077,14 +1080,22 @@ // Functions might be defining usage sites of TAITs. // To define an TAITs, that TAIT must appear in the function's signatures. // So, it suffices to check for params and return types. - if self - .return_ty - .data(Interner) - .flags - .intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER)) - { - tait_candidates.insert(self.return_ty.clone()); - } + fold_tys( + self.return_ty.clone(), + |ty, _| { + match ty.kind(Interner) { + TyKind::OpaqueType(..) + | TyKind::Alias(AliasTy::Opaque(..)) + | TyKind::InferenceVar(..) => { + tait_candidates.insert(self.return_ty.clone()); + } + _ => {} + } + ty + }, + DebruijnIndex::INNERMOST, + ); + self.make_tait_coercion_table(tait_candidates.iter()); } @@ -1199,20 +1210,19 @@ ) -> std::ops::ControlFlow<Self::BreakTy> { let ty = self.table.resolve_ty_shallow(ty); - if let TyKind::OpaqueType(id, _) = ty.kind(Interner) { - if let ImplTraitId::TypeAliasImplTrait(alias_id, _) = + if let TyKind::OpaqueType(id, _) = ty.kind(Interner) + && let ImplTraitId::TypeAliasImplTrait(alias_id, _) = self.db.lookup_intern_impl_trait_id((*id).into()) - { - let loc = self.db.lookup_intern_type_alias(alias_id); - match loc.container { - ItemContainerId::ImplId(impl_id) => { - self.assocs.insert(*id, (impl_id, ty.clone())); - } - ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => { - self.non_assocs.insert(*id, ty.clone()); - } - _ => {} + { + let loc = self.db.lookup_intern_type_alias(alias_id); + match loc.container { + ItemContainerId::ImplId(impl_id) => { + self.assocs.insert(*id, (impl_id, ty.clone())); } + ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => { + self.non_assocs.insert(*id, ty.clone()); + } + _ => {} } } @@ -1780,7 +1790,6 @@ TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) - | TypeNs::TraitAliasId(_) | TypeNs::ModuleId(_) => { // FIXME diagnostic (self.err_ty(), None)
diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs index 4e95eca..f0a4167 100644 --- a/crates/hir-ty/src/infer/cast.rs +++ b/crates/hir-ty/src/infer/cast.rs
@@ -233,26 +233,25 @@ F: FnMut(ExprId, Vec<Adjustment>), { // Mutability order is opposite to rustc. `Mut < Not` - if m_expr <= m_cast { - if let TyKind::Array(ety, _) = t_expr.kind(Interner) { - // Coerce to a raw pointer so that we generate RawPtr in MIR. - let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner); - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type, CoerceNever::Yes) - { - apply_adjustments(self.source_expr, adj); - } else { - never!( - "could not cast from reference to array to pointer to array ({:?} to {:?})", - self.expr_ty, - array_ptr_type - ); - } + if m_expr <= m_cast + && let TyKind::Array(ety, _) = t_expr.kind(Interner) + { + // Coerce to a raw pointer so that we generate RawPtr in MIR. + let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner); + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type, CoerceNever::Yes) { + apply_adjustments(self.source_expr, adj); + } else { + never!( + "could not cast from reference to array to pointer to array ({:?} to {:?})", + self.expr_ty, + array_ptr_type + ); + } - // This is a less strict condition than rustc's `demand_eqtype`, - // but false negative is better than false positive - if table.coerce(ety, t_cast, CoerceNever::Yes).is_ok() { - return Ok(()); - } + // This is a less strict condition than rustc's `demand_eqtype`, + // but false negative is better than false positive + if table.coerce(ety, t_cast, CoerceNever::Yes).is_ok() { + return Ok(()); } }
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index c3029bf..d8fc20e 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs
@@ -3,12 +3,13 @@ use std::{cmp, convert::Infallible, mem, ops::ControlFlow}; use chalk_ir::{ - BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, + BoundVar, DebruijnIndex, FnSubst, GenericArg, Mutability, TyKind, cast::Cast, fold::{FallibleTypeFolder, Shift, TypeFoldable}, visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, }; use either::Either; +use hir_def::Lookup; use hir_def::{ DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, expr_store::path::Path, @@ -19,8 +20,8 @@ item_tree::FieldsShape, lang_item::LangItem, resolver::ValueNs, + type_ref::TypeRefId, }; -use hir_def::{Lookup, type_ref::TypeRefId}; use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; @@ -30,8 +31,8 @@ use crate::{ Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, - DynTyExt, FnAbi, FnPointer, FnSig, GenericArg, Interner, OpaqueTy, ProjectionTy, - ProjectionTyExt, Substitution, Ty, TyBuilder, TyExt, WhereClause, + DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTy, ProjectionTyExt, + Substitution, Ty, TyBuilder, TyExt, WhereClause, db::{HirDatabase, InternedClosure, InternedCoroutine}, error_lifetime, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx, generics::Generics, @@ -176,12 +177,12 @@ } // Deduction based on the expected `dyn Fn` is done separately. - if let TyKind::Dyn(dyn_ty) = expected_ty.kind(Interner) { - if let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) { - let expected_sig_ty = TyKind::Function(sig).intern(Interner); + if let TyKind::Dyn(dyn_ty) = expected_ty.kind(Interner) + && let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) + { + let expected_sig_ty = TyKind::Function(sig).intern(Interner); - self.unify(sig_ty, &expected_sig_ty); - } + self.unify(sig_ty, &expected_sig_ty); } } @@ -208,14 +209,13 @@ alias: AliasTy::Projection(projection_ty), ty: projected_ty, }) = bound.skip_binders() - { - if let Some(sig) = self.deduce_sig_from_projection( + && let Some(sig) = self.deduce_sig_from_projection( closure_kind, projection_ty, projected_ty, - ) { - return Some(sig); - } + ) + { + return Some(sig); } None }); @@ -254,55 +254,44 @@ let mut expected_kind = None; for clause in elaborate_clause_supertraits(self.db, clauses.rev()) { - if expected_sig.is_none() { - if let WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection), - ty, - }) = &clause - { - let inferred_sig = - self.deduce_sig_from_projection(closure_kind, projection, ty); - // Make sure that we didn't infer a signature that mentions itself. - // This can happen when we elaborate certain supertrait bounds that - // mention projections containing the `Self` type. See rust-lang/rust#105401. - struct MentionsTy<'a> { - expected_ty: &'a Ty, + if expected_sig.is_none() + && let WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection), ty }) = + &clause + { + let inferred_sig = self.deduce_sig_from_projection(closure_kind, projection, ty); + // Make sure that we didn't infer a signature that mentions itself. + // This can happen when we elaborate certain supertrait bounds that + // mention projections containing the `Self` type. See rust-lang/rust#105401. + struct MentionsTy<'a> { + expected_ty: &'a Ty, + } + impl TypeVisitor<Interner> for MentionsTy<'_> { + type BreakTy = (); + + fn interner(&self) -> Interner { + Interner } - impl TypeVisitor<Interner> for MentionsTy<'_> { - type BreakTy = (); - fn interner(&self) -> Interner { - Interner - } - - fn as_dyn( - &mut self, - ) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> - { - self - } - - fn visit_ty( - &mut self, - t: &Ty, - db: chalk_ir::DebruijnIndex, - ) -> ControlFlow<()> { - if t == self.expected_ty { - ControlFlow::Break(()) - } else { - t.super_visit_with(self, db) - } - } - } - if inferred_sig - .visit_with( - &mut MentionsTy { expected_ty }, - chalk_ir::DebruijnIndex::INNERMOST, - ) - .is_continue() + fn as_dyn( + &mut self, + ) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> { - expected_sig = inferred_sig; + self } + + fn visit_ty(&mut self, t: &Ty, db: chalk_ir::DebruijnIndex) -> ControlFlow<()> { + if t == self.expected_ty { + ControlFlow::Break(()) + } else { + t.super_visit_with(self, db) + } + } + } + if inferred_sig + .visit_with(&mut MentionsTy { expected_ty }, chalk_ir::DebruijnIndex::INNERMOST) + .is_continue() + { + expected_sig = inferred_sig; } } @@ -617,11 +606,10 @@ if let CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow, }) = current_capture + && self.projections[len..].contains(&ProjectionElem::Deref) { - if self.projections[len..].contains(&ProjectionElem::Deref) { - current_capture = - CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }); - } + current_capture = + CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }); } current_capture } @@ -1076,12 +1064,11 @@ Mutability::Mut => CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared), }; - if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) { - if let Some(place) = + if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) + && let Some(place) = apply_adjusts_to_place(&mut self.current_capture_span_stack, place, rest) - { - self.add_capture(place, capture_kind); - } + { + self.add_capture(place, capture_kind); } self.walk_expr_with_adjust(tgt_expr, rest); } @@ -1169,15 +1156,15 @@ } } self.walk_expr(*expr); - if let Some(discr_place) = self.place_of_expr(*expr) { - if self.is_upvar(&discr_place) { - let mut capture_mode = None; - for arm in arms.iter() { - self.walk_pat(&mut capture_mode, arm.pat); - } - if let Some(c) = capture_mode { - self.push_capture(discr_place, c); - } + if let Some(discr_place) = self.place_of_expr(*expr) + && self.is_upvar(&discr_place) + { + let mut capture_mode = None; + for arm in arms.iter() { + self.walk_pat(&mut capture_mode, arm.pat); + } + if let Some(c) = capture_mode { + self.push_capture(discr_place, c); } } } @@ -1209,13 +1196,11 @@ let mutability = 'b: { if let Some(deref_trait) = self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait()) - { - if let Some(deref_fn) = deref_trait + && let Some(deref_fn) = deref_trait .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::deref_mut)) - { - break 'b deref_fn == f; - } + { + break 'b deref_fn == f; } false }; @@ -1405,10 +1390,10 @@ fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { let mut ty = None; - if let Some(it) = self.result.expr_adjustments.get(&e) { - if let Some(it) = it.last() { - ty = Some(it.target.clone()); - } + if let Some(it) = self.result.expr_adjustments.get(&e) + && let Some(it) = it.last() + { + ty = Some(it.target.clone()); } ty.unwrap_or_else(|| self.expr_ty(e)) } @@ -1793,10 +1778,10 @@ } pub(super) fn add_current_closure_dependency(&mut self, dep: ClosureId) { - if let Some(c) = self.current_closure { - if !dep_creates_cycle(&self.closure_dependencies, &mut FxHashSet::default(), c, dep) { - self.closure_dependencies.entry(c).or_default().push(dep); - } + if let Some(c) = self.current_closure + && !dep_creates_cycle(&self.closure_dependencies, &mut FxHashSet::default(), c, dep) + { + self.closure_dependencies.entry(c).or_default().push(dep); } fn dep_creates_cycle(
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index 39bd908..631a91b 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs
@@ -13,14 +13,15 @@ use triomphe::Arc; use crate::{ - Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Lifetime, - Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, + Canonical, DomainGoal, FnAbi, FnPointer, FnSig, InEnvironment, Interner, Lifetime, + Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, autoderef::{Autoderef, AutoderefKind}, db::HirDatabase, infer::{ Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, TypeError, TypeMismatch, }, + traits::NextTraitSolveResult, utils::ClosureSubst, }; @@ -164,14 +165,14 @@ // - [Comment from rustc](https://github.com/rust-lang/rust/blob/5ff18d0eaefd1bd9ab8ec33dab2404a44e7631ed/compiler/rustc_hir_typeck/src/coercion.rs#L1334-L1335) // First try to coerce the new expression to the type of the previous ones, // but only if the new expression has no coercion already applied to it. - if expr.is_none_or(|expr| !ctx.result.expr_adjustments.contains_key(&expr)) { - if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), CoerceNever::Yes) { - self.final_ty = Some(res); - if let Some(expr) = expr { - self.expressions.push(expr); - } - return; + if expr.is_none_or(|expr| !ctx.result.expr_adjustments.contains_key(&expr)) + && let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), CoerceNever::Yes) + { + self.final_ty = Some(res); + if let Some(expr) = expr { + self.expressions.push(expr); } + return; } if let Ok((adjustments, res)) = @@ -322,18 +323,13 @@ // If we are coercing into a TAIT, coerce into its proxy inference var, instead. let mut to_ty = to_ty; let _to; - if let Some(tait_table) = &self.tait_coercion_table { - if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) { - if !matches!( - from_ty.kind(Interner), - TyKind::InferenceVar(..) | TyKind::OpaqueType(..) - ) { - if let Some(ty) = tait_table.get(opaque_ty_id) { - _to = ty.clone(); - to_ty = &_to; - } - } - } + if let Some(tait_table) = &self.tait_coercion_table + && let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) + && !matches!(from_ty.kind(Interner), TyKind::InferenceVar(..) | TyKind::OpaqueType(..)) + && let Some(ty) = tait_table.get(opaque_ty_id) + { + _to = ty.clone(); + to_ty = &_to; } // Consider coercing the subtype to a DST @@ -594,14 +590,13 @@ F: FnOnce(Ty) -> Vec<Adjustment>, G: FnOnce(Ty) -> Vec<Adjustment>, { - if let TyKind::Function(to_fn_ptr) = to_ty.kind(Interner) { - if let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) = + if let TyKind::Function(to_fn_ptr) = to_ty.kind(Interner) + && let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) = (from_fn_ptr.sig.safety, to_fn_ptr.sig.safety) - { - let from_unsafe = - TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(Interner); - return self.unify_and(&from_unsafe, to_ty, to_unsafe); - } + { + let from_unsafe = + TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(Interner); + return self.unify_and(&from_unsafe, to_ty, to_unsafe); } self.unify_and(&from_ty, to_ty, normal) } @@ -721,13 +716,18 @@ // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the // rest for later. Also, there's some logic about sized type variables. // Need to find out in what cases this is necessary - let solution = self - .db - .trait_solve(krate, self.trait_env.block, canonicalized.value.clone().cast(Interner)) - .ok_or(TypeError)?; + let solution = self.db.trait_solve( + krate, + self.trait_env.block, + canonicalized.value.clone().cast(Interner), + ); match solution { - Solution::Unique(v) => { + // FIXME: this is a weaker guarantee than Chalk's `Guidance::Unique` + // was. Chalk's unique guidance at least guarantees that the real solution + // is some "subset" of the solutions matching the guidance, but the + // substs for `Certainty::No` don't have that same guarantee (I think). + NextTraitSolveResult::Certain(v) => { canonicalized.apply_solution( self, Canonical { @@ -737,13 +737,12 @@ }, ); } - Solution::Ambig(Guidance::Definite(subst)) => { - // FIXME need to record an obligation here - canonicalized.apply_solution(self, subst) + // ...so, should think about how to get some actually get some guidance here + NextTraitSolveResult::Uncertain(..) | NextTraitSolveResult::NoSolution => { + return Err(TypeError); } - // FIXME actually we maybe should also accept unknown guidance here - _ => return Err(TypeError), - }; + } + let unsize = Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() }; let adjustments = match reborrow {
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index d43c99f..261c023 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs
@@ -278,6 +278,7 @@ } } + #[tracing::instrument(level = "debug", skip(self, is_read), ret)] fn infer_expr_inner( &mut self, tgt_expr: ExprId, @@ -286,7 +287,9 @@ ) -> Ty { self.db.unwind_if_revision_cancelled(); - let ty = match &self.body[tgt_expr] { + let expr = &self.body[tgt_expr]; + tracing::trace!(?expr); + 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); @@ -653,19 +656,18 @@ // FIXME: Note down method resolution her match op { UnaryOp::Deref => { - if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref) { - if let Some(deref_fn) = deref_trait + if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref) + && let Some(deref_fn) = deref_trait .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::deref)) - { - // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that - // the mutability is not wrong, and will be fixed in `self.infer_mut`). - self.write_method_resolution( - tgt_expr, - deref_fn, - Substitution::empty(Interner), - ); - } + { + // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that + // the mutability is not wrong, and will be fixed in `self.infer_mut`). + self.write_method_resolution( + tgt_expr, + deref_fn, + Substitution::empty(Interner), + ); } if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { self.resolve_ty_shallow(derefed) @@ -1387,28 +1389,28 @@ let ret_ty = match method_ty.callable_sig(self.db) { Some(sig) => { let p_left = &sig.params()[0]; - if matches!(op, BinaryOp::CmpOp(..) | BinaryOp::Assignment { .. }) { - if let TyKind::Ref(mtbl, lt, _) = p_left.kind(Interner) { - self.write_expr_adj( - lhs, - Box::new([Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)), - target: p_left.clone(), - }]), - ); - } + if matches!(op, BinaryOp::CmpOp(..) | BinaryOp::Assignment { .. }) + && let TyKind::Ref(mtbl, lt, _) = p_left.kind(Interner) + { + self.write_expr_adj( + lhs, + Box::new([Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)), + target: p_left.clone(), + }]), + ); } let p_right = &sig.params()[1]; - if matches!(op, BinaryOp::CmpOp(..)) { - if let TyKind::Ref(mtbl, lt, _) = p_right.kind(Interner) { - self.write_expr_adj( - rhs, - Box::new([Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)), - target: p_right.clone(), - }]), - ); - } + if matches!(op, BinaryOp::CmpOp(..)) + && let TyKind::Ref(mtbl, lt, _) = p_right.kind(Interner) + { + self.write_expr_adj( + rhs, + Box::new([Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), *mtbl)), + target: p_right.clone(), + }]), + ); } sig.ret().clone() } @@ -1664,14 +1666,12 @@ 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); - if !is_public { - if let Either::Left(field) = field_id { - // FIXME: Merge this diagnostic into UnresolvedField? - self.push_diagnostic(InferenceDiagnostic::PrivateField { - expr: tgt_expr, - field, - }); - } + if !is_public && let Either::Left(field) = field_id { + // FIXME: Merge this diagnostic into UnresolvedField? + self.push_diagnostic(InferenceDiagnostic::PrivateField { + expr: tgt_expr, + field, + }); } ty } @@ -1952,7 +1952,11 @@ sig.ret().clone(), sig.is_varargs, ), - None => ((self.err_ty(), Vec::new()), self.err_ty(), true), + None => { + let formal_receiver_ty = self.table.new_type_var(); + let ret_ty = self.table.new_type_var(); + ((formal_receiver_ty, Vec::new()), ret_ty, true) + } }; self.unify(&formal_receiver_ty, &receiver_ty);
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 3f7eba9..c798e9e 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs
@@ -124,53 +124,41 @@ self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) } &Expr::Index { base, index } => { - if mutability == Mutability::Mut { - if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) { - if let Some(index_trait) = - LangItem::IndexMut.resolve_trait(self.db, self.table.trait_env.krate) - { - if let Some(index_fn) = index_trait - .trait_items(self.db) - .method_by_name(&Name::new_symbol_root(sym::index_mut)) - { - *f = index_fn; - let mut base_ty = None; - let base_adjustments = self - .result - .expr_adjustments - .get_mut(&base) - .and_then(|it| it.last_mut()); - if let Some(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)), - target, - }) = base_adjustments - { - if let TyKind::Ref(_, _, ty) = target.kind(Interner) { - base_ty = Some(ty.clone()); - } - *mutability = Mutability::Mut; - } - - // Apply `IndexMut` obligation for non-assignee expr - if let Some(base_ty) = base_ty { - let index_ty = - if let Some(ty) = self.result.type_of_expr.get(index) { - ty.clone() - } else { - self.infer_expr( - index, - &Expectation::none(), - ExprIsRead::Yes, - ) - }; - let trait_ref = TyBuilder::trait_ref(self.db, index_trait) - .push(base_ty) - .fill(|_| index_ty.clone().cast(Interner)) - .build(); - self.push_obligation(trait_ref.cast(Interner)); - } - } + if mutability == Mutability::Mut + && let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) + && let Some(index_trait) = + LangItem::IndexMut.resolve_trait(self.db, self.table.trait_env.krate) + && let Some(index_fn) = index_trait + .trait_items(self.db) + .method_by_name(&Name::new_symbol_root(sym::index_mut)) + { + *f = index_fn; + let mut base_ty = None; + let base_adjustments = + self.result.expr_adjustments.get_mut(&base).and_then(|it| it.last_mut()); + if let Some(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)), + target, + }) = base_adjustments + { + if let TyKind::Ref(_, _, ty) = target.kind(Interner) { + base_ty = Some(ty.clone()); } + *mutability = Mutability::Mut; + } + + // Apply `IndexMut` obligation for non-assignee expr + if let Some(base_ty) = base_ty { + let index_ty = if let Some(ty) = self.result.type_of_expr.get(index) { + ty.clone() + } else { + self.infer_expr(index, &Expectation::none(), ExprIsRead::Yes) + }; + let trait_ref = TyBuilder::trait_ref(self.db, index_trait) + .push(base_ty) + .fill(|_| index_ty.clone().cast(Interner)) + .build(); + self.push_obligation(trait_ref.cast(Interner)); } } self.infer_mut_expr(base, mutability); @@ -178,28 +166,23 @@ } Expr::UnaryOp { expr, op: UnaryOp::Deref } => { let mut mutability = mutability; - if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) { - if mutability == Mutability::Mut { - if let Some(deref_trait) = - LangItem::DerefMut.resolve_trait(self.db, self.table.trait_env.krate) - { - let ty = self.result.type_of_expr.get(*expr); - let is_mut_ptr = ty.is_some_and(|ty| { - let ty = self.table.resolve_ty_shallow(ty); - matches!( - ty.kind(Interner), - chalk_ir::TyKind::Raw(Mutability::Mut, _) - ) - }); - if is_mut_ptr { - mutability = Mutability::Not; - } else if let Some(deref_fn) = deref_trait - .trait_items(self.db) - .method_by_name(&Name::new_symbol_root(sym::deref_mut)) - { - *f = deref_fn; - } - } + if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) + && mutability == Mutability::Mut + && let Some(deref_trait) = + LangItem::DerefMut.resolve_trait(self.db, self.table.trait_env.krate) + { + let ty = self.result.type_of_expr.get(*expr); + let is_mut_ptr = ty.is_some_and(|ty| { + let ty = self.table.resolve_ty_shallow(ty); + matches!(ty.kind(Interner), chalk_ir::TyKind::Raw(Mutability::Mut, _)) + }); + if is_mut_ptr { + mutability = Mutability::Not; + } else if let Some(deref_fn) = deref_trait + .trait_items(self.db) + .method_by_name(&Name::new_symbol_root(sym::deref_mut)) + { + *f = deref_fn; } } self.infer_mut_expr(*expr, mutability);
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 18288b7..707bec0 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs
@@ -498,12 +498,12 @@ // 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() { - if let Some(resolved_array_ty) = + 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); - } + { + self.unify(&expected, &resolved_array_ty); } let expected = self.resolve_ty_shallow(&expected); @@ -539,17 +539,16 @@ fn infer_lit_pat(&mut self, expr: ExprId, expected: &Ty) -> Ty { // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`. - if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] { - if let Some((inner, ..)) = expected.as_reference() { - let inner = self.resolve_ty_shallow(inner); - if matches!(inner.kind(Interner), TyKind::Slice(_)) { - let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner); - let slice_ty = TyKind::Slice(elem_ty).intern(Interner); - let ty = - TyKind::Ref(Mutability::Not, static_lifetime(), slice_ty).intern(Interner); - self.write_expr_ty(expr, ty.clone()); - return ty; - } + if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] + && let Some((inner, ..)) = expected.as_reference() + { + let inner = self.resolve_ty_shallow(inner); + if matches!(inner.kind(Interner), TyKind::Slice(_)) { + let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner); + let slice_ty = TyKind::Slice(elem_ty).intern(Interner); + let ty = TyKind::Ref(Mutability::Not, static_lifetime(), slice_ty).intern(Interner); + self.write_expr_ty(expr, ty.clone()); + return ty; } }
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index c077555..a709aeb 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs
@@ -19,11 +19,13 @@ use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, - GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, InferenceVar, Interner, - Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, - TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, - consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, - to_chalk_trait_id, traits::FnTrait, + GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, + OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, + TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + consteval::unknown_const, + db::HirDatabase, + fold_generic_args, fold_tys_and_consts, to_chalk_trait_id, + traits::{FnTrait, NextTraitSolveResult}, }; impl InferenceContext<'_> { @@ -116,9 +118,12 @@ // eagerly replace projections in the type; we may be getting types // e.g. from where clauses where this hasn't happened yet let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), Interner)); + tracing::debug!("unifying {:?} {:?}", var, ty); ctx.unify(var.assert_ty_ref(Interner), &ty); } else { - let _ = ctx.try_unify(var, &new_vars.apply(v.clone(), Interner)); + let v = new_vars.apply(v.clone(), Interner); + tracing::debug!("try_unifying {:?} {:?}", var, v); + let _ = ctx.try_unify(var, &v); } } } @@ -326,6 +331,7 @@ /// type annotation (e.g. from a let type annotation, field type or function /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. + #[tracing::instrument(skip(self), ret)] pub(crate) fn normalize_associated_types_in<T>(&mut self, ty: T) -> T where T: HasInterner<Interner = Interner> + TypeFoldable<Interner>, @@ -333,12 +339,88 @@ fold_tys_and_consts( ty, |e, _| match e { - Either::Left(ty) => Either::Left(match ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - self.normalize_projection_ty(proj_ty.clone()) - } - _ => ty, - }), + Either::Left(ty) => { + let ty = self.resolve_ty_shallow(&ty); + tracing::debug!(?ty); + Either::Left(match ty.kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj_ty)) => { + let ty = self.normalize_projection_ty(proj_ty.clone()); + self.resolve_ty_shallow(&ty) + } + TyKind::AssociatedType(id, subst) => { + if ty.data(Interner).flags.intersects( + chalk_ir::TypeFlags::HAS_TY_INFER + | chalk_ir::TypeFlags::HAS_CT_INFER, + ) { + return Either::Left(ty); + } + let var = self.new_type_var(); + let proj_ty = chalk_ir::ProjectionTy { + associated_ty_id: *id, + substitution: subst.clone(), + }; + let normalize = chalk_ir::Normalize { + alias: AliasTy::Projection(proj_ty), + ty: var.clone(), + }; + let goal = chalk_ir::Goal::new( + Interner, + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Normalize( + normalize, + )), + ); + let in_env = InEnvironment::new(&self.trait_env.env, goal); + + let canonicalized = { + let result = + self.var_unification_table.canonicalize(Interner, in_env); + let free_vars = result + .free_vars + .into_iter() + .map(|free_var| free_var.to_generic_arg(Interner)) + .collect(); + Canonicalized { value: result.quantified, free_vars } + }; + let solution = self.db.trait_solve( + self.trait_env.krate, + self.trait_env.block, + canonicalized.value.clone(), + ); + if let NextTraitSolveResult::Certain(canonical_subst) = solution { + // This is not great :) But let's just assert this for now and come back to it later. + if canonical_subst.value.subst.len(Interner) != 1 { + ty + } else { + let normalized = canonical_subst.value.subst.as_slice(Interner) + [0] + .assert_ty_ref(Interner); + match normalized.kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj_ty)) => { + if id == &proj_ty.associated_ty_id + && subst == &proj_ty.substitution + { + ty + } else { + normalized.clone() + } + } + TyKind::AssociatedType(new_id, new_subst) => { + if new_id == id && new_subst == subst { + ty + } else { + normalized.clone() + } + } + _ => normalized.clone(), + } + } + } else { + ty + } + } + _ => ty, + }) + } Either::Right(c) => Either::Right(match &c.data(Interner).value { chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { crate::ConstScalar::UnevaluatedConst(c_id, subst) => { @@ -588,7 +670,6 @@ } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - #[tracing::instrument(skip_all)] pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, @@ -606,7 +687,7 @@ }; result.goals.iter().all(|goal| { let canonicalized = self.canonicalize_with_free_vars(goal.clone()); - self.try_resolve_obligation(&canonicalized).is_some() + self.try_resolve_obligation(&canonicalized).certain() }) } @@ -633,6 +714,9 @@ /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { + if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) { + return ty.clone(); + } self.resolve_obligations_as_possible(); self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone()) } @@ -662,7 +746,8 @@ /// 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). - pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option<Solution> { + #[tracing::instrument(level = "debug", skip(self))] + pub(crate) fn try_obligation(&mut self, goal: Goal) -> NextTraitSolveResult { let in_env = InEnvironment::new(&self.trait_env.env, goal); let canonicalized = self.canonicalize(in_env); @@ -674,10 +759,45 @@ self.register_obligation_in_env(in_env) } + #[tracing::instrument(level = "debug", skip(self))] fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) { - let canonicalized = self.canonicalize_with_free_vars(goal); + match goal.goal.data(Interner) { + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), + )) => { + if ty.inference_var(Interner).is_some() { + match alias { + chalk_ir::AliasTy::Opaque(opaque) => { + if self.unify( + &chalk_ir::TyKind::OpaqueType( + opaque.opaque_ty_id, + opaque.substitution.clone(), + ) + .intern(Interner), + ty, + ) { + return; + } + } + _ => {} + } + } + } + _ => {} + } + let canonicalized = { + let result = self.var_unification_table.canonicalize(Interner, goal); + let free_vars = result + .free_vars + .into_iter() + .map(|free_var| free_var.to_generic_arg(Interner)) + .collect(); + Canonicalized { value: result.quantified, free_vars } + }; + tracing::debug!(?canonicalized); let solution = self.try_resolve_obligation(&canonicalized); - if matches!(solution, Some(Solution::Ambig(_))) { + tracing::debug!(?solution); + if solution.uncertain() { self.pending_obligations.push(canonicalized); } } @@ -694,7 +814,9 @@ mem::swap(&mut self.pending_obligations, &mut obligations); for canonicalized in obligations.drain(..) { + tracing::debug!(obligation = ?canonicalized); if !self.check_changed(&canonicalized) { + tracing::debug!("not changed"); self.pending_obligations.push(canonicalized); continue; } @@ -799,37 +921,36 @@ }) } + #[tracing::instrument(level = "debug", skip(self))] fn try_resolve_obligation( &mut self, canonicalized: &Canonicalized<InEnvironment<Goal>>, - ) -> Option<chalk_solve::Solution<Interner>> { + ) -> NextTraitSolveResult { let solution = self.db.trait_solve( self.trait_env.krate, self.trait_env.block, canonicalized.value.clone(), ); + tracing::debug!(?solution, ?canonicalized); match &solution { - Some(Solution::Unique(canonical_subst)) => { + NextTraitSolveResult::Certain(v) => { canonicalized.apply_solution( self, Canonical { - binders: canonical_subst.binders.clone(), - // FIXME: handle constraints - value: canonical_subst.value.subst.clone(), + binders: v.binders.clone(), + // FIXME handle constraints + value: v.value.subst.clone(), }, ); } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(self, substs.clone()); + // ...so, should think about how to get some actually get some guidance here + NextTraitSolveResult::Uncertain(v) => { + canonicalized.apply_solution(self, v.clone()); } - Some(_) => { - // FIXME use this when trying to resolve everything at the end - } - None => { - // FIXME obligation cannot be fulfilled => diagnostic - } + NextTraitSolveResult::NoSolution => {} } + solution } @@ -896,7 +1017,10 @@ environment: trait_env.clone(), }; let canonical = self.canonicalize(obligation.clone()); - if self.db.trait_solve(krate, self.trait_env.block, canonical.cast(Interner)).is_some() + if !self + .db + .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) + .no_solution() { self.register_obligation(obligation.goal); let return_ty = self.normalize_projection_ty(projection); @@ -909,10 +1033,10 @@ environment: trait_env.clone(), }; let canonical = self.canonicalize(obligation.clone()); - if self + if !self .db .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) - .is_some() + .no_solution() { return Some((fn_x, arg_tys, return_ty)); } @@ -1032,7 +1156,7 @@ substitution: Substitution::from1(Interner, ty), }); let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner); - matches!(self.try_obligation(goal), Some(Solution::Unique(_))) + self.try_obligation(goal).certain() } }
diff --git a/crates/hir-ty/src/interner.rs b/crates/hir-ty/src/interner.rs index fecb3f4..57ef552 100644 --- a/crates/hir-ty/src/interner.rs +++ b/crates/hir-ty/src/interner.rs
@@ -2,11 +2,10 @@ //! representation of the various objects Chalk deals with (types, goals etc.). use crate::{ - AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData, ConstScalar, - Constraint, Constraints, FnAbi, FnDefId, GenericArg, GenericArgData, Goal, GoalData, Goals, - InEnvironment, Lifetime, LifetimeData, OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseData, - ProgramClauses, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, Ty, - TyData, TyKind, VariableKind, VariableKinds, chalk_db, tls, + AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData, ConstScalar, FnAbi, + FnDefId, GenericArg, GenericArgData, Goal, GoalData, InEnvironment, Lifetime, LifetimeData, + OpaqueTy, OpaqueTyId, ProgramClause, ProjectionTy, QuantifiedWhereClause, + QuantifiedWhereClauses, Substitution, Ty, TyKind, VariableKind, chalk_db, tls, }; use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variance}; use hir_def::TypeAliasId; @@ -15,11 +14,19 @@ use std::fmt; use triomphe::Arc; +type TyData = chalk_ir::TyData<Interner>; +type VariableKinds = chalk_ir::VariableKinds<Interner>; +type Goals = chalk_ir::Goals<Interner>; +type ProgramClauseData = chalk_ir::ProgramClauseData<Interner>; +type Constraint = chalk_ir::Constraint<Interner>; +type Constraints = chalk_ir::Constraints<Interner>; +type ProgramClauses = chalk_ir::ProgramClauses<Interner>; + #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] pub struct Interner; -#[derive(PartialEq, Eq, Hash)] -pub struct InternedWrapper<T>(T); +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] +pub struct InternedWrapper<T>(pub(crate) T); impl<T: fmt::Debug> fmt::Debug for InternedWrapper<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -27,6 +34,9 @@ } } +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] +pub struct InternedWrapperNoDebug<T>(pub(crate) T); + impl<T> std::ops::Deref for InternedWrapper<T> { type Target = T; @@ -124,6 +134,7 @@ fmt: &mut fmt::Formatter<'_>, ) -> Option<fmt::Result> { tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt))) + .or_else(|| Some(fmt.write_str("ProjectionTy"))) } fn debug_opaque_ty(opaque_ty: &OpaqueTy, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 107da6a..2d0471d 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs
@@ -2,33 +2,43 @@ use std::fmt; -use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ - LocalFieldId, StructId, - layout::{ - Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutData, Primitive, - ReprOptions, Scalar, StructKind, TargetDataLayout, WrappingRange, - }, + AdtId, LocalFieldId, StructId, + layout::{LayoutCalculatorError, LayoutData}, }; use la_arena::{Idx, RawIdx}; -use rustc_abi::AddressSpace; -use rustc_index::IndexVec; +use rustc_abi::{ + AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind, + TargetDataLayout, WrappingRange, +}; +use rustc_index::IndexVec; +use rustc_type_ir::{ + FloatTy, IntTy, UintTy, + inherent::{IntoKind, SliceLike}, +}; use triomphe::Arc; use crate::{ - Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, - consteval::try_const_usize, - db::{HirDatabase, InternedClosure}, - infer::normalize, - utils::ClosureSubst, + TraitEnvironment, + consteval_nextsolver::try_const_usize, + db::HirDatabase, + next_solver::{ + DbInterner, GenericArgs, ParamEnv, SolverDefId, Ty, TyKind, TypingMode, + infer::{DbInternerInferExt, traits::ObligationCause}, + mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, + project::solve_normalize::deeply_normalize, + }, }; -pub(crate) use self::adt::layout_of_adt_cycle_result; -pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; +pub(crate) use self::adt::{layout_of_adt_cycle_result, layout_of_adt_ns_cycle_result}; +pub use self::{ + adt::{layout_of_adt_ns_query, layout_of_adt_query}, + target::target_data_layout_query, +}; -mod adt; -mod target; +pub(crate) mod adt; +pub(crate) mod target; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub usize); @@ -119,11 +129,12 @@ } } -fn layout_of_simd_ty( - db: &dyn HirDatabase, +// FIXME: move this to the `rustc_abi`. +fn layout_of_simd_ty<'db>( + db: &'db dyn HirDatabase, id: StructId, repr_packed: bool, - subst: &Substitution, + args: &GenericArgs<'db>, env: Arc<TraitEnvironment>, dl: &TargetDataLayout, ) -> Result<Arc<Layout>, LayoutError> { @@ -132,18 +143,18 @@ // * #[repr(simd)] struct S([T; 4]) // // where T is a primitive scalar (integer/float/pointer). - let fields = db.field_types(id.into()); + let fields = db.field_types_ns(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.clone().substitute(Interner, subst).kind(Interner).clone()) + .map(|f| (*f.1).instantiate(DbInterner::new_with(db, None, None), args).kind()) else { return Err(LayoutError::InvalidSimdType); }; let e_len = try_const_usize(db, &e_len).ok_or(LayoutError::HasErrorConst)? as u64; - let e_ly = db.layout_of_ty(e_ty, env)?; + let e_ly = db.layout_of_ty_ns(e_ty, env)?; let cx = LayoutCx::new(dl); Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?)) @@ -151,108 +162,122 @@ pub fn layout_of_ty_query( db: &dyn HirDatabase, - ty: Ty, + ty: crate::Ty, trait_env: Arc<TraitEnvironment>, ) -> Result<Arc<Layout>, LayoutError> { let krate = trait_env.krate; + let interner = DbInterner::new_with(db, Some(krate), trait_env.block); + db.layout_of_ty_ns(ty.to_nextsolver(interner), trait_env) +} + +pub fn layout_of_ty_ns_query<'db>( + db: &'db dyn HirDatabase, + ty: Ty<'db>, + trait_env: Arc<TraitEnvironment>, +) -> Result<Arc<Layout>, LayoutError> { + let krate = trait_env.krate; + let interner = DbInterner::new_with(db, Some(krate), trait_env.block); let Ok(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); }; let dl = &*target; let cx = LayoutCx::new(dl); - let ty = normalize(db, trait_env.clone(), ty); - let kind = ty.kind(Interner); - let result = match kind { - TyKind::Adt(AdtId(def), subst) => { - if let hir_def::AdtId::StructId(s) = def { - let data = db.struct_signature(*s); - let repr = data.repr.unwrap_or_default(); - if repr.simd() { - return layout_of_simd_ty(db, *s, repr.packed(), subst, trait_env, &target); + let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis); + let cause = ObligationCause::dummy(); + let ty = deeply_normalize(infer_ctxt.at(&cause, ParamEnv::empty()), ty).unwrap_or(ty); + let result = match ty.kind() { + TyKind::Adt(def, args) => { + match def.inner().id { + hir_def::AdtId::StructId(s) => { + let data = db.struct_signature(s); + let repr = data.repr.unwrap_or_default(); + if repr.simd() { + return layout_of_simd_ty(db, s, repr.packed(), &args, trait_env, &target); + } } - }; - return db.layout_of_adt(*def, subst.clone(), trait_env); + _ => {} + } + return db.layout_of_adt_ns(def.inner().id, args, trait_env); } - TyKind::Scalar(s) => match s { - chalk_ir::Scalar::Bool => Layout::scalar( + TyKind::Bool => Layout::scalar( + dl, + Scalar::Initialized { + value: Primitive::Int(Integer::I8, false), + valid_range: WrappingRange { start: 0, end: 1 }, + }, + ), + TyKind::Char => Layout::scalar( + dl, + Scalar::Initialized { + value: Primitive::Int(Integer::I32, false), + valid_range: WrappingRange { start: 0, end: 0x10FFFF }, + }, + ), + TyKind::Int(i) => Layout::scalar( + dl, + scalar_unit( dl, - Scalar::Initialized { - value: Primitive::Int(Integer::I8, false), - valid_range: WrappingRange { start: 0, end: 1 }, - }, - ), - chalk_ir::Scalar::Char => Layout::scalar( - dl, - Scalar::Initialized { - value: Primitive::Int(Integer::I32, false), - valid_range: WrappingRange { start: 0, end: 0x10FFFF }, - }, - ), - chalk_ir::Scalar::Int(i) => Layout::scalar( - dl, - scalar_unit( - dl, - Primitive::Int( - match i { - IntTy::Isize => dl.ptr_sized_integer(), - IntTy::I8 => Integer::I8, - IntTy::I16 => Integer::I16, - IntTy::I32 => Integer::I32, - IntTy::I64 => Integer::I64, - IntTy::I128 => Integer::I128, - }, - true, - ), + Primitive::Int( + match i { + IntTy::Isize => dl.ptr_sized_integer(), + IntTy::I8 => Integer::I8, + IntTy::I16 => Integer::I16, + IntTy::I32 => Integer::I32, + IntTy::I64 => Integer::I64, + IntTy::I128 => Integer::I128, + }, + true, ), ), - chalk_ir::Scalar::Uint(i) => Layout::scalar( + ), + TyKind::Uint(i) => Layout::scalar( + dl, + scalar_unit( dl, - scalar_unit( - dl, - Primitive::Int( - match i { - UintTy::Usize => dl.ptr_sized_integer(), - UintTy::U8 => Integer::I8, - UintTy::U16 => Integer::I16, - UintTy::U32 => Integer::I32, - UintTy::U64 => Integer::I64, - UintTy::U128 => Integer::I128, - }, - false, - ), + Primitive::Int( + match i { + UintTy::Usize => dl.ptr_sized_integer(), + UintTy::U8 => Integer::I8, + UintTy::U16 => Integer::I16, + UintTy::U32 => Integer::I32, + UintTy::U64 => Integer::I64, + UintTy::U128 => Integer::I128, + }, + false, ), ), - chalk_ir::Scalar::Float(f) => Layout::scalar( + ), + TyKind::Float(f) => Layout::scalar( + dl, + scalar_unit( dl, - scalar_unit( - dl, - Primitive::Float(match f { - FloatTy::F16 => Float::F16, - FloatTy::F32 => Float::F32, - FloatTy::F64 => Float::F64, - FloatTy::F128 => Float::F128, - }), - ), + Primitive::Float(match f { + FloatTy::F16 => Float::F16, + FloatTy::F32 => Float::F32, + FloatTy::F64 => Float::F64, + FloatTy::F128 => Float::F128, + }), ), - }, - TyKind::Tuple(len, tys) => { - let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; + ), + TyKind::Tuple(tys) => { + let kind = + if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; let fields = tys - .iter(Interner) - .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), trait_env.clone())) + .iter() + .map(|k| db.layout_of_ty_ns(k, trait_env.clone())) .collect::<Result<Vec<_>, _>>()?; let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>(); let fields = fields.iter().collect::<IndexVec<_, _>>(); cx.calc.univariant(&fields, &ReprOptions::default(), kind)? } TyKind::Array(element, count) => { - let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64; - let element = db.layout_of_ty(element.clone(), trait_env)?; + let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64; + let element = db.layout_of_ty_ns(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, Some(count))? } TyKind::Slice(element) => { - let element = db.layout_of_ty(element.clone(), trait_env)?; + let element = db.layout_of_ty_ns(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, None)? } TyKind::Str => { @@ -260,18 +285,21 @@ cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)? } // Potentially-wide pointers. - TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { + TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => { let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO)); - if matches!(ty.kind(Interner), TyKind::Ref(..)) { + if matches!(ty.kind(), TyKind::Ref(..)) { data_ptr.valid_range_mut().start = 1; } + // FIXME(next-solver) // let pointee = tcx.normalize_erasing_regions(param_env, pointee); // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) { - // return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr))); + // return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); // } - let mut unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone()); + let unsized_part = struct_tail_erasing_lifetimes(db, pointee); + // FIXME(next-solver) + /* if let TyKind::AssociatedType(id, subst) = unsized_part.kind(Interner) { unsized_part = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy { associated_ty_id: *id, @@ -280,11 +308,12 @@ .intern(Interner); } unsized_part = normalize(db, trait_env, unsized_part); - let metadata = match unsized_part.kind(Interner) { + */ + let metadata = match unsized_part.kind() { TyKind::Slice(_) | TyKind::Str => { scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false)) } - TyKind::Dyn(..) => { + TyKind::Dynamic(..) => { let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO)); vtable.valid_range_mut().start = 1; vtable @@ -299,97 +328,93 @@ LayoutData::scalar_pair(dl, data_ptr, metadata) } TyKind::Never => LayoutData::never_type(dl), - TyKind::FnDef(..) | TyKind::Dyn(_) | TyKind::Foreign(_) => { - let sized = matches!(kind, TyKind::FnDef(..)); - LayoutData::unit(dl, sized) - } - TyKind::Function(_) => { + TyKind::FnDef(..) => LayoutData::unit(dl, true), + TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false), + TyKind::FnPtr(..) => { let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space)); ptr.valid_range_mut().start = 1; Layout::scalar(dl, ptr) } - TyKind::OpaqueType(opaque_ty_id, _) => { - let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); - match impl_trait_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let infer = db.infer(func.into()); - return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); - } - crate::ImplTraitId::TypeAliasImplTrait(..) => { - return Err(LayoutError::NotImplemented); - } - crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { - return Err(LayoutError::NotImplemented); - } - } - } - TyKind::Closure(c, subst) => { - let InternedClosure(def, _) = db.lookup_intern_closure((*c).into()); - let infer = db.infer(def); - let (captures, _) = infer.closure_info(c); + TyKind::Closure(c, args) => { + let id = match c { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + let def = db.lookup_intern_closure(id); + let infer = db.infer(def.0); + let (captures, _) = infer.closure_info(&id.into()); let fields = captures .iter() .map(|it| { - db.layout_of_ty( - it.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()), - trait_env.clone(), - ) + let ty = + convert_binder_to_early_binder(interner, it.ty.to_nextsolver(interner)) + .instantiate(interner, args); + db.layout_of_ty_ns(ty, trait_env.clone()) }) .collect::<Result<Vec<_>, _>>()?; let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>(); let fields = fields.iter().collect::<IndexVec<_, _>>(); cx.calc.univariant(&fields, &ReprOptions::default(), StructKind::AlwaysSized)? } - TyKind::Coroutine(_, _) | TyKind::CoroutineWitness(_, _) => { + + TyKind::Coroutine(_, _) + | TyKind::CoroutineWitness(_, _) + | TyKind::CoroutineClosure(_, _) => { return Err(LayoutError::NotImplemented); } - TyKind::Error => return Err(LayoutError::HasErrorType), - TyKind::AssociatedType(id, subst) => { - // Try again with `TyKind::Alias` to normalize the associated type. - // Usually we should not try to normalize `TyKind::AssociatedType`, but layout calculation is used - // in monomorphized MIR where this is okay. If outside monomorphization, this will lead to cycle, - // which we will recover from with an error. - let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy { - associated_ty_id: *id, - substitution: subst.clone(), - })) - .intern(Interner); - return db.layout_of_ty(ty, trait_env); + + TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => { + return Err(LayoutError::NotImplemented); } - TyKind::Alias(_) - | TyKind::Placeholder(_) - | TyKind::BoundVar(_) - | TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder), + + TyKind::Error(_) => return Err(LayoutError::HasErrorType), + TyKind::Placeholder(_) + | TyKind::Bound(..) + | TyKind::Infer(..) + | TyKind::Param(..) + | TyKind::Alias(..) => { + return Err(LayoutError::HasPlaceholder); + } }; Ok(Arc::new(result)) } pub(crate) fn layout_of_ty_cycle_result( _: &dyn HirDatabase, - _: Ty, + _: crate::Ty, _: Arc<TraitEnvironment>, ) -> Result<Arc<Layout>, LayoutError> { Err(LayoutError::RecursiveTypeWithoutIndirection) } -fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { - match pointee.kind(Interner) { - &TyKind::Adt(AdtId(hir_def::AdtId::StructId(i)), ref subst) => { - let data = i.fields(db); +pub(crate) fn layout_of_ty_ns_cycle_result<'db>( + _: &dyn HirDatabase, + _: Ty<'db>, + _: Arc<TraitEnvironment>, +) -> Result<Arc<Layout>, LayoutError> { + Err(LayoutError::RecursiveTypeWithoutIndirection) +} + +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 { + AdtId::StructId(id) => id, + _ => return pointee, + }; + let data = struct_id.fields(db); let mut it = data.fields().iter().rev(); match it.next() { Some((f, _)) => { - let last_field_ty = field_ty(db, i.into(), f, subst); + let last_field_ty = field_ty(db, struct_id.into(), f, &args); struct_tail_erasing_lifetimes(db, last_field_ty) } None => pointee, } } - TyKind::Tuple(_, subst) => { - if let Some(last_field_ty) = - subst.iter(Interner).last().and_then(|arg| arg.ty(Interner)) - { - struct_tail_erasing_lifetimes(db, last_field_ty.clone()) + TyKind::Tuple(tys) => { + if let Some(last_field_ty) = tys.iter().last() { + struct_tail_erasing_lifetimes(db, last_field_ty) } else { pointee } @@ -398,13 +423,13 @@ } } -fn field_ty( - db: &dyn HirDatabase, +fn field_ty<'a>( + db: &'a dyn HirDatabase, def: hir_def::VariantId, fd: LocalFieldId, - subst: &Substitution, -) -> Ty { - db.field_types(def)[fd].clone().substitute(Interner, subst) + args: &GenericArgs<'a>, +) -> Ty<'a> { + db.field_types_ns(def)[fd].instantiate(DbInterner::new_with(db, None, None), args) } fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 236f316..2fa01b6 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs
@@ -4,10 +4,10 @@ use hir_def::{ AdtId, VariantId, - layout::{Integer, ReprOptions, TargetDataLayout}, signatures::{StructFlags, VariantFields}, }; use intern::sym; +use rustc_abi::{Integer, ReprOptions, TargetDataLayout}; use rustc_index::IndexVec; use smallvec::SmallVec; use triomphe::Arc; @@ -15,17 +15,26 @@ use crate::{ Substitution, TraitEnvironment, db::HirDatabase, - layout::{Layout, LayoutError, field_ty}, + layout::{Layout, LayoutCx, LayoutError, field_ty}, + next_solver::{DbInterner, GenericArgs, mapping::ChalkToNextSolver}, }; -use super::LayoutCx; - pub fn layout_of_adt_query( db: &dyn HirDatabase, def: AdtId, subst: Substitution, trait_env: Arc<TraitEnvironment>, ) -> Result<Arc<Layout>, LayoutError> { + let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); + db.layout_of_adt_ns(def, subst.to_nextsolver(interner), trait_env) +} + +pub fn layout_of_adt_ns_query<'db>( + db: &'db dyn HirDatabase, + def: AdtId, + args: GenericArgs<'db>, + trait_env: Arc<TraitEnvironment>, +) -> Result<Arc<Layout>, LayoutError> { let krate = trait_env.krate; let Ok(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); @@ -35,7 +44,7 @@ let handle_variant = |def: VariantId, var: &VariantFields| { var.fields() .iter() - .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), trait_env.clone())) + .map(|(fd, _)| db.layout_of_ty_ns(field_ty(db, def, fd, &args), trait_env.clone())) .collect::<Result<Vec<_>, _>>() }; let (variants, repr, is_special_no_niche) = match def { @@ -85,16 +94,6 @@ let d = db.const_eval_discriminant(e.enum_variants(db).variants[id.0].0).ok()?; Some((id, d)) }), - // FIXME: The current code for niche-filling relies on variant indices - // instead of actual discriminants, so enums with - // explicit discriminants (RFC #2363) would misbehave and we should disable - // niche optimization for them. - // The code that do it in rustc: - // repr.inhibit_enum_layout_opt() || def - // .variants() - // .iter_enumerated() - // .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32())) - repr.inhibit_enum_layout_opt(), !matches!(def, AdtId::EnumId(..)) && variants .iter() @@ -106,6 +105,24 @@ Ok(Arc::new(result)) } +pub(crate) fn layout_of_adt_cycle_result( + _: &dyn HirDatabase, + _: AdtId, + _: Substitution, + _: Arc<TraitEnvironment>, +) -> Result<Arc<Layout>, LayoutError> { + Err(LayoutError::RecursiveTypeWithoutIndirection) +} + +pub(crate) fn layout_of_adt_ns_cycle_result<'db>( + _: &'db dyn HirDatabase, + _def: AdtId, + _args: GenericArgs<'db>, + _trait_env: Arc<TraitEnvironment>, +) -> Result<Arc<Layout>, LayoutError> { + Err(LayoutError::RecursiveTypeWithoutIndirection) +} + fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) { let attrs = db.attrs(def.into()); let get = |name| { @@ -130,15 +147,6 @@ (get(sym::rustc_layout_scalar_valid_range_start), get(sym::rustc_layout_scalar_valid_range_end)) } -pub(crate) fn layout_of_adt_cycle_result( - _: &dyn HirDatabase, - _: AdtId, - _: Substitution, - _: Arc<TraitEnvironment>, -) -> Result<Arc<Layout>, LayoutError> { - Err(LayoutError::RecursiveTypeWithoutIndirection) -} - /// Finds the appropriate Integer type and signedness for the given /// signed discriminant range and `#[repr]` attribute. /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index b3bc226..93f2e12 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs
@@ -11,6 +11,7 @@ Interner, Substitution, db::HirDatabase, layout::{Layout, LayoutError}, + setup_tracing, test_db::TestDB, }; @@ -29,6 +30,7 @@ #[rust_analyzer::rust_fixture] ra_fixture: &str, minicore: &str, ) -> Result<Arc<Layout>, LayoutError> { + let _tracing = setup_tracing(); let target_data_layout = current_machine_data_layout(); let ra_fixture = format!( "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\n{ra_fixture}", @@ -97,6 +99,7 @@ #[rust_analyzer::rust_fixture] ra_fixture: &str, minicore: &str, ) -> Result<Arc<Layout>, LayoutError> { + let _tracing = setup_tracing(); let target_data_layout = current_machine_data_layout(); let ra_fixture = format!( "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\nfn main(){{let goal = {{{ra_fixture}}};}}",
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index e787fd9..2b0dc93 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs
@@ -21,6 +21,24 @@ #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_ast_ir; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_ast_ir as rustc_ast_ir; + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_type_ir; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_type_ir as rustc_type_ir; + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_next_trait_solver; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; + mod builder; mod chalk_db; mod chalk_ext; @@ -29,13 +47,16 @@ mod inhabitedness; mod interner; mod lower; +mod lower_nextsolver; mod mapping; +pub mod next_solver; mod target_feature; mod tls; mod utils; pub mod autoderef; pub mod consteval; +pub mod consteval_nextsolver; pub mod db; pub mod diagnostics; pub mod display; @@ -57,7 +78,7 @@ use std::hash::Hash; use chalk_ir::{ - NoSolution, + NoSolution, VariableKinds, fold::{Shift, TypeFoldable}, interner::HasInterner, }; @@ -121,9 +142,9 @@ pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>; pub type PlaceholderIndex = chalk_ir::PlaceholderIndex; -pub type VariableKind = chalk_ir::VariableKind<Interner>; -pub type VariableKinds = chalk_ir::VariableKinds<Interner>; pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>; + +pub(crate) type VariableKind = chalk_ir::VariableKind<Interner>; /// Represents generic parameters and an item bound by them. When the item has parent, the binders /// also contain the generic parameters for its parent. See chalk's documentation for details. /// @@ -145,52 +166,45 @@ pub type Ty = chalk_ir::Ty<Interner>; pub type TyKind = chalk_ir::TyKind<Interner>; pub type TypeFlags = chalk_ir::TypeFlags; -pub type DynTy = chalk_ir::DynTy<Interner>; +pub(crate) type DynTy = chalk_ir::DynTy<Interner>; pub type FnPointer = chalk_ir::FnPointer<Interner>; -// pub type FnSubst = chalk_ir::FnSubst<Interner>; // a re-export so we don't lose the tuple constructor -pub use chalk_ir::FnSubst; -pub type ProjectionTy = chalk_ir::ProjectionTy<Interner>; -pub type AliasTy = chalk_ir::AliasTy<Interner>; -pub type OpaqueTy = chalk_ir::OpaqueTy<Interner>; -pub type InferenceVar = chalk_ir::InferenceVar; +pub(crate) use chalk_ir::FnSubst; // a re-export so we don't lose the tuple constructor -pub type Lifetime = chalk_ir::Lifetime<Interner>; -pub type LifetimeData = chalk_ir::LifetimeData<Interner>; -pub type LifetimeOutlives = chalk_ir::LifetimeOutlives<Interner>; +pub type AliasTy = chalk_ir::AliasTy<Interner>; + +pub type ProjectionTy = chalk_ir::ProjectionTy<Interner>; +pub(crate) type OpaqueTy = chalk_ir::OpaqueTy<Interner>; +pub(crate) type InferenceVar = chalk_ir::InferenceVar; + +pub(crate) type Lifetime = chalk_ir::Lifetime<Interner>; +pub(crate) type LifetimeData = chalk_ir::LifetimeData<Interner>; +pub(crate) type LifetimeOutlives = chalk_ir::LifetimeOutlives<Interner>; + +pub type ConstValue = chalk_ir::ConstValue<Interner>; pub type Const = chalk_ir::Const<Interner>; -pub type ConstData = chalk_ir::ConstData<Interner>; -pub type ConstValue = chalk_ir::ConstValue<Interner>; -pub type ConcreteConst = chalk_ir::ConcreteConst<Interner>; +pub(crate) type ConstData = chalk_ir::ConstData<Interner>; +pub(crate) type ConcreteConst = chalk_ir::ConcreteConst<Interner>; -pub type ChalkTraitId = chalk_ir::TraitId<Interner>; pub type TraitRef = chalk_ir::TraitRef<Interner>; pub type QuantifiedWhereClause = Binders<WhereClause>; -pub type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses<Interner>; pub type Canonical<T> = chalk_ir::Canonical<T>; -pub type FnSig = chalk_ir::FnSig<Interner>; +pub(crate) type ChalkTraitId = chalk_ir::TraitId<Interner>; +pub(crate) type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses<Interner>; + +pub(crate) type FnSig = chalk_ir::FnSig<Interner>; pub type InEnvironment<T> = chalk_ir::InEnvironment<T>; -pub type Environment = chalk_ir::Environment<Interner>; -pub type DomainGoal = chalk_ir::DomainGoal<Interner>; -pub type Goal = chalk_ir::Goal<Interner>; pub type AliasEq = chalk_ir::AliasEq<Interner>; -pub type Solution = chalk_solve::Solution<Interner>; -pub type Constraint = chalk_ir::Constraint<Interner>; -pub type Constraints = chalk_ir::Constraints<Interner>; -pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>; -pub type Guidance = chalk_solve::Guidance<Interner>; pub type WhereClause = chalk_ir::WhereClause<Interner>; -pub type CanonicalVarKind = chalk_ir::CanonicalVarKind<Interner>; -pub type GoalData = chalk_ir::GoalData<Interner>; -pub type Goals = chalk_ir::Goals<Interner>; -pub type ProgramClauseData = chalk_ir::ProgramClauseData<Interner>; -pub type ProgramClause = chalk_ir::ProgramClause<Interner>; -pub type ProgramClauses = chalk_ir::ProgramClauses<Interner>; -pub type TyData = chalk_ir::TyData<Interner>; -pub type Variances = chalk_ir::Variances<Interner>; +pub(crate) type DomainGoal = chalk_ir::DomainGoal<Interner>; +pub(crate) type Goal = chalk_ir::Goal<Interner>; + +pub(crate) type CanonicalVarKind = chalk_ir::CanonicalVarKind<Interner>; +pub(crate) type GoalData = chalk_ir::GoalData<Interner>; +pub(crate) type ProgramClause = chalk_ir::ProgramClause<Interner>; /// A constant can have reference to other things. Memory map job is holding /// the necessary bits of memory of the const eval session to keep the constant @@ -311,30 +325,11 @@ Binders::empty(Interner, value.shifted_in_from(Interner, DebruijnIndex::ONE)) } -pub(crate) fn make_type_and_const_binders<T: HasInterner<Interner = Interner>>( - which_is_const: impl Iterator<Item = Option<Ty>>, - value: T, -) -> Binders<T> { - Binders::new( - VariableKinds::from_iter( - Interner, - which_is_const.map(|x| { - if let Some(ty) = x { - chalk_ir::VariableKind::Const(ty) - } else { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - }), - ), - value, - ) -} - pub(crate) fn make_single_type_binders<T: HasInterner<Interner = Interner>>( value: T, ) -> Binders<T> { Binders::new( - VariableKinds::from_iter( + chalk_ir::VariableKinds::from_iter( Interner, std::iter::once(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)), ), @@ -353,7 +348,7 @@ pub(crate) fn variable_kinds_from_iter( db: &dyn HirDatabase, iter: impl Iterator<Item = hir_def::GenericParamId>, -) -> VariableKinds { +) -> VariableKinds<Interner> { VariableKinds::from_iter( Interner, iter.map(|x| match x { @@ -918,7 +913,7 @@ let obligation = InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() }; let canonical = table.canonicalize(obligation.clone()); - if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() { + if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() { table.register_obligation(obligation.goal); let return_ty = table.normalize_projection_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { @@ -929,7 +924,7 @@ environment: trait_env.clone(), }; let canonical = table.canonicalize(obligation.clone()); - if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() { + if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() { let ret_ty = table.resolve_completely(return_ty); let args_ty = table.resolve_completely(args_ty); let params = args_ty @@ -985,7 +980,7 @@ outer_binder: DebruijnIndex, ) -> std::ops::ControlFlow<Self::BreakTy> { let has_placeholder_bits = TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER; - let TyData { kind, flags } = ty.data(Interner); + let chalk_ir::TyData { kind, flags } = ty.data(Interner); if let TyKind::Placeholder(idx) = kind { self.collect(*idx); @@ -1045,3 +1040,25 @@ pub(crate) struct DeclContext { pub(crate) origin: DeclOrigin, } + +pub fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> { + use std::env; + use std::sync::LazyLock; + use tracing_subscriber::{Registry, layer::SubscriberExt}; + use tracing_tree::HierarchicalLayer; + + static ENABLE: LazyLock<bool> = LazyLock::new(|| env::var("CHALK_DEBUG").is_ok()); + if !*ENABLE { + return None; + } + + let filter: tracing_subscriber::filter::Targets = + env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default(); + let layer = HierarchicalLayer::default() + .with_indent_lines(true) + .with_ansi(false) + .with_indent_amount(2) + .with_writer(std::io::stderr); + let subscriber = Registry::default().with(filter).with(layer); + Some(tracing::subscriber::set_default(subscriber)) +}
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index f32b6af..7b6b3c3 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs
@@ -46,9 +46,9 @@ use triomphe::{Arc, ThinArc}; use crate::{ - AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig, - FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData, - LifetimeOutlives, PolyFnSig, ProgramClause, QuantifiedWhereClause, QuantifiedWhereClauses, + AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DomainGoal, DynTy, FnAbi, + FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, + LifetimeData, LifetimeOutlives, PolyFnSig, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, all_super_traits, consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic}, @@ -81,7 +81,7 @@ } } -pub(crate) struct PathDiagnosticCallbackData(TypeRefId); +pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId); #[derive(Debug, Clone)] pub enum LifetimeElisionKind { @@ -590,9 +590,14 @@ .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); let pointee_sized = LangItem::PointeeSized .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); - if meta_sized.is_some_and(|it| it == trait_ref.hir_trait_id()) { + let destruct = LangItem::Destruct + .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); + let hir_trait_id = trait_ref.hir_trait_id(); + if meta_sized.is_some_and(|it| it == hir_trait_id) + || destruct.is_some_and(|it| it == hir_trait_id) + { // Ignore this bound - } else if pointee_sized.is_some_and(|it| it == trait_ref.hir_trait_id()) { + } else if pointee_sized.is_some_and(|it| it == hir_trait_id) { // Regard this as `?Sized` bound ctx.ty_ctx().unsized_types.insert(self_ty); } else { @@ -825,10 +830,10 @@ let data = t.hir_trait_id().trait_items(db); for (name, assoc_id) in &data.items { - if let AssocItemId::TypeAliasId(alias) = assoc_id { - if let Some(result) = cb(name, &t, *alias) { - return Some(result); - } + if let AssocItemId::TypeAliasId(alias) = assoc_id + && let Some(result) = cb(name, &t, *alias) + { + return Some(result); } } None @@ -884,7 +889,7 @@ pub(crate) type Diagnostics = Option<ThinArc<(), TyLoweringDiagnostic>>; -fn create_diagnostics(diagnostics: Vec<TyLoweringDiagnostic>) -> Diagnostics { +pub(crate) fn create_diagnostics(diagnostics: Vec<TyLoweringDiagnostic>) -> Diagnostics { (!diagnostics.is_empty()).then(|| ThinArc::from_header_and_iter((), diagnostics.into_iter())) } @@ -1100,8 +1105,9 @@ traits_in_scope .push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id())); } - let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner); - clauses.push(program_clause.into_from_env_clause(Interner)); + let program_clause: Binders<DomainGoal> = + pred.map(|pred| pred.into_from_env_goal(Interner).cast(Interner)); + clauses.push(program_clause); } } } @@ -1114,7 +1120,10 @@ let substs = TyBuilder::placeholder_subst(db, trait_id); let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs }; let pred = WhereClause::Implemented(trait_ref); - clauses.push(pred.cast::<ProgramClause>(Interner).into_from_env_clause(Interner)); + clauses.push(Binders::empty( + Interner, + pred.cast::<DomainGoal>(Interner).into_from_env_goal(Interner), + )); } let subst = generics.placeholder_subst(db); @@ -1123,15 +1132,30 @@ if let Some(implicitly_sized_clauses) = implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) { - clauses.extend( - implicitly_sized_clauses.map(|pred| { - pred.cast::<ProgramClause>(Interner).into_from_env_clause(Interner) - }), - ); + clauses.extend(implicitly_sized_clauses.map(|pred| { + Binders::empty( + Interner, + pred.into_from_env_goal(Interner).cast::<DomainGoal>(Interner), + ) + })); }; } - let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); + let clauses = chalk_ir::ProgramClauses::from_iter( + Interner, + clauses.into_iter().map(|g| { + chalk_ir::ProgramClause::new( + Interner, + chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication { + consequence: g, + conditions: chalk_ir::Goals::empty(Interner), + constraints: chalk_ir::Constraints::empty(Interner), + priority: chalk_ir::ClausePriority::High, + })), + ) + }), + ); + let env = chalk_ir::Environment { clauses }; TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env) } @@ -1172,7 +1196,7 @@ } /// Resolve the where clause(s) of an item with generics, -/// except the ones inherited from the parent +/// with a given filter fn generic_predicates_filtered_by<F>( db: &dyn HirDatabase, def: GenericDefId,
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs index 5c06234..fb85909 100644 --- a/crates/hir-ty/src/lower/path.rs +++ b/crates/hir-ty/src/lower/path.rs
@@ -220,10 +220,6 @@ }; return (ty, None); } - TypeNs::TraitAliasId(_) => { - // FIXME(trait_alias): Implement trait alias. - return (TyKind::Error.intern(Interner), None); - } TypeNs::GenericParam(param_id) => match self.ctx.type_param_mode { ParamLoweringMode::Placeholder => { TyKind::Placeholder(to_placeholder_idx(self.ctx.db, param_id.into())) @@ -311,8 +307,7 @@ TypeNs::AdtId(_) | TypeNs::EnumVariantId(_) | TypeNs::TypeAliasId(_) - | TypeNs::TraitId(_) - | TypeNs::TraitAliasId(_) => {} + | TypeNs::TraitId(_) => {} } } @@ -360,15 +355,14 @@ } } - if let Some(enum_segment) = enum_segment { - if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) - && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) - { - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment: (enum_segment + 1) as u32, - reason: GenericArgsProhibitedReason::EnumVariant, - }); - } + if let Some(enum_segment) = enum_segment + && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }); } self.handle_type_ns_resolution(&resolution); @@ -417,15 +411,14 @@ } } - if let Some(enum_segment) = enum_segment { - if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) - && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) - { - self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { - segment: (enum_segment + 1) as u32, - reason: GenericArgsProhibitedReason::EnumVariant, - }); - } + if let Some(enum_segment) = enum_segment + && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }); } match &res { @@ -576,13 +569,12 @@ // This simplifies the code a bit. let penultimate_idx = self.current_segment_idx.wrapping_sub(1); let penultimate = self.segments.get(penultimate_idx); - if let Some(penultimate) = penultimate { - if self.current_or_prev_segment.args_and_bindings.is_none() - && penultimate.args_and_bindings.is_some() - { - self.current_segment_idx = penultimate_idx; - self.current_or_prev_segment = penultimate; - } + if let Some(penultimate) = penultimate + && self.current_or_prev_segment.args_and_bindings.is_none() + && penultimate.args_and_bindings.is_some() + { + self.current_segment_idx = penultimate_idx; + self.current_or_prev_segment = penultimate; } var.lookup(self.ctx.db).parent.into() } @@ -607,37 +599,36 @@ ) -> Substitution { let mut lifetime_elision = self.ctx.lifetime_elision.clone(); - if let Some(args) = self.current_or_prev_segment.args_and_bindings { - if args.parenthesized != GenericArgsParentheses::No { - let prohibit_parens = match def { - GenericDefId::TraitId(trait_) => { - // RTN is prohibited anyways if we got here. - let is_rtn = - args.parenthesized == GenericArgsParentheses::ReturnTypeNotation; - let is_fn_trait = self - .ctx - .db - .trait_signature(trait_) - .flags - .contains(TraitFlags::RUSTC_PAREN_SUGAR); - is_rtn || !is_fn_trait - } - _ => true, - }; - - if prohibit_parens { - let segment = self.current_segment_u32(); - self.on_diagnostic( - PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, - ); - - return TyBuilder::unknown_subst(self.ctx.db, def); + if let Some(args) = self.current_or_prev_segment.args_and_bindings + && args.parenthesized != GenericArgsParentheses::No + { + let prohibit_parens = match def { + GenericDefId::TraitId(trait_) => { + // RTN is prohibited anyways if we got here. + let is_rtn = args.parenthesized == GenericArgsParentheses::ReturnTypeNotation; + let is_fn_trait = self + .ctx + .db + .trait_signature(trait_) + .flags + .contains(TraitFlags::RUSTC_PAREN_SUGAR); + is_rtn || !is_fn_trait } + _ => true, + }; - // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. - lifetime_elision = - LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; + if prohibit_parens { + let segment = self.current_segment_u32(); + self.on_diagnostic( + PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, + ); + + return TyBuilder::unknown_subst(self.ctx.db, def); } + + // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. + lifetime_elision = + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; } self.substs_from_args_and_bindings( @@ -753,18 +744,20 @@ match param { GenericParamDataRef::LifetimeParamData(_) => error_lifetime().cast(Interner), GenericParamDataRef::TypeParamData(param) => { - if !infer_args && param.default.is_some() { - if let Some(default) = default() { - return default; - } + if !infer_args + && param.default.is_some() + && let Some(default) = default() + { + return default; } TyKind::Error.intern(Interner).cast(Interner) } GenericParamDataRef::ConstParamData(param) => { - if !infer_args && param.default.is_some() { - if let Some(default) = default() { - return default; - } + if !infer_args + && param.default.is_some() + && let Some(default) = default() + { + return default; } let GenericParamId::ConstParamId(const_id) = param_id else { unreachable!("non-const param ID for const param");
diff --git a/crates/hir-ty/src/lower_nextsolver.rs b/crates/hir-ty/src/lower_nextsolver.rs new file mode 100644 index 0000000..24bda43 --- /dev/null +++ b/crates/hir-ty/src/lower_nextsolver.rs
@@ -0,0 +1,1609 @@ +//! Methods for lowering the HIR to types. There are two main cases here: +//! +//! - Lowering a type reference like `&usize` or `Option<foo::bar::Baz>` to a +//! type: The entry point for this is `TyLoweringContext::lower_ty`. +//! - Building the type for an item: This happens through the `ty` query. +//! +//! This usually involves resolving names, collecting generic arguments etc. +#![allow(unused)] +// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir +pub(crate) mod path; + +use std::{ + cell::OnceCell, + iter, mem, + ops::{self, Not as _}, +}; + +use base_db::Crate; +use either::Either; +use hir_def::{ + AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId, + GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TypeAliasId, + TypeOrConstParamId, VariantId, + expr_store::{ + ExpressionStore, + path::{GenericArg, Path}, + }, + hir::generics::{TypeOrConstParamData, WherePredicate}, + lang_item::LangItem, + resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, + signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, + type_ref::{ + ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, + TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, + }, +}; +use hir_expand::name::Name; +use intern::sym; +use la_arena::{Arena, ArenaMap, Idx}; +use path::{PathDiagnosticCallback, PathLoweringContext, builtin}; +use rustc_ast_ir::Mutability; +use rustc_hash::FxHashSet; +use rustc_pattern_analysis::Captures; +use rustc_type_ir::{ + AliasTyKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, + ExistentialTraitRef, FnSig, OutlivesPredicate, + TyKind::{self}, + TypeVisitableExt, + inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _}, +}; +use salsa::plumbing::AsId; +use stdx::never; +use triomphe::Arc; + +use crate::{ + FnAbi, ImplTraitId, Interner, ParamKind, TyDefId, TyLoweringDiagnostic, + TyLoweringDiagnosticKind, + consteval_nextsolver::{intern_const_ref, path_to_const, unknown_const_as_generic}, + db::HirDatabase, + generics::{Generics, generics, trait_self_param_idx}, + lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics}, + next_solver::{ + AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, + BoundVarKind, BoundVarKinds, Clause, Const, DbInterner, EarlyBinder, EarlyParamRegion, + ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, + TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, mapping::ChalkToNextSolver, + }, +}; + +#[derive(PartialEq, Eq, Debug, Hash)] +pub struct ImplTraits<'db> { + pub(crate) impl_traits: Arena<ImplTrait<'db>>, +} + +#[derive(PartialEq, Eq, Debug, Hash)] +pub(crate) struct ImplTrait<'db> { + pub(crate) predicates: Vec<Clause<'db>>, +} + +pub(crate) type ImplTraitIdx<'db> = Idx<ImplTrait<'db>>; + +#[derive(Debug, Default)] +struct ImplTraitLoweringState<'db> { + /// When turning `impl Trait` into opaque types, we have to collect the + /// bounds at the same time to get the IDs correct (without becoming too + /// complicated). + mode: ImplTraitLoweringMode, + // This is structured as a struct with fields and not as an enum because it helps with the borrow checker. + opaque_type_data: Arena<ImplTrait<'db>>, + param_and_variable_counter: u16, +} +impl<'db> ImplTraitLoweringState<'db> { + fn new(mode: ImplTraitLoweringMode) -> ImplTraitLoweringState<'db> { + Self { mode, opaque_type_data: Arena::new(), param_and_variable_counter: 0 } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum LifetimeElisionKind<'db> { + /// Create a new anonymous lifetime parameter and reference it. + /// + /// If `report_in_path`, report an error when encountering lifetime elision in a path: + /// ```compile_fail + /// struct Foo<'a> { x: &'a () } + /// async fn foo(x: Foo) {} + /// ``` + /// + /// Note: the error should not trigger when the elided lifetime is in a pattern or + /// expression-position path: + /// ``` + /// struct Foo<'a> { x: &'a () } + /// async fn foo(Foo { x: _ }: Foo<'_>) {} + /// ``` + AnonymousCreateParameter { report_in_path: bool }, + + /// Replace all anonymous lifetimes by provided lifetime. + Elided(Region<'db>), + + /// Give a hard error when either `&` or `'_` is written. Used to + /// rule out things like `where T: Foo<'_>`. Does not imply an + /// error on default object bounds (e.g., `Box<dyn Foo>`). + AnonymousReportError, + + /// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope, + /// otherwise give a warning that the previous behavior of introducing a new early-bound + /// lifetime is a bug and will be removed (if `only_lint` is enabled). + StaticIfNoLifetimeInScope { only_lint: bool }, + + /// Signal we cannot find which should be the anonymous lifetime. + ElisionFailure, + + /// Infer all elided lifetimes. + Infer, +} + +impl<'db> LifetimeElisionKind<'db> { + #[inline] + pub(crate) fn for_const( + interner: DbInterner<'db>, + const_parent: ItemContainerId, + ) -> LifetimeElisionKind<'db> { + match const_parent { + ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => { + LifetimeElisionKind::Elided(Region::new_static(interner)) + } + ItemContainerId::ImplId(_) => { + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: true } + } + ItemContainerId::TraitId(_) => { + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: false } + } + } + } + + #[inline] + pub(crate) fn for_fn_params(data: &FunctionSignature) -> LifetimeElisionKind<'db> { + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: data.is_async() } + } + + #[inline] + pub(crate) fn for_fn_ret(interner: DbInterner<'db>) -> LifetimeElisionKind<'db> { + // FIXME: We should use the elided lifetime here, or `ElisionFailure`. + LifetimeElisionKind::Elided(Region::error(interner)) + } +} + +#[derive(Debug)] +pub(crate) struct TyLoweringContext<'db, 'a> { + pub db: &'db dyn HirDatabase, + interner: DbInterner<'db>, + resolver: &'a Resolver<'db>, + store: &'a ExpressionStore, + def: GenericDefId, + generics: OnceCell<Generics>, + in_binders: DebruijnIndex, + impl_trait_mode: ImplTraitLoweringState<'db>, + /// Tracks types with explicit `?Sized` bounds. + pub(crate) unsized_types: FxHashSet<Ty<'db>>, + pub(crate) diagnostics: Vec<TyLoweringDiagnostic>, + lifetime_elision: LifetimeElisionKind<'db>, +} + +impl<'db, 'a> TyLoweringContext<'db, 'a> { + pub(crate) fn new( + db: &'db dyn HirDatabase, + resolver: &'a Resolver<'db>, + store: &'a ExpressionStore, + def: GenericDefId, + lifetime_elision: LifetimeElisionKind<'db>, + ) -> Self { + let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); + let in_binders = DebruijnIndex::ZERO; + Self { + db, + interner: DbInterner::new_with(db, Some(resolver.krate()), None), + resolver, + def, + generics: Default::default(), + store, + in_binders, + impl_trait_mode, + unsized_types: FxHashSet::default(), + diagnostics: Vec::new(), + lifetime_elision, + } + } + + pub(crate) fn with_debruijn<T>( + &mut self, + debruijn: DebruijnIndex, + f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, + ) -> T { + let old_debruijn = mem::replace(&mut self.in_binders, debruijn); + let result = f(self); + self.in_binders = old_debruijn; + result + } + + pub(crate) fn with_shifted_in<T>( + &mut self, + debruijn: DebruijnIndex, + f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, + ) -> T { + self.with_debruijn(self.in_binders.shifted_in(debruijn.as_u32()), f) + } + + pub(crate) fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { + Self { impl_trait_mode: ImplTraitLoweringState::new(impl_trait_mode), ..self } + } + + pub(crate) fn impl_trait_mode(&mut self, impl_trait_mode: ImplTraitLoweringMode) -> &mut Self { + self.impl_trait_mode = ImplTraitLoweringState::new(impl_trait_mode); + self + } + + pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) { + self.diagnostics.push(TyLoweringDiagnostic { source: type_ref, kind }); + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +pub(crate) enum ImplTraitLoweringMode { + /// `impl Trait` gets lowered into an opaque type that doesn't unify with + /// anything except itself. This is used in places where values flow 'out', + /// i.e. for arguments of the function we're currently checking, and return + /// types of functions we're calling. + Opaque, + /// `impl Trait` is disallowed and will be an error. + #[default] + Disallowed, +} + +impl<'db, 'a> TyLoweringContext<'db, 'a> { + pub(crate) fn lower_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { + self.lower_ty_ext(type_ref).0 + } + + pub(crate) fn lower_const(&mut self, const_ref: &ConstRef, const_type: Ty<'db>) -> Const<'db> { + let const_ref = &self.store[const_ref.expr]; + match const_ref { + hir_def::hir::Expr::Path(path) => { + path_to_const(self.db, self.resolver, path, || self.generics(), const_type) + .unwrap_or_else(|| unknown_const(const_type)) + } + hir_def::hir::Expr::Literal(literal) => intern_const_ref( + self.db, + &match *literal { + hir_def::hir::Literal::Float(_, _) + | hir_def::hir::Literal::String(_) + | hir_def::hir::Literal::ByteString(_) + | hir_def::hir::Literal::CString(_) => LiteralConstRef::Unknown, + hir_def::hir::Literal::Char(c) => LiteralConstRef::Char(c), + hir_def::hir::Literal::Bool(b) => LiteralConstRef::Bool(b), + hir_def::hir::Literal::Int(val, _) => LiteralConstRef::Int(val), + hir_def::hir::Literal::Uint(val, _) => LiteralConstRef::UInt(val), + }, + const_type, + self.resolver.krate(), + ), + _ => 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, path, || self.generics(), const_type) + .unwrap_or_else(|| unknown_const(const_type)) + } + + fn generics(&self) -> &Generics { + self.generics.get_or_init(|| generics(self.db, self.def)) + } + + #[tracing::instrument(skip(self), ret)] + pub(crate) fn lower_ty_ext(&mut self, type_ref_id: TypeRefId) -> (Ty<'db>, Option<TypeNs>) { + let interner = self.interner; + let mut res = None; + let type_ref = &self.store[type_ref_id]; + tracing::debug!(?type_ref); + let ty = match type_ref { + TypeRef::Never => Ty::new(interner, TyKind::Never), + TypeRef::Tuple(inner) => { + let inner_tys = inner.iter().map(|&tr| self.lower_ty(tr)); + Ty::new_tup_from_iter(interner, inner_tys) + } + TypeRef::Path(path) => { + let (ty, res_) = + self.lower_path(path, PathId::from_type_ref_unchecked(type_ref_id)); + res = res_; + ty + } + &TypeRef::TypeParam(type_param_id) => { + 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"); + let type_data = match data { + TypeOrConstParamData::TypeParamData(ty) => ty, + _ => unreachable!(), + }; + Ty::new_param( + self.interner, + idx as u32, + type_data + .name + .as_ref() + .map_or_else(|| sym::MISSING_NAME.clone(), |d| d.symbol().clone()), + ) + } + &TypeRef::RawPtr(inner, mutability) => { + let inner_ty = self.lower_ty(inner); + Ty::new(interner, TyKind::RawPtr(inner_ty, lower_mutability(mutability))) + } + TypeRef::Array(array) => { + let inner_ty = self.lower_ty(array.ty); + let const_len = self.lower_const(&array.len, Ty::new_usize(interner)); + Ty::new_array_with_const_len(interner, inner_ty, const_len) + } + &TypeRef::Slice(inner) => { + let inner_ty = self.lower_ty(inner); + Ty::new_slice(interner, inner_ty) + } + 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)); + Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability)) + } + TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed), + TypeRef::Fn(fn_) => { + let substs = self.with_shifted_in( + DebruijnIndex::from_u32(1), + |ctx: &mut TyLoweringContext<'_, '_>| { + Tys::new_from_iter( + interner, + fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)), + ) + }, + ); + 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, + inputs_and_output: substs, + }), + ) + } + TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), + TypeRef::ImplTrait(bounds) => { + match self.impl_trait_mode.mode { + ImplTraitLoweringMode::Opaque => { + let origin = match self.resolver.generic_def() { + Some(GenericDefId::FunctionId(it)) => Either::Left(it), + Some(GenericDefId::TypeAliasId(it)) => Either::Right(it), + _ => panic!( + "opaque impl trait lowering must be in function or type alias" + ), + }; + + // this dance is to make sure the data is in the right + // place even if we encounter more opaque types while + // lowering the bounds + let idx = self + .impl_trait_mode + .opaque_type_data + .alloc(ImplTrait { predicates: Vec::default() }); + + // FIXME(next-solver): this from_raw/into_raw dance isn't nice, but it's minimal + let impl_trait_id = origin.either( + |f| ImplTraitId::ReturnTypeImplTrait(f, Idx::from_raw(idx.into_raw())), + |a| ImplTraitId::TypeAliasImplTrait(a, Idx::from_raw(idx.into_raw())), + ); + let opaque_ty_id: SolverDefId = + self.db.intern_impl_trait_id(impl_trait_id).into(); + + // We don't want to lower the bounds inside the binders + // we're currently in, because they don't end up inside + // those binders. E.g. when we have `impl Trait<impl + // OtherTrait<T>>`, the `impl OtherTrait<T>` can't refer + // to the self parameter from `impl Trait`, and the + // bounds aren't actually stored nested within each + // other, but separately. So if the `T` refers to a type + // parameter of the outer function, it's just one binder + // away instead of two. + let actual_opaque_type_data = self + .with_debruijn(DebruijnIndex::ZERO, |ctx| { + ctx.lower_impl_trait(opaque_ty_id, bounds, self.resolver.krate()) + }); + self.impl_trait_mode.opaque_type_data[idx] = actual_opaque_type_data; + + let args = GenericArgs::identity_for_item(self.interner, opaque_ty_id); + Ty::new_alias( + self.interner, + AliasTyKind::Opaque, + AliasTy::new_from_args(self.interner, opaque_ty_id, args), + ) + } + ImplTraitLoweringMode::Disallowed => { + // FIXME: report error + Ty::new_error(self.interner, ErrorGuaranteed) + } + } + } + TypeRef::Error => Ty::new_error(self.interner, ErrorGuaranteed), + }; + (ty, res) + } + + /// This is only for `generic_predicates_for_param`, where we can't just + /// lower the self types of the predicates since that could lead to cycles. + /// So we just check here if the `type_ref` resolves to a generic param, and which. + fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option<TypeOrConstParamId> { + let type_ref = &self.store[type_ref]; + let path = match type_ref { + TypeRef::Path(path) => path, + &TypeRef::TypeParam(idx) => return Some(idx.into()), + _ => return None, + }; + if path.type_anchor().is_some() { + return None; + } + if path.segments().len() > 1 { + return None; + } + let resolution = match self.resolver.resolve_path_in_type_ns(self.db, path) { + Some((it, None, _)) => it, + _ => return None, + }; + match resolution { + TypeNs::GenericParam(param_id) => Some(param_id.into()), + _ => None, + } + } + + #[inline] + fn on_path_diagnostic_callback(type_ref: TypeRefId) -> PathDiagnosticCallback<'static, 'db> { + PathDiagnosticCallback { + data: Either::Left(PathDiagnosticCallbackData(type_ref)), + callback: |data, this, diag| { + let type_ref = data.as_ref().left().unwrap().0; + this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag)) + }, + } + } + + #[inline] + fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'a, 'db> { + PathLoweringContext::new( + self, + Self::on_path_diagnostic_callback(path_id.type_ref()), + &self.store[path_id], + ) + } + + pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty<'db>, Option<TypeNs>) { + // Resolve the path (in type namespace) + 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); + } + + 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), + }; + + if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { + // trait object type without dyn + let bound = TypeBound::Path(path_id, TraitBoundModifier::None); + let ty = self.lower_dyn_trait(&[bound]); + return (ty, None); + } + + ctx.lower_partly_resolved_path(resolution, false) + } + + fn lower_trait_ref_from_path( + &mut self, + path_id: PathId, + explicit_self_ty: Ty<'db>, + ) -> Option<(TraitRef<'db>, PathLoweringContext<'_, 'a, 'db>)> { + let mut ctx = self.at_path(path_id); + let resolved = match ctx.resolve_path_in_type_ns_fully()? { + // FIXME(trait_alias): We need to handle trait alias here. + TypeNs::TraitId(tr) => tr, + _ => return None, + }; + Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty), ctx)) + } + + fn lower_trait_ref( + &mut self, + trait_ref: &HirTraitRef, + explicit_self_ty: Ty<'db>, + ) -> Option<TraitRef<'db>> { + self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0) + } + + pub(crate) fn lower_where_predicate<'b>( + &'b mut self, + where_predicate: &'b WherePredicate, + ignore_bindings: bool, + generics: &Generics, + predicate_filter: PredicateFilter, + ) -> impl Iterator<Item = Clause<'db>> + use<'a, 'b, 'db> { + match where_predicate { + WherePredicate::ForLifetime { target, bound, .. } + | WherePredicate::TypeBound { target, bound } => { + if let PredicateFilter::SelfTrait = predicate_filter { + let target_type = &self.store[*target]; + let self_type = 'is_self: { + if let TypeRef::Path(path) = target_type + && path.is_self_type() + { + break 'is_self true; + } + if let TypeRef::TypeParam(param) = target_type + && generics[param.local_id()].is_trait_self() + { + break 'is_self true; + } + false + }; + if !self_type { + return Either::Left(Either::Left(iter::empty())); + } + } + let self_ty = self.lower_ty(*target); + Either::Left(Either::Right(self.lower_type_bound(bound, self_ty, ignore_bindings))) + } + &WherePredicate::Lifetime { bound, target } => { + Either::Right(iter::once(Clause(Predicate::new( + self.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::RegionOutlives(OutlivesPredicate( + self.lower_lifetime(bound), + self.lower_lifetime(target), + )), + )), + )))) + } + } + .into_iter() + } + + pub(crate) fn lower_type_bound<'b>( + &'b mut self, + bound: &'b TypeBound, + self_ty: Ty<'db>, + ignore_bindings: bool, + ) -> impl Iterator<Item = Clause<'db>> + use<'b, 'a, 'db> { + let interner = self.interner; + let mut assoc_bounds = None; + let mut clause = None; + match bound { + &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => { + // FIXME Don't silently drop the hrtb lifetimes here + if let Some((trait_ref, mut ctx)) = self.lower_trait_ref_from_path(path, self_ty) { + // FIXME(sized-hierarchy): Remove this bound modifications once we have implemented + // sized-hierarchy correctly. + let meta_sized = LangItem::MetaSized + .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); + let pointee_sized = LangItem::PointeeSized + .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); + if meta_sized.is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) { + // Ignore this bound + } else if pointee_sized + .is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) + { + // Regard this as `?Sized` bound + ctx.ty_ctx().unsized_types.insert(self_ty); + } else { + if !ignore_bindings { + assoc_bounds = ctx.assoc_type_bindings_from_type_bound(trait_ref); + } + clause = Some(Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + ))); + } + } + } + &TypeBound::Path(path, TraitBoundModifier::Maybe) => { + let sized_trait = LangItem::Sized.resolve_trait(self.db, self.resolver.krate()); + // Don't lower associated type bindings as the only possible relaxed trait bound + // `?Sized` has no of them. + // If we got another trait here ignore the bound completely. + let trait_id = + self.lower_trait_ref_from_path(path, self_ty).map(|(trait_ref, _)| { + match trait_ref.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + } + }); + if trait_id == sized_trait { + self.unsized_types.insert(self_ty); + } + } + &TypeBound::Lifetime(l) => { + let lifetime = self.lower_lifetime(l); + clause = Some(Clause(Predicate::new( + self.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::TypeOutlives(OutlivesPredicate( + self_ty, lifetime, + )), + )), + ))); + } + TypeBound::Use(_) | TypeBound::Error => {} + } + clause.into_iter().chain(assoc_bounds.into_iter().flatten()) + } + + fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> { + let interner = self.interner; + // FIXME: we should never create non-existential predicates in the first place + // For now, use an error type so we don't run into dummy binder issues + let self_ty = Ty::new_error(interner, ErrorGuaranteed); + // 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 + // bounds in the input. + // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound. + // These invariants are utilized by `TyExt::dyn_trait()` and chalk. + let mut lifetime = None; + let bounds = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let mut lowered_bounds: Vec< + rustc_type_ir::Binder<DbInterner<'db>, ExistentialPredicate<DbInterner<'db>>>, + > = Vec::new(); + for b in bounds { + let db = ctx.db; + ctx.lower_type_bound(b, self_ty, false).for_each(|b| { + if let Some(bound) = b + .kind() + .map_bound(|c| match c { + rustc_type_ir::ClauseKind::Trait(t) => { + let id = t.def_id(); + let id = match id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let is_auto = + db.trait_signature(id).flags.contains(TraitFlags::AUTO); + if is_auto { + Some(ExistentialPredicate::AutoTrait(t.def_id())) + } else { + Some(ExistentialPredicate::Trait( + ExistentialTraitRef::new_from_args( + interner, + t.def_id(), + GenericArgs::new_from_iter( + interner, + t.trait_ref.args.iter().skip(1), + ), + ), + )) + } + } + rustc_type_ir::ClauseKind::Projection(p) => { + Some(ExistentialPredicate::Projection( + ExistentialProjection::new_from_args( + interner, + p.def_id(), + GenericArgs::new_from_iter( + interner, + p.projection_term.args.iter().skip(1), + ), + p.term, + ), + )) + } + rustc_type_ir::ClauseKind::TypeOutlives(outlives_predicate) => { + lifetime = Some(outlives_predicate.1); + None + } + rustc_type_ir::ClauseKind::RegionOutlives(_) + | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) + | rustc_type_ir::ClauseKind::WellFormed(_) + | rustc_type_ir::ClauseKind::ConstEvaluatable(_) + | rustc_type_ir::ClauseKind::HostEffect(_) + | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), + }) + .transpose() + { + lowered_bounds.push(bound); + } + }) + } + + let mut multiple_regular_traits = false; + let mut multiple_same_projection = false; + lowered_bounds.sort_unstable_by(|lhs, rhs| { + use std::cmp::Ordering; + match ((*lhs).skip_binder(), (*rhs).skip_binder()) { + (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => { + multiple_regular_traits = true; + // Order doesn't matter - we error + Ordering::Equal + } + ( + ExistentialPredicate::AutoTrait(lhs_id), + ExistentialPredicate::AutoTrait(rhs_id), + ) => { + let lhs_id = match lhs_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let rhs_id = match rhs_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + lhs_id.cmp(&rhs_id) + } + (ExistentialPredicate::Trait(_), _) => Ordering::Less, + (_, ExistentialPredicate::Trait(_)) => Ordering::Greater, + (ExistentialPredicate::AutoTrait(_), _) => Ordering::Less, + (_, ExistentialPredicate::AutoTrait(_)) => Ordering::Greater, + ( + ExistentialPredicate::Projection(lhs), + ExistentialPredicate::Projection(rhs), + ) => { + let lhs_id = match lhs.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + let rhs_id = match rhs.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + // We only compare the `associated_ty_id`s. We shouldn't have + // multiple bounds for an associated type in the correct Rust code, + // and if we do, we error out. + if lhs_id == rhs_id { + multiple_same_projection = true; + } + lhs_id.as_id().index().cmp(&rhs_id.as_id().index()) + } + } + }); + + if multiple_regular_traits || multiple_same_projection { + return None; + } + + if !lowered_bounds.first().map_or(false, |b| { + matches!( + b.as_ref().skip_binder(), + ExistentialPredicate::Trait(_) | ExistentialPredicate::AutoTrait(_) + ) + }) { + return None; + } + + // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the + // bounds. We shouldn't have repeated elements besides auto traits at this point. + lowered_bounds.dedup(); + + Some(BoundExistentialPredicates::new_from_iter(interner, lowered_bounds)) + }); + + if let Some(bounds) = bounds { + let region = match lifetime { + Some(it) => match it.kind() { + rustc_type_ir::RegionKind::ReBound(db, var) => Region::new_bound( + self.interner, + db.shifted_out_to_binder(DebruijnIndex::from_u32(2)), + var, + ), + _ => it, + }, + None => Region::new_static(self.interner), + }; + Ty::new_dynamic(self.interner, bounds, region, rustc_type_ir::DynKind::Dyn) + } else { + // FIXME: report error + // (additional non-auto traits, associated type rebound, or no resolved trait) + Ty::new_error(self.interner, ErrorGuaranteed) + } + } + + fn lower_impl_trait( + &mut self, + def_id: SolverDefId, + bounds: &[TypeBound], + krate: Crate, + ) -> ImplTrait<'db> { + let interner = self.interner; + cov_mark::hit!(lower_rpit); + let args = GenericArgs::identity_for_item(interner, def_id); + let self_ty = Ty::new_alias( + self.interner, + rustc_type_ir::AliasTyKind::Opaque, + AliasTy::new_from_args(interner, def_id, args), + ); + let predicates = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let mut predicates = Vec::new(); + for b in bounds { + predicates.extend(ctx.lower_type_bound(b, self_ty, false)); + } + + if !ctx.unsized_types.contains(&self_ty) { + let sized_trait = LangItem::Sized.resolve_trait(self.db, krate); + let sized_clause = sized_trait.map(|trait_id| { + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_iter(interner, [self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }); + predicates.extend(sized_clause); + } + predicates.shrink_to_fit(); + predicates + }); + ImplTrait { predicates } + } + + pub(crate) fn lower_lifetime(&self, lifetime: LifetimeRefId) -> Region<'db> { + match self.resolver.resolve_lifetime(&self.store[lifetime]) { + 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, + }; + Region::new_early_param(self.interner, EarlyParamRegion { index: idx as u32 }) + } + }, + None => Region::error(self.interner), + } + } +} + +pub(crate) fn lower_mutability(m: hir_def::type_ref::Mutability) -> Mutability { + match m { + hir_def::type_ref::Mutability::Shared => Mutability::Not, + hir_def::type_ref::Mutability::Mut => Mutability::Mut, + } +} + +fn unknown_const(_ty: Ty<'_>) -> Const<'_> { + Const::new(DbInterner::conjure(), ConstKind::Error(ErrorGuaranteed)) +} + +pub(crate) fn impl_trait_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> Option<EarlyBinder<'db, TraitRef<'db>>> { + db.impl_trait_with_diagnostics_ns(impl_id).map(|it| it.0) +} + +pub(crate) fn impl_trait_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> Option<(EarlyBinder<'db, TraitRef<'db>>, Diagnostics)> { + let impl_data = db.impl_signature(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_ns(impl_id).skip_binder(); + let target_trait = impl_data.target_trait.as_ref()?; + let trait_ref = EarlyBinder::bind(ctx.lower_trait_ref(target_trait, self_ty)?); + Some((trait_ref, create_diagnostics(ctx.diagnostics))) +} + +pub(crate) fn return_type_impl_traits<'db>( + db: &'db dyn HirDatabase, + def: hir_def::FunctionId, +) -> Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>> { + // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe + let data = db.function_signature(def); + let resolver = def.resolver(db); + let mut ctx_ret = + TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + if let Some(ret_type) = data.ret_type { + let _ret = ctx_ret.lower_ty(ret_type); + } + let return_type_impl_traits = + ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data }; + if return_type_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(EarlyBinder::bind(return_type_impl_traits))) + } +} + +pub(crate) fn type_alias_impl_traits<'db>( + db: &'db dyn HirDatabase, + def: hir_def::TypeAliasId, +) -> Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>> { + let data = db.type_alias_signature(def); + let resolver = def.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + if let Some(type_ref) = data.ty { + let _ty = ctx.lower_ty(type_ref); + } + let type_alias_impl_traits = ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data }; + if type_alias_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(EarlyBinder::bind(type_alias_impl_traits))) + } +} + +/// Build the declared type of an item. This depends on the namespace; e.g. for +/// `struct Foo(usize)`, we have two types: The type of the struct itself, and +/// the constructor function `(usize) -> Foo` which lives in the values +/// namespace. +pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + match def { + TyDefId::BuiltinType(it) => EarlyBinder::bind(builtin(interner, it)), + TyDefId::AdtId(it) => EarlyBinder::bind(Ty::new_adt( + interner, + AdtDef::new(it, interner), + GenericArgs::identity_for_item(interner, it.into()), + )), + TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics_ns(it).0, + } +} + +pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + t: TypeAliasId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + let type_alias_data = db.type_alias_signature(t); + let mut diags = None; + let resolver = t.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let inner = if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { + EarlyBinder::bind(Ty::new_foreign(interner, t.into())) + } else { + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + t.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + let res = EarlyBinder::bind( + type_alias_data + .ty + .map(|type_ref| ctx.lower_ty(type_ref)) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)), + ); + diags = create_diagnostics(ctx.diagnostics); + res + }; + (inner, diags) +} + +pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result<'db>( + db: &'db dyn HirDatabase, + _adt: TypeAliasId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None) +} + +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_ns(impl_id).0 +} + +pub(crate) fn impl_self_ty_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + let resolver = impl_id.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + + let impl_data = db.impl_signature(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()); + (EarlyBinder::bind(ty), create_diagnostics(ctx.diagnostics)) +} + +pub(crate) fn impl_self_ty_with_diagnostics_cycle_result( + db: &dyn HirDatabase, + _impl_id: ImplId, +) -> (EarlyBinder<'_, Ty<'_>>, Diagnostics) { + (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None) +} + +pub(crate) fn const_param_ty_query<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> { + db.const_param_ty_with_diagnostics_ns(def).0 +} + +// returns None if def is a type arg +pub(crate) fn const_param_ty_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + def: ConstParamId, +) -> (Ty<'db>, Diagnostics) { + let (parent_data, store) = db.generic_params_and_store(def.parent()); + let data = &parent_data[def.local_id()]; + let resolver = def.parent().resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + 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, create_diagnostics(ctx.diagnostics)) +} + +pub(crate) fn field_types_query<'db>( + db: &'db dyn HirDatabase, + variant_id: VariantId, +) -> Arc<ArenaMap<LocalFieldId, EarlyBinder<'db, Ty<'db>>>> { + db.field_types_with_diagnostics_ns(variant_id).0 +} + +/// Build the type of all specific fields of a struct or enum variant. +pub(crate) fn field_types_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + variant_id: VariantId, +) -> (Arc<ArenaMap<LocalFieldId, EarlyBinder<'db, Ty<'db>>>>, Diagnostics) { + let var_data = variant_id.fields(db); + let fields = var_data.fields(); + if fields.is_empty() { + return (Arc::new(ArenaMap::default()), None); + } + + let (resolver, 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 mut res = ArenaMap::default(); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &var_data.store, + def, + LifetimeElisionKind::AnonymousReportError, + ); + for (field_id, field_data) in var_data.fields().iter() { + res.insert(field_id, EarlyBinder::bind(ctx.lower_ty(field_data.type_ref))); + } + (Arc::new(res), create_diagnostics(ctx.diagnostics)) +} + +/// This query exists only to be used when resolving short-hand associated types +/// like `T::Item`. +/// +/// See the analogous query in rustc and its comment: +/// <https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46> +/// This is a query mostly to handle cycles somewhat gracefully; e.g. the +/// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but +/// these are fine: `T: Foo<U::Item>, U: Foo<()>`. +#[tracing::instrument(skip(db), ret)] +pub(crate) fn generic_predicates_for_param_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + param_id: TypeOrConstParamId, + assoc_name: Option<Name>, +) -> GenericPredicates<'db> { + let generics = generics(db, def); + let interner = DbInterner::new_with(db, None, None); + let resolver = def.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ); + + // we have to filter out all other predicates *first*, before attempting to lower them + let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred { + WherePredicate::ForLifetime { target, bound, .. } + | WherePredicate::TypeBound { target, bound, .. } => { + let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) }; + if invalid_target { + // FIXME(sized-hierarchy): Revisit and adjust this properly once we have implemented + // sized-hierarchy correctly. + // If this is filtered out without lowering, `?Sized` or `PointeeSized` is not gathered into + // `ctx.unsized_types` + let lower = || -> bool { + match bound { + TypeBound::Path(_, TraitBoundModifier::Maybe) => true, + TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { + let TypeRef::Path(path) = &ctx.store[path.type_ref()] else { + return false; + }; + let Some(pointee_sized) = + LangItem::PointeeSized.resolve_trait(ctx.db, ctx.resolver.krate()) + else { + return false; + }; + // Lower the path directly with `Resolver` instead of PathLoweringContext` + // to prevent diagnostics duplications. + ctx.resolver.resolve_path_in_type_ns_fully(ctx.db, path).is_some_and( + |it| matches!(it, TypeNs::TraitId(tr) if tr == pointee_sized), + ) + } + _ => false, + } + }(); + if lower { + ctx.lower_where_predicate(pred, true, &generics, PredicateFilter::All) + .for_each(drop); + } + return false; + } + + match bound { + &TypeBound::ForLifetime(_, path) | &TypeBound::Path(path, _) => { + // Only lower the bound if the trait could possibly define the associated + // type we're looking for. + let path = &ctx.store[path]; + + let Some(assoc_name) = &assoc_name else { return true }; + let Some(TypeNs::TraitId(tr)) = + resolver.resolve_path_in_type_ns_fully(db, path) + else { + return false; + }; + + rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| { + let tr = match tr { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + tr.trait_items(db).items.iter().any(|(name, item)| { + matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name + }) + }) + } + TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false, + } + } + WherePredicate::Lifetime { .. } => false, + }; + let mut predicates = Vec::new(); + for maybe_parent_generics in + std::iter::successors(Some(&generics), |generics| generics.parent_generics()) + { + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + if predicate(pred, &mut ctx) { + predicates.extend(ctx.lower_where_predicate( + pred, + true, + maybe_parent_generics, + PredicateFilter::All, + )); + } + } + } + + let args = GenericArgs::identity_for_item(interner, def.into()); + if !args.is_empty() { + let explicitly_unsized_tys = ctx.unsized_types; + if let Some(implicitly_sized_predicates) = + implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &args, &resolver) + { + predicates.extend(implicitly_sized_predicates); + }; + } + GenericPredicates(predicates.is_empty().not().then(|| predicates.into())) +} + +pub(crate) fn generic_predicates_for_param_cycle_result( + _db: &dyn HirDatabase, + _def: GenericDefId, + _param_id: TypeOrConstParamId, + _assoc_name: Option<Name>, +) -> GenericPredicates<'_> { + GenericPredicates(None) +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenericPredicates<'db>(Option<Arc<[Clause<'db>]>>); + +impl<'db> ops::Deref for GenericPredicates<'db> { + type Target = [Clause<'db>]; + + fn deref(&self) -> &Self::Target { + self.0.as_deref().unwrap_or(&[]) + } +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum PredicateFilter { + SelfTrait, + All, +} + +/// Resolve the where clause(s) of an item with generics. +#[tracing::instrument(skip(db))] +pub(crate) fn generic_predicates_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates<'db> { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |_| true).0 +} + +pub(crate) fn generic_predicates_without_parent_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates<'db> { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def).0 +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +pub(crate) fn generic_predicates_without_parent_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> (GenericPredicates<'db>, Diagnostics) { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def) +} + +/// Resolve the where clause(s) of an item with generics, +/// with a given filter +#[tracing::instrument(skip(db, filter), ret)] +pub(crate) fn generic_predicates_filtered_by<'db, F>( + db: &'db dyn HirDatabase, + def: GenericDefId, + predicate_filter: PredicateFilter, + filter: F, +) -> (GenericPredicates<'db>, Diagnostics) +where + F: Fn(GenericDefId) -> bool, +{ + let generics = generics(db, def); + let resolver = def.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ); + + let mut predicates = Vec::new(); + for maybe_parent_generics in + std::iter::successors(Some(&generics), |generics| generics.parent_generics()) + { + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + tracing::debug!(?pred); + if filter(maybe_parent_generics.def()) { + // We deliberately use `generics` and not `maybe_parent_generics` here. This is not a mistake! + // If we use the parent generics + predicates.extend(ctx.lower_where_predicate( + pred, + false, + maybe_parent_generics, + predicate_filter, + )); + } + } + } + + let explicitly_unsized_tys = ctx.unsized_types; + + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); + if let Some(sized_trait) = sized_trait { + let (mut generics, mut def_id) = + (crate::next_solver::generics::generics(db, def.into()), def); + loop { + if filter(def_id) { + let self_idx = trait_self_param_idx(db, def_id); + for (idx, p) in generics.own_params.iter().enumerate() { + if let Some(self_idx) = self_idx + && p.index() as usize == self_idx + { + continue; + } + let GenericParamDefKind::Type = p.kind else { + continue; + }; + let idx = idx as u32 + generics.parent_count as u32; + let param_ty = Ty::new_param(interner, idx, p.name.clone()); + if explicitly_unsized_tys.contains(¶m_ty) { + continue; + } + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_iter(interner, [param_ty.into()]), + ); + let clause = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )); + predicates.push(clause); + } + } + + if let Some(g) = generics.parent { + generics = crate::next_solver::generics::generics(db, g.into()); + def_id = g; + } else { + break; + } + } + } + + ( + GenericPredicates(predicates.is_empty().not().then(|| predicates.into())), + create_diagnostics(ctx.diagnostics), + ) +} + +/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. +/// Exception is Self of a trait def. +fn implicitly_sized_clauses<'a, 'subst, 'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + explicitly_unsized_tys: &'a FxHashSet<Ty<'db>>, + args: &'subst GenericArgs<'db>, + resolver: &Resolver<'db>, +) -> Option<impl Iterator<Item = Clause<'db>> + Captures<'a> + Captures<'subst>> { + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate())?; + + let trait_self_idx = trait_self_param_idx(db, def); + + Some( + args.iter() + .enumerate() + .filter_map( + move |(idx, generic_arg)| { + if Some(idx) == trait_self_idx { None } else { Some(generic_arg) } + }, + ) + .filter_map(|generic_arg| generic_arg.as_type()) + .filter(move |self_ty| !explicitly_unsized_tys.contains(self_ty)) + .map(move |self_ty| { + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_iter(interner, [self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }), + ) +} + +pub(crate) fn make_binders<'db, T: rustc_type_ir::TypeVisitable<DbInterner<'db>>>( + interner: DbInterner<'db>, + generics: &Generics, + value: T, +) -> Binder<'db, T> { + Binder::bind_with_vars( + value, + BoundVarKinds::new_from_iter( + interner, + generics.iter_id().map(|x| match x { + hir_def::GenericParamId::ConstParamId(_) => BoundVarKind::Const, + hir_def::GenericParamId::TypeParamId(_) => BoundVarKind::Ty(BoundTyKind::Anon), + hir_def::GenericParamId::LifetimeParamId(_) => { + BoundVarKind::Region(BoundRegionKind::Anon) + } + }), + ), + ) +} + +/// Checks if the provided generic arg matches its expected kind, then lower them via +/// provided closures. Use unknown if there was kind mismatch. +/// +pub(crate) fn lower_generic_arg<'a, 'db, T>( + db: &'db dyn HirDatabase, + kind_id: GenericParamId, + arg: &'a GenericArg, + this: &mut T, + store: &ExpressionStore, + for_type: impl FnOnce(&mut T, TypeRefId) -> Ty<'db> + 'a, + for_const: impl FnOnce(&mut T, &ConstRef, Ty<'db>) -> Const<'db> + 'a, + for_const_ty_path_fallback: impl FnOnce(&mut T, &Path, Ty<'db>) -> Const<'db> + 'a, + for_lifetime: impl FnOnce(&mut T, &LifetimeRefId) -> Region<'db> + 'a, +) -> crate::next_solver::GenericArg<'db> { + let interner = DbInterner::new_with(db, None, None); + let kind = match kind_id { + GenericParamId::TypeParamId(_) => ParamKind::Type, + GenericParamId::ConstParamId(id) => { + let ty = db.const_param_ty(id); + ParamKind::Const(ty) + } + GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, + }; + match (arg, kind) { + (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, *type_ref).into(), + (GenericArg::Const(c), ParamKind::Const(c_ty)) => { + for_const(this, c, c_ty.to_nextsolver(interner)).into() + } + (GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => { + for_lifetime(this, lifetime_ref).into() + } + (GenericArg::Const(_), ParamKind::Type) => Ty::new_error(interner, ErrorGuaranteed).into(), + (GenericArg::Lifetime(_), ParamKind::Type) => { + Ty::new_error(interner, ErrorGuaranteed).into() + } + (GenericArg::Type(t), ParamKind::Const(c_ty)) => match &store[*t] { + TypeRef::Path(p) => { + for_const_ty_path_fallback(this, p, c_ty.to_nextsolver(interner)).into() + } + _ => unknown_const_as_generic(c_ty.to_nextsolver(interner)), + }, + (GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => { + unknown_const(c_ty.to_nextsolver(interner)).into() + } + (GenericArg::Type(_), ParamKind::Lifetime) => Region::error(interner).into(), + (GenericArg::Const(_), ParamKind::Lifetime) => Region::error(interner).into(), + } +} + +/// Build the signature of a callable item (function, struct or enum variant). +pub(crate) fn callable_item_signature_query<'db>( + db: &'db dyn HirDatabase, + def: CallableDefId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + 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), + } +} + +fn fn_sig_for_fn<'db>( + db: &'db dyn HirDatabase, + def: FunctionId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let data = db.function_signature(def); + let resolver = def.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx_params = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::for_fn_params(&data), + ); + let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); + + 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, &[]), + }; + + let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); + // If/when we track late bound vars, we need to switch this to not be `dummy` + EarlyBinder::bind(rustc_type_ir::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 }, + inputs_and_output, + })) +} + +fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + let args = GenericArgs::identity_for_item(interner, adt.into()); + let ty = Ty::new_adt(interner, AdtDef::new(adt, interner), args); + EarlyBinder::bind(ty) +} + +fn fn_sig_for_struct_constructor<'db>( + db: &'db dyn HirDatabase, + def: StructId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let field_tys = db.field_types_ns(def.into()); + let params = field_tys.iter().map(|(_, ty)| ty.skip_binder()); + let ret = type_for_adt(db, def.into()).skip_binder(); + + let inputs_and_output = + Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret))); + EarlyBinder::bind(Binder::dummy(FnSig { + abi: FnAbi::RustCall, + c_variadic: false, + safety: Safety::Safe, + inputs_and_output, + })) +} + +fn fn_sig_for_enum_variant_constructor<'db>( + db: &'db dyn HirDatabase, + def: EnumVariantId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let field_tys = db.field_types_ns(def.into()); + let params = field_tys.iter().map(|(_, ty)| ty.skip_binder()); + let parent = def.lookup(db).parent; + let ret = type_for_adt(db, parent.into()).skip_binder(); + + let inputs_and_output = + Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret))); + EarlyBinder::bind(Binder::dummy(FnSig { + abi: FnAbi::RustCall, + c_variadic: false, + safety: Safety::Safe, + inputs_and_output, + })) +} + +pub(crate) fn associated_type_by_name_including_super_traits<'db>( + db: &'db dyn HirDatabase, + trait_ref: TraitRef<'db>, + name: &Name, +) -> Option<(TraitRef<'db>, TypeAliasId)> { + let interner = DbInterner::new_with(db, None, None); + rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| { + let trait_id = match t.as_ref().skip_binder().def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; + Some((t.skip_binder(), assoc_type)) + }) +}
diff --git a/crates/hir-ty/src/lower_nextsolver/path.rs b/crates/hir-ty/src/lower_nextsolver/path.rs new file mode 100644 index 0000000..df67b2c --- /dev/null +++ b/crates/hir-ty/src/lower_nextsolver/path.rs
@@ -0,0 +1,1454 @@ +//! A wrapper around [`TyLoweringContext`] specifically for lowering paths. + +use std::ops::Deref; + +use either::Either; +use hir_def::{ + AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId, + builtin_type::BuiltinType, + expr_store::{ + ExpressionStore, HygieneId, + path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments}, + }, + hir::generics::{ + GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, + }, + resolver::{ResolveValueResult, TypeNs, ValueNs}, + signatures::TraitFlags, + type_ref::{TypeRef, TypeRefId}, +}; +use intern::sym; +use rustc_hash::FxHashSet; +use rustc_type_ir::{ + AliasTerm, AliasTy, AliasTyKind, TypeVisitableExt, + inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, +}; +use smallvec::{SmallVec, smallvec}; +use stdx::never; + +use crate::{ + GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource, + PathLoweringDiagnostic, TyDefId, ValueTyDefId, + consteval_nextsolver::{unknown_const, unknown_const_as_generic}, + db::HirDatabase, + generics::{Generics, generics}, + lower::PathDiagnosticCallbackData, + lower_nextsolver::{LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by}, + next_solver::{ + AdtDef, Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate, + Region, SolverDefId, TraitRef, Ty, + mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, + }, + primitive, +}; + +use super::{ + ImplTraitLoweringMode, TyLoweringContext, associated_type_by_name_including_super_traits, + const_param_ty_query, ty_query, +}; + +type CallbackData<'a> = + Either<PathDiagnosticCallbackData, crate::infer::diagnostics::PathDiagnosticCallbackData<'a>>; + +// We cannot use `&mut dyn FnMut()` because of lifetime issues, and we don't want to use `Box<dyn FnMut()>` +// because of the allocation, so we create a lifetime-less callback, tailored for our needs. +pub(crate) struct PathDiagnosticCallback<'a, 'db> { + pub(crate) data: CallbackData<'a>, + pub(crate) callback: + fn(&CallbackData<'_>, &mut TyLoweringContext<'db, '_>, PathLoweringDiagnostic), +} + +pub(crate) struct PathLoweringContext<'a, 'b, 'db> { + ctx: &'a mut TyLoweringContext<'db, 'b>, + on_diagnostic: PathDiagnosticCallback<'a, 'db>, + path: &'a Path, + segments: PathSegments<'a>, + current_segment_idx: usize, + /// Contains the previous segment if `current_segment_idx == segments.len()` + current_or_prev_segment: PathSegment<'a>, +} + +impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { + #[inline] + pub(crate) fn new( + ctx: &'a mut TyLoweringContext<'db, 'b>, + on_diagnostic: PathDiagnosticCallback<'a, 'db>, + path: &'a Path, + ) -> Self { + let segments = path.segments(); + let first_segment = segments.first().unwrap_or(PathSegment::MISSING); + Self { + ctx, + on_diagnostic, + path, + segments, + current_segment_idx: 0, + current_or_prev_segment: first_segment, + } + } + + #[inline] + #[cold] + fn on_diagnostic(&mut self, diag: PathLoweringDiagnostic) { + (self.on_diagnostic.callback)(&self.on_diagnostic.data, self.ctx, diag); + } + + #[inline] + pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'db, 'b> { + self.ctx + } + + #[inline] + fn current_segment_u32(&self) -> u32 { + self.current_segment_idx as u32 + } + + #[inline] + fn skip_resolved_segment(&mut self) { + if !matches!(self.path, Path::LangItem(..)) { + // In lang items, the resolved "segment" is not one of the segments. Perhaps we should've put it + // point at -1, but I don't feel this is clearer. + self.current_segment_idx += 1; + } + self.update_current_segment(); + } + + #[inline] + fn update_current_segment(&mut self) { + self.current_or_prev_segment = + self.segments.get(self.current_segment_idx).unwrap_or(self.current_or_prev_segment); + } + + #[inline] + pub(crate) fn ignore_last_segment(&mut self) { + self.segments = self.segments.strip_last(); + } + + #[inline] + pub(crate) fn set_current_segment(&mut self, segment: usize) { + self.current_segment_idx = segment; + self.current_or_prev_segment = self + .segments + .get(segment) + .expect("invalid segment passed to PathLoweringContext::set_current_segment()"); + } + + #[inline] + fn with_lifetime_elision<T>( + &mut self, + lifetime_elision: LifetimeElisionKind<'db>, + f: impl FnOnce(&mut PathLoweringContext<'_, '_, 'db>) -> T, + ) -> T { + let old_lifetime_elision = + std::mem::replace(&mut self.ctx.lifetime_elision, lifetime_elision); + let result = f(self); + self.ctx.lifetime_elision = old_lifetime_elision; + result + } + + pub(crate) fn lower_ty_relative_path( + &mut self, + ty: Ty<'db>, + // We need the original resolution to lower `Self::AssocTy` correctly + res: Option<TypeNs>, + ) -> (Ty<'db>, Option<TypeNs>) { + let remaining_segments = self.segments.len() - self.current_segment_idx; + match remaining_segments { + 0 => (ty, res), + 1 => { + // resolve unselected assoc types + (self.select_associated_type(res), None) + } + _ => { + // FIXME report error (ambiguous associated type) + (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None) + } + } + } + + fn prohibit_parenthesized_generic_args(&mut self) -> bool { + if let Some(generic_args) = self.current_or_prev_segment.args_and_bindings { + match generic_args.parenthesized { + GenericArgsParentheses::No => {} + GenericArgsParentheses::ReturnTypeNotation | GenericArgsParentheses::ParenSugar => { + let segment = self.current_segment_u32(); + self.on_diagnostic( + PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, + ); + return true; + } + } + } + false + } + + // When calling this, the current segment is the resolved segment (we don't advance it yet). + pub(crate) fn lower_partly_resolved_path( + &mut self, + resolution: TypeNs, + infer_args: bool, + ) -> (Ty<'db>, Option<TypeNs>) { + let remaining_segments = self.segments.skip(self.current_segment_idx + 1); + tracing::debug!(?remaining_segments); + let rem_seg_len = remaining_segments.len(); + tracing::debug!(?rem_seg_len); + + let ty = match resolution { + TypeNs::TraitId(trait_) => { + let ty = match remaining_segments.len() { + 1 => { + let trait_ref = self.lower_trait_ref_from_resolved_path( + trait_, + Ty::new_error(self.ctx.interner, ErrorGuaranteed), + ); + tracing::debug!(?trait_ref); + self.skip_resolved_segment(); + let segment = self.current_or_prev_segment; + let trait_id = match trait_ref.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let found = + trait_id.trait_items(self.ctx.db).associated_type_by_name(segment.name); + + tracing::debug!(?found); + match found { + Some(associated_ty) => { + // 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 (`trait_ref.substitution`). + let substitution = self.substs_from_path_segment( + associated_ty.into(), + false, + None, + true, + ); + let args = crate::next_solver::GenericArgs::new_from_iter( + self.ctx.interner, + trait_ref + .args + .iter() + .chain(substitution.iter().skip(trait_ref.args.len())), + ); + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args( + self.ctx.interner, + associated_ty.into(), + args, + ), + ) + } + None => { + // FIXME: report error (associated type not found) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) + } + } + } + 0 => { + // 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) + } + _ => { + // FIXME report error (ambiguous associated type) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) + } + }; + return (ty, None); + } + 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()); + let p = match param { + GenericParamDataRef::TypeParamData(p) => p, + _ => unreachable!(), + }; + Ty::new_param( + self.ctx.interner, + idx as u32, + p.name + .as_ref() + .map_or_else(|| sym::MISSING_NAME.clone(), |p| p.symbol().clone()), + ) + } + } + } + TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty_ns(impl_id).skip_binder(), + TypeNs::AdtSelfType(adt) => { + let args = crate::next_solver::GenericArgs::identity_for_item( + self.ctx.interner, + adt.into(), + ); + Ty::new_adt(self.ctx.interner, AdtDef::new(adt, self.ctx.interner), 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), + // FIXME: report error + TypeNs::EnumVariantId(_) | TypeNs::ModuleId(_) => { + return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); + } + }; + + tracing::debug!(?ty); + + self.skip_resolved_segment(); + self.lower_ty_relative_path(ty, Some(resolution)) + } + + fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) { + let mut prohibit_generics_on_resolved = |reason| { + if self.current_or_prev_segment.args_and_bindings.is_some() { + let segment = self.current_segment_u32(); + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment, + reason, + }); + } + }; + + match resolution { + TypeNs::SelfType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + TypeNs::GenericParam(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam) + } + TypeNs::AdtSelfType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + TypeNs::BuiltinType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) + } + TypeNs::ModuleId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Module) + } + TypeNs::AdtId(_) + | TypeNs::EnumVariantId(_) + | TypeNs::TypeAliasId(_) + | TypeNs::TraitId(_) => {} + } + } + + pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option<TypeNs> { + let (res, unresolved) = self.resolve_path_in_type_ns()?; + if unresolved.is_some() { + return None; + } + Some(res) + } + + #[tracing::instrument(skip(self), ret)] + pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option<usize>)> { + let (resolution, remaining_index, _, prefix_info) = + self.ctx.resolver.resolve_path_in_type_ns_with_prefix_info(self.ctx.db, self.path)?; + + let segments = self.segments; + if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { + // `segments.is_empty()` can occur with `self`. + return Some((resolution, remaining_index)); + } + + let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index { + None if prefix_info.enum_variant => { + (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2)) + } + None => (segments.strip_last(), segments.len() - 1, None), + Some(i) => (segments.take(i - 1), i - 1, None), + }; + + self.current_segment_idx = resolved_segment_idx; + self.current_or_prev_segment = + segments.get(resolved_segment_idx).expect("should have resolved segment"); + + if matches!(self.path, Path::BarePath(..)) { + // Bare paths cannot have generics, so skip them as an optimization. + return Some((resolution, remaining_index)); + } + + for (i, mod_segment) in module_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }); + } + } + + if let Some(enum_segment) = enum_segment + && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }); + } + + self.handle_type_ns_resolution(&resolution); + + Some((resolution, remaining_index)) + } + + pub(crate) fn resolve_path_in_value_ns( + &mut self, + hygiene_id: HygieneId, + ) -> Option<ResolveValueResult> { + let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( + self.ctx.db, + self.path, + hygiene_id, + )?; + + let segments = self.segments; + if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { + // `segments.is_empty()` can occur with `self`. + return Some(res); + } + + let (mod_segments, enum_segment, resolved_segment_idx) = match res { + ResolveValueResult::Partial(_, unresolved_segment, _) => { + (segments.take(unresolved_segment - 1), None, unresolved_segment - 1) + } + ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _) + if prefix_info.enum_variant => + { + (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1) + } + ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1), + }; + + self.current_segment_idx = resolved_segment_idx; + self.current_or_prev_segment = + segments.get(resolved_segment_idx).expect("should have resolved segment"); + + for (i, mod_segment) in mod_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }); + } + } + + if let Some(enum_segment) = enum_segment + && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }); + } + + match &res { + ResolveValueResult::ValueNs(resolution, _) => { + let resolved_segment_idx = self.current_segment_u32(); + let resolved_segment = self.current_or_prev_segment; + + let mut prohibit_generics_on_resolved = |reason| { + if resolved_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: resolved_segment_idx, + reason, + }); + } + }; + + match resolution { + ValueNs::ImplSelf(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not + // E0109 (generic arguments provided for a type that doesn't accept them) for + // consts and statics, presumably as a defense against future in which consts + // and statics can be generic, or just because it was easier for rustc implementors. + // That means we'll show the wrong error code. Because of us it's easier to do it + // this way :) + ValueNs::GenericParam(_) | ValueNs::ConstId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) + } + ValueNs::StaticId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) + } + ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {} + ValueNs::LocalBinding(_) => {} + } + } + ResolveValueResult::Partial(resolution, _, _) => { + self.handle_type_ns_resolution(resolution); + } + }; + Some(res) + } + + #[tracing::instrument(skip(self), ret)] + fn select_associated_type(&mut self, res: Option<TypeNs>) -> Ty<'db> { + let interner = self.ctx.interner; + let Some(res) = res else { + return Ty::new_error(self.ctx.interner, ErrorGuaranteed); + }; + let segment = self.current_or_prev_segment; + let assoc_name = segment.name; + let db = self.ctx.db; + let def = self.ctx.def; + let mut search = |t: TraitRef<'db>| { + let trait_id = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let mut checked_traits = FxHashSet::default(); + let mut check_trait = |trait_id: TraitId| { + let name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_id, ?name); + if !checked_traits.insert(trait_id) { + return None; + } + let data = trait_id.trait_items(db); + + tracing::debug!(?data.items); + for (name, assoc_id) in &data.items { + if let &AssocItemId::TypeAliasId(alias) = assoc_id { + if name != assoc_name { + continue; + } + + // 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(alias.into(), false, None, true); + + let substs = crate::next_solver::GenericArgs::new_from_iter( + interner, + t.args.iter().chain(substs.iter().skip(t.args.len())), + ); + + return Some(Ty::new_alias( + interner, + AliasTyKind::Projection, + AliasTy::new(interner, alias.into(), substs), + )); + } + } + None + }; + let mut stack: SmallVec<[_; 4]> = smallvec![trait_id]; + while let Some(trait_def_id) = stack.pop() { + if let Some(alias) = check_trait(trait_def_id) { + return alias; + } + for pred in generic_predicates_filtered_by( + db, + GenericDefId::TraitId(trait_def_id), + PredicateFilter::SelfTrait, + |pred| pred == GenericDefId::TraitId(trait_def_id), + ) + .0 + .deref() + { + tracing::debug!(?pred); + let trait_id = match pred.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(), + _ => continue, + }; + let trait_id = match trait_id { + SolverDefId::TraitId(trait_id) => trait_id, + _ => continue, + }; + stack.push(trait_id); + } + tracing::debug!(?stack); + } + + Ty::new_error(interner, ErrorGuaranteed) + }; + + match res { + TypeNs::SelfType(impl_id) => { + let trait_ref = db.impl_trait_ns(impl_id); + let Some(trait_ref) = trait_ref else { + return Ty::new_error(interner, ErrorGuaranteed); + }; + + // we're _in_ the impl -- the binders get added back later. Correct, + // but it would be nice to make this more explicit + search(trait_ref.skip_binder()) + } + TypeNs::GenericParam(param_id) => { + // Handle `Self::Type` referring to own associated type in trait definitions + // This *must* be done first to avoid cycles with + // `generic_predicates_for_param`, but not sure that it's sufficient, + // see FIXME in `search`. + if let GenericDefId::TraitId(trait_id) = param_id.parent() { + let trait_name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_name); + let trait_generics = generics(db, trait_id.into()); + tracing::debug!(?trait_generics); + if trait_generics[param_id.local_id()].is_trait_self() { + let args = crate::next_solver::GenericArgs::identity_for_item( + interner, + trait_id.into(), + ); + let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); + tracing::debug!(?args, ?trait_ref); + return search(trait_ref); + } + } + + let predicates = db.generic_predicates_for_param_ns( + def, + param_id.into(), + Some(segment.name.clone()), + ); + predicates + .iter() + .find_map(|pred| match (*pred).kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate), + _ => None, + }) + .map(|trait_predicate| { + let trait_ref = trait_predicate.trait_ref; + assert!( + !trait_ref.has_escaping_bound_vars(), + "FIXME unexpected higher-ranked trait bound" + ); + search(trait_ref) + }) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) + } + _ => Ty::new_error(interner, ErrorGuaranteed), + } + } + + fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { + let generic_def = match typeable { + TyDefId::BuiltinType(builtinty) => return builtin(self.ctx.interner, builtinty), + TyDefId::AdtId(it) => it.into(), + TyDefId::TypeAliasId(it) => it.into(), + }; + let args = self.substs_from_path_segment(generic_def, infer_args, None, false); + let ty = ty_query(self.ctx.db, typeable); + ty.instantiate(self.ctx.interner, args) + } + + /// Collect generic arguments from a path into a `Substs`. See also + /// `create_substs_for_ast_path` and `def_to_ty` in rustc. + pub(crate) fn substs_from_path( + &mut self, + // Note that we don't call `db.value_type(resolved)` here, + // `ValueTyDefId` is just a convenient way to pass generics and + // special-case enum variants + resolved: ValueTyDefId, + infer_args: bool, + lowering_assoc_type_generics: bool, + ) -> crate::next_solver::GenericArgs<'db> { + let interner = self.ctx.interner; + let prev_current_segment_idx = self.current_segment_idx; + let prev_current_segment = self.current_or_prev_segment; + + let generic_def = match resolved { + ValueTyDefId::FunctionId(it) => it.into(), + ValueTyDefId::StructId(it) => it.into(), + ValueTyDefId::UnionId(it) => it.into(), + ValueTyDefId::ConstId(it) => it.into(), + ValueTyDefId::StaticId(_) => { + return crate::next_solver::GenericArgs::new_from_iter(interner, []); + } + ValueTyDefId::EnumVariantId(var) => { + // the generic args for an enum variant may be either specified + // on the segment referring to the enum, or on the segment + // referring to the variant. So `Option::<T>::None` and + // `Option::None::<T>` are both allowed (though the former is + // FIXME: This isn't strictly correct, enum variants may be used not through the enum + // (via `use Enum::Variant`). The resolver returns whether they were, but we don't have its result + // available here. The worst that can happen is that we will show some confusing diagnostics to the user, + // if generics exist on the module and they don't match with the variant. + // preferred). See also `def_ids_for_path_segments` in rustc. + // + // `wrapping_sub(1)` will return a number which `get` will return None for if current_segment_idx<2. + // This simplifies the code a bit. + let penultimate_idx = self.current_segment_idx.wrapping_sub(1); + let penultimate = self.segments.get(penultimate_idx); + if let Some(penultimate) = penultimate + && self.current_or_prev_segment.args_and_bindings.is_none() + && penultimate.args_and_bindings.is_some() + { + self.current_segment_idx = penultimate_idx; + self.current_or_prev_segment = penultimate; + } + var.lookup(self.ctx.db).parent.into() + } + }; + let result = self.substs_from_path_segment( + generic_def, + infer_args, + None, + lowering_assoc_type_generics, + ); + self.current_segment_idx = prev_current_segment_idx; + self.current_or_prev_segment = prev_current_segment; + result + } + + pub(crate) fn substs_from_path_segment( + &mut self, + def: GenericDefId, + infer_args: bool, + explicit_self_ty: Option<Ty<'db>>, + lowering_assoc_type_generics: bool, + ) -> crate::next_solver::GenericArgs<'db> { + let mut lifetime_elision = self.ctx.lifetime_elision.clone(); + + if let Some(args) = self.current_or_prev_segment.args_and_bindings + && args.parenthesized != GenericArgsParentheses::No + { + let prohibit_parens = match def { + GenericDefId::TraitId(trait_) => { + // RTN is prohibited anyways if we got here. + let is_rtn = args.parenthesized == GenericArgsParentheses::ReturnTypeNotation; + let is_fn_trait = self + .ctx + .db + .trait_signature(trait_) + .flags + .contains(TraitFlags::RUSTC_PAREN_SUGAR); + is_rtn || !is_fn_trait + } + _ => true, + }; + + if prohibit_parens { + let segment = self.current_segment_u32(); + self.on_diagnostic( + PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, + ); + + return unknown_subst(self.ctx.interner, def); + } + + // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. + lifetime_elision = + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; + } + + self.substs_from_args_and_bindings( + self.current_or_prev_segment.args_and_bindings, + def, + infer_args, + explicit_self_ty, + PathGenericsSource::Segment(self.current_segment_u32()), + lowering_assoc_type_generics, + lifetime_elision, + ) + } + + pub(super) fn substs_from_args_and_bindings( + &mut self, + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + infer_args: bool, + explicit_self_ty: Option<Ty<'db>>, + generics_source: PathGenericsSource, + lowering_assoc_type_generics: bool, + lifetime_elision: LifetimeElisionKind<'db>, + ) -> crate::next_solver::GenericArgs<'db> { + struct LowererCtx<'a, 'b, 'c, 'db> { + ctx: &'a mut PathLoweringContext<'b, 'c, 'db>, + generics_source: PathGenericsSource, + } + + impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, '_, 'db> { + fn report_len_mismatch( + &mut self, + def: GenericDefId, + provided_count: u32, + expected_count: u32, + kind: IncorrectGenericsLenKind, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsLen { + generics_source: self.generics_source, + provided_count, + expected_count, + kind, + def, + }); + } + + fn report_arg_mismatch( + &mut self, + param_id: GenericParamId, + arg_idx: u32, + has_self_arg: bool, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsOrder { + generics_source: self.generics_source, + param_id, + arg_idx, + has_self_arg, + }); + } + + fn provided_kind( + &mut self, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + arg: &GenericArg, + ) -> crate::next_solver::GenericArg<'db> { + match (param, arg) { + (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => { + self.ctx.ctx.lower_lifetime(*lifetime).into() + } + (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => { + self.ctx.ctx.lower_ty(*type_ref).into() + } + (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => { + let GenericParamId::ConstParamId(const_id) = param_id else { + unreachable!("non-const param ID for const param"); + }; + self.ctx + .ctx + .lower_const(konst, const_param_ty_query(self.ctx.ctx.db, const_id)) + .into() + } + _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), + } + } + + fn provided_type_like_const( + &mut self, + const_ty: Ty<'db>, + arg: TypeLikeConst<'_>, + ) -> crate::next_solver::Const<'db> { + match arg { + TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty), + TypeLikeConst::Infer => unknown_const(const_ty), + } + } + + fn inferred_kind( + &mut self, + def: GenericDefId, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + infer_args: bool, + preceding_args: &[crate::next_solver::GenericArg<'db>], + ) -> crate::next_solver::GenericArg<'db> { + let default = || { + self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(|default| { + convert_binder_to_early_binder( + self.ctx.ctx.interner, + default.to_nextsolver(self.ctx.ctx.interner), + ) + .instantiate(self.ctx.ctx.interner, preceding_args) + }) + }; + match param { + GenericParamDataRef::LifetimeParamData(_) => { + Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) + .into() + } + GenericParamDataRef::TypeParamData(param) => { + if !infer_args + && param.default.is_some() + && let Some(default) = default() + { + return default; + } + Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() + } + GenericParamDataRef::ConstParamData(param) => { + if !infer_args + && param.default.is_some() + && let Some(default) = default() + { + return default; + } + let GenericParamId::ConstParamId(const_id) = 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)) + } + } + } + + fn parent_arg( + &mut self, + param_id: GenericParamId, + ) -> crate::next_solver::GenericArg<'db> { + match param_id { + 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() + } + } + } + + fn report_elided_lifetimes_in_path( + &mut self, + def: GenericDefId, + expected_count: u32, + hard_error: bool, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::ElidedLifetimesInPath { + generics_source: self.generics_source, + def, + expected_count, + hard_error, + }); + } + + fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::ElisionFailure { + generics_source: self.generics_source, + def, + expected_count, + }); + } + + fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::MissingLifetime { + generics_source: self.generics_source, + def, + expected_count, + }); + } + } + + substs_from_args_and_bindings( + self.ctx.db, + self.ctx.store, + args_and_bindings, + def, + infer_args, + lifetime_elision, + lowering_assoc_type_generics, + explicit_self_ty, + &mut LowererCtx { ctx: self, generics_source }, + ) + } + + pub(crate) fn lower_trait_ref_from_resolved_path( + &mut self, + resolved: TraitId, + explicit_self_ty: Ty<'db>, + ) -> TraitRef<'db> { + let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty); + TraitRef::new_from_args(self.ctx.interner, resolved.into(), args) + } + + fn trait_ref_substs_from_path( + &mut self, + resolved: TraitId, + explicit_self_ty: Ty<'db>, + ) -> crate::next_solver::GenericArgs<'db> { + self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty), false) + } + + pub(super) fn assoc_type_bindings_from_type_bound<'c>( + mut self, + trait_ref: TraitRef<'db>, + ) -> Option<impl Iterator<Item = Clause<'db>> + use<'a, 'b, 'c, 'db>> { + let interner = self.ctx.interner; + self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { + args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| { + let found = associated_type_by_name_including_super_traits( + self.ctx.db, + trait_ref, + &binding.name, + ); + let (super_trait_ref, associated_ty) = match found { + None => return SmallVec::new(), + Some(t) => t, + }; + let args = + self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| { + // 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 (`super_trait_ref.substitution`). + this.substs_from_args_and_bindings( + binding.args.as_ref(), + associated_ty.into(), + false, // this is not relevant + Some(super_trait_ref.self_ty()), + PathGenericsSource::AssocType { + segment: this.current_segment_u32(), + assoc_type: binding_idx as u32, + }, + false, + this.ctx.lifetime_elision.clone(), + ) + }); + let args = crate::next_solver::GenericArgs::new_from_iter( + interner, + super_trait_ref.args.iter().chain(args.iter().skip(super_trait_ref.args.len())), + ); + let projection_term = + AliasTerm::new_from_args(interner, associated_ty.into(), args); + let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity( + binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), + ); + if let Some(type_ref) = binding.type_ref { + match (&self.ctx.store[type_ref], self.ctx.impl_trait_mode.mode) { + (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), + (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => { + let ty = self.ctx.lower_ty(type_ref); + let pred = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Projection(ProjectionPredicate { + projection_term, + term: ty.into(), + }), + )), + )); + predicates.push(pred); + } + } + } + for bound in binding.bounds.iter() { + predicates.extend(self.ctx.lower_type_bound( + bound, + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args(self.ctx.interner, associated_ty.into(), args), + ), + false, + )); + } + predicates + }) + }) + } +} + +/// A const that were parsed like a type. +pub(crate) enum TypeLikeConst<'a> { + Infer, + Path(&'a Path), +} + +pub(crate) trait GenericArgsLowerer<'db> { + fn report_elided_lifetimes_in_path( + &mut self, + def: GenericDefId, + expected_count: u32, + hard_error: bool, + ); + + fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32); + + fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32); + + fn report_len_mismatch( + &mut self, + def: GenericDefId, + provided_count: u32, + expected_count: u32, + kind: IncorrectGenericsLenKind, + ); + + fn report_arg_mismatch(&mut self, param_id: GenericParamId, arg_idx: u32, has_self_arg: bool); + + fn provided_kind( + &mut self, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + arg: &GenericArg, + ) -> crate::next_solver::GenericArg<'db>; + + fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>) + -> Const<'db>; + + fn inferred_kind( + &mut self, + def: GenericDefId, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + infer_args: bool, + preceding_args: &[crate::next_solver::GenericArg<'db>], + ) -> crate::next_solver::GenericArg<'db>; + + fn parent_arg(&mut self, param_id: GenericParamId) -> crate::next_solver::GenericArg<'db>; +} + +/// Returns true if there was an error. +fn check_generic_args_len<'db>( + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + def_generics: &Generics, + infer_args: bool, + lifetime_elision: &LifetimeElisionKind<'db>, + lowering_assoc_type_generics: bool, + ctx: &mut impl GenericArgsLowerer<'db>, +) -> bool { + let mut had_error = false; + + let (mut provided_lifetimes_count, mut provided_types_and_consts_count) = (0usize, 0usize); + if let Some(args_and_bindings) = args_and_bindings { + let args_no_self = &args_and_bindings.args[usize::from(args_and_bindings.has_self_type)..]; + for arg in args_no_self { + match arg { + GenericArg::Lifetime(_) => provided_lifetimes_count += 1, + GenericArg::Type(_) | GenericArg::Const(_) => provided_types_and_consts_count += 1, + } + } + } + + let lifetime_args_len = def_generics.len_lifetimes_self(); + if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics { + // In generic associated types, we never allow inferring the lifetimes. + match lifetime_elision { + &LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => { + ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path); + had_error |= report_in_path; + } + LifetimeElisionKind::AnonymousReportError => { + ctx.report_missing_lifetime(def, lifetime_args_len as u32); + had_error = true + } + LifetimeElisionKind::ElisionFailure => { + ctx.report_elision_failure(def, lifetime_args_len as u32); + had_error = true; + } + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { + // FIXME: Check there are other lifetimes in scope, and error/lint. + } + LifetimeElisionKind::Elided(_) => { + ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, false); + } + LifetimeElisionKind::Infer => { + // Allow eliding lifetimes. + } + } + } else if lifetime_args_len != provided_lifetimes_count { + ctx.report_len_mismatch( + def, + provided_lifetimes_count as u32, + lifetime_args_len as u32, + IncorrectGenericsLenKind::Lifetimes, + ); + had_error = true; + } + + let defaults_count = + def_generics.iter_self_type_or_consts().filter(|(_, param)| param.has_default()).count(); + let named_type_and_const_params_count = def_generics + .iter_self_type_or_consts() + .filter(|(_, param)| match param { + TypeOrConstParamData::TypeParamData(param) => { + param.provenance == TypeParamProvenance::TypeParamList + } + TypeOrConstParamData::ConstParamData(_) => true, + }) + .count(); + let expected_max = named_type_and_const_params_count; + let expected_min = + if infer_args { 0 } else { named_type_and_const_params_count - defaults_count }; + if provided_types_and_consts_count < expected_min + || expected_max < provided_types_and_consts_count + { + ctx.report_len_mismatch( + def, + provided_types_and_consts_count as u32, + named_type_and_const_params_count as u32, + IncorrectGenericsLenKind::TypesAndConsts, + ); + had_error = true; + } + + had_error +} + +pub(crate) fn substs_from_args_and_bindings<'db>( + db: &'db dyn HirDatabase, + store: &ExpressionStore, + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + mut infer_args: bool, + lifetime_elision: LifetimeElisionKind<'db>, + lowering_assoc_type_generics: bool, + explicit_self_ty: Option<Ty<'db>>, + ctx: &mut impl GenericArgsLowerer<'db>, +) -> crate::next_solver::GenericArgs<'db> { + let interner = DbInterner::new_with(db, None, None); + + tracing::debug!(?args_and_bindings); + + // Order is + // - Parent parameters + // - Optional Self parameter + // - Lifetime parameters + // - Type or Const parameters + let def_generics = generics(db, def); + let args_slice = args_and_bindings.map(|it| &*it.args).unwrap_or_default(); + + // We do not allow inference if there are specified args, i.e. we do not allow partial inference. + let has_non_lifetime_args = + args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))); + infer_args &= !has_non_lifetime_args; + + let had_count_error = check_generic_args_len( + args_and_bindings, + def, + &def_generics, + infer_args, + &lifetime_elision, + lowering_assoc_type_generics, + ctx, + ); + + let mut substs = Vec::with_capacity(def_generics.len()); + + substs.extend(def_generics.iter_parent_id().map(|id| ctx.parent_arg(id))); + + let mut args = args_slice.iter().enumerate().peekable(); + let mut params = def_generics.iter_self().peekable(); + + // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. + // If we later encounter a lifetime, we know that the arguments were provided in the + // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be + // inferred, so we can use it for diagnostics later. + let mut force_infer_lt = None; + + let has_self_arg = args_and_bindings.is_some_and(|it| it.has_self_type); + // First, handle `Self` parameter. Consume it from the args if provided, otherwise from `explicit_self_ty`, + // and lastly infer it. + if let Some(&( + self_param_id, + self_param @ GenericParamDataRef::TypeParamData(TypeParamData { + provenance: TypeParamProvenance::TraitSelf, + .. + }), + )) = params.peek() + { + let self_ty = if has_self_arg { + let (_, self_ty) = args.next().expect("has_self_type=true, should have Self type"); + 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) + }) + }; + params.next(); + substs.push(self_ty); + } + + loop { + // We're going to iterate through the generic arguments that the user + // provided, matching them with the generic parameters we expect. + // Mismatches can occur as a result of elided lifetimes, or for malformed + // input. We try to handle both sensibly. + match (args.peek(), params.peek()) { + (Some(&(arg_idx, arg)), Some(&(param_id, param))) => match (arg, param) { + (GenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param)) + if type_param.provenance == TypeParamProvenance::ArgumentImplTrait => + { + // 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)); + params.next(); + } + (GenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_)) + | (GenericArg::Type(_), GenericParamDataRef::TypeParamData(_)) + | (GenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => { + substs.push(ctx.provided_kind(param_id, param, arg)); + args.next(); + params.next(); + } + ( + GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDataRef::LifetimeParamData(_), + ) => { + // 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)); + params.next(); + force_infer_lt = Some((arg_idx as u32, param_id)); + } + (GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => { + if let Some(konst) = type_looks_like_const(store, *type_ref) { + 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()); + args.next(); + params.next(); + } else { + // See the `_ => { ... }` branch. + if !had_count_error { + ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg); + } + while args.next().is_some() {} + } + } + _ => { + // We expected one kind of parameter, but the user provided + // another. This is an error. However, if we already know that + // the arguments don't match up with the parameters, we won't issue + // an additional error, as the user already knows what's wrong. + if !had_count_error { + ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg); + } + + // We've reported the error, but we want to make sure that this + // problem doesn't bubble down and create additional, irrelevant + // errors. In this case, we're simply going to ignore the argument + // and any following arguments. The rest of the parameters will be + // inferred. + while args.next().is_some() {} + } + }, + + (Some(&(_, arg)), None) => { + // We should never be able to reach this point with well-formed input. + // There are two situations in which we can encounter this issue. + // + // 1. The number of arguments is incorrect. In this case, an error + // will already have been emitted, and we can ignore it. + // 2. We've inferred some lifetimes, which have been provided later (i.e. + // after a type or const). We want to throw an error in this case. + if !had_count_error { + assert!( + matches!(arg, GenericArg::Lifetime(_)), + "the only possible situation here is incorrect lifetime order" + ); + let (provided_arg_idx, param_id) = + force_infer_lt.expect("lifetimes ought to have been inferred"); + ctx.report_arg_mismatch(param_id, provided_arg_idx, has_self_arg); + } + + break; + } + + (None, Some(&(param_id, param))) => { + // If there are fewer arguments than parameters, it means we're inferring the remaining arguments. + let param = if let GenericParamId::LifetimeParamId(_) = param_id { + match &lifetime_elision { + LifetimeElisionKind::ElisionFailure + | LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true } + | LifetimeElisionKind::AnonymousReportError => { + assert!(had_count_error); + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + } + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { + Region::new_static(interner).into() + } + LifetimeElisionKind::Elided(lifetime) => (*lifetime).into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false } + | 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) + } + } + } else { + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + }; + substs.push(param); + params.next(); + } + + (None, None) => break, + } + } + + crate::next_solver::GenericArgs::new_from_iter(interner, substs) +} + +fn type_looks_like_const( + store: &ExpressionStore, + type_ref: TypeRefId, +) -> Option<TypeLikeConst<'_>> { + // A path/`_` const will be parsed as a type, instead of a const, because when parsing/lowering + // in hir-def we don't yet know the expected argument kind. rustc does this a bit differently, + // when lowering to HIR it resolves the path, and if it doesn't resolve to the type namespace + // it is lowered as a const. Our behavior could deviate from rustc when the value is resolvable + // in both the type and value namespaces, but I believe we only allow more code. + let type_ref = &store[type_ref]; + match type_ref { + TypeRef::Path(path) => Some(TypeLikeConst::Path(path)), + TypeRef::Placeholder => Some(TypeLikeConst::Infer), + _ => None, + } +} + +fn unknown_subst<'db>( + interner: DbInterner<'db>, + def: impl Into<GenericDefId>, +) -> crate::next_solver::GenericArgs<'db> { + let params = generics(interner.db(), def.into()); + crate::next_solver::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(_) => { + crate::next_solver::Region::error(interner).into() + } + }), + ) +} + +pub(crate) fn builtin<'db>(interner: DbInterner<'db>, builtin: BuiltinType) -> Ty<'db> { + match builtin { + BuiltinType::Char => Ty::new(interner, rustc_type_ir::TyKind::Char), + BuiltinType::Bool => Ty::new_bool(interner), + BuiltinType::Str => Ty::new(interner, rustc_type_ir::TyKind::Str), + BuiltinType::Int(t) => { + let int_ty = match primitive::int_ty_from_builtin(t) { + chalk_ir::IntTy::Isize => rustc_type_ir::IntTy::Isize, + chalk_ir::IntTy::I8 => rustc_type_ir::IntTy::I8, + chalk_ir::IntTy::I16 => rustc_type_ir::IntTy::I16, + chalk_ir::IntTy::I32 => rustc_type_ir::IntTy::I32, + chalk_ir::IntTy::I64 => rustc_type_ir::IntTy::I64, + chalk_ir::IntTy::I128 => rustc_type_ir::IntTy::I128, + }; + Ty::new_int(interner, int_ty) + } + BuiltinType::Uint(t) => { + let uint_ty = match primitive::uint_ty_from_builtin(t) { + chalk_ir::UintTy::Usize => rustc_type_ir::UintTy::Usize, + chalk_ir::UintTy::U8 => rustc_type_ir::UintTy::U8, + chalk_ir::UintTy::U16 => rustc_type_ir::UintTy::U16, + chalk_ir::UintTy::U32 => rustc_type_ir::UintTy::U32, + chalk_ir::UintTy::U64 => rustc_type_ir::UintTy::U64, + chalk_ir::UintTy::U128 => rustc_type_ir::UintTy::U128, + }; + Ty::new_uint(interner, uint_ty) + } + BuiltinType::Float(t) => { + let float_ty = match primitive::float_ty_from_builtin(t) { + chalk_ir::FloatTy::F16 => rustc_type_ir::FloatTy::F16, + chalk_ir::FloatTy::F32 => rustc_type_ir::FloatTy::F32, + chalk_ir::FloatTy::F64 => rustc_type_ir::FloatTy::F64, + chalk_ir::FloatTy::F128 => rustc_type_ir::FloatTy::F128, + }; + Ty::new_float(interner, float_ty) + } + } +}
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index a6150a9..4943815 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs
@@ -16,22 +16,24 @@ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_type_ir::inherent::{IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; use crate::{ AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, - Goal, Guidance, InEnvironment, Interner, Mutability, Scalar, Solution, Substitution, - TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, - VariableKind, WhereClause, + Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, + TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause, autoderef::{self, AutoderefKind}, db::HirDatabase, error_lifetime, from_chalk_trait_id, from_foreign_def_id, infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable}, lang_items::is_box, + next_solver::SolverDefId, primitive::{FloatTy, IntTy, UintTy}, to_chalk_trait_id, + traits::NextTraitSolveResult, utils::all_super_traits, }; @@ -43,6 +45,7 @@ Slice, Array, Never, + Ref(Mutability), RawPtr(Mutability), Scalar(Scalar), // These can have user-defined impls: @@ -88,7 +91,7 @@ TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?, - TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty), + TyKind::Ref(mutability, _, _) => TyFingerprint::Ref(*mutability), TyKind::Tuple(_, subst) => { let first_ty = subst.interned().first().map(|arg| arg.assert_ty_ref(Interner)); match first_ty { @@ -113,6 +116,94 @@ }; Some(fp) } + + /// Creates a TyFingerprint for looking up a trait impl. + pub fn for_trait_impl_ns<'db>(ty: &crate::next_solver::Ty<'db>) -> Option<TyFingerprint> { + use rustc_type_ir::TyKind; + let fp = match (*ty).kind() { + TyKind::Str => TyFingerprint::Str, + TyKind::Never => TyFingerprint::Never, + TyKind::Slice(..) => TyFingerprint::Slice, + TyKind::Array(..) => TyFingerprint::Array, + TyKind::Int(int) => TyFingerprint::Scalar(Scalar::Int(match int { + rustc_type_ir::IntTy::Isize => IntTy::Isize, + rustc_type_ir::IntTy::I8 => IntTy::I8, + rustc_type_ir::IntTy::I16 => IntTy::I16, + rustc_type_ir::IntTy::I32 => IntTy::I32, + rustc_type_ir::IntTy::I64 => IntTy::I64, + rustc_type_ir::IntTy::I128 => IntTy::I128, + })), + TyKind::Uint(uint) => TyFingerprint::Scalar(Scalar::Uint(match uint { + rustc_type_ir::UintTy::Usize => UintTy::Usize, + rustc_type_ir::UintTy::U8 => UintTy::U8, + rustc_type_ir::UintTy::U16 => UintTy::U16, + rustc_type_ir::UintTy::U32 => UintTy::U32, + rustc_type_ir::UintTy::U64 => UintTy::U64, + rustc_type_ir::UintTy::U128 => UintTy::U128, + })), + TyKind::Float(float) => TyFingerprint::Scalar(Scalar::Float(match float { + rustc_type_ir::FloatTy::F16 => FloatTy::F16, + rustc_type_ir::FloatTy::F32 => FloatTy::F32, + rustc_type_ir::FloatTy::F64 => FloatTy::F64, + rustc_type_ir::FloatTy::F128 => FloatTy::F128, + })), + TyKind::Bool => TyFingerprint::Scalar(Scalar::Bool), + TyKind::Char => TyFingerprint::Scalar(Scalar::Char), + TyKind::Adt(def, _) => TyFingerprint::Adt(def.inner().id), + TyKind::RawPtr(.., mutability) => match mutability { + rustc_ast_ir::Mutability::Mut => TyFingerprint::RawPtr(Mutability::Mut), + rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not), + }, + TyKind::Foreign(def) => { + let SolverDefId::ForeignId(def) = def else { unreachable!() }; + TyFingerprint::ForeignType(crate::to_foreign_def_id(def)) + } + TyKind::Dynamic(bounds, _, _) => { + let trait_ref = bounds + .as_slice() + .iter() + .map(|b| (*b).skip_binder()) + .filter_map(|b| match b { + rustc_type_ir::ExistentialPredicate::Trait(t) => Some(t.def_id), + _ => None, + }) + .next()?; + let trait_id = match trait_ref { + SolverDefId::TraitId(id) => id, + _ => panic!("Bad GenericDefId in trait ref"), + }; + TyFingerprint::Dyn(trait_id) + } + TyKind::Ref(_, _, mutability) => match mutability { + rustc_ast_ir::Mutability::Mut => TyFingerprint::Ref(Mutability::Mut), + rustc_ast_ir::Mutability::Not => TyFingerprint::Ref(Mutability::Not), + }, + TyKind::Tuple(tys) => { + let first_ty = tys.as_slice().iter().next(); + match first_ty { + Some(ty) => return TyFingerprint::for_trait_impl_ns(ty), + None => TyFingerprint::Unit, + } + } + TyKind::FnDef(_, _) + | TyKind::Closure(_, _) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Pat(..) + | TyKind::CoroutineClosure(..) => TyFingerprint::Unnameable, + TyKind::FnPtr(sig, _) => { + TyFingerprint::Function(sig.inputs().skip_binder().len() as u32) + } + TyKind::Alias(..) + | TyKind::Placeholder(_) + | TyKind::Bound(..) + | TyKind::Infer(_) + | TyKind::Error(_) + | TyKind::Param(..) + | TyKind::UnsafeBinder(..) => return None, + }; + Some(fp) + } } pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [ @@ -581,15 +672,15 @@ } if self.unsize_array { ty = 'it: { - if let TyKind::Ref(m, l, inner) = ty.kind(Interner) { - if let TyKind::Array(inner, _) = inner.kind(Interner) { - break 'it TyKind::Ref( - *m, - l.clone(), - TyKind::Slice(inner.clone()).intern(Interner), - ) - .intern(Interner); - } + if let TyKind::Ref(m, l, inner) = ty.kind(Interner) + && let TyKind::Array(inner, _) = inner.kind(Interner) + { + break 'it TyKind::Ref( + *m, + l.clone(), + TyKind::Slice(inner.clone()).intern(Interner), + ) + .intern(Interner); } // FIXME: report diagnostic if array unsizing happens without indirection. ty @@ -807,10 +898,13 @@ let wcs = crate::chalk_db::convert_where_clauses(db, impl_.into(), &impl_substs) .into_iter() - .map(|b| b.cast(Interner)); - let goal = crate::Goal::all(Interner, wcs); - table.try_obligation(goal.clone())?; - table.register_obligation(goal); + .map(|b| -> Goal { b.cast(Interner) }); + for goal in wcs { + if table.try_obligation(goal.clone()).no_solution() { + return None; + } + table.register_obligation(goal); + } Some((impl_.impl_items(db), table.resolve_completely(impl_substs))) }) }) @@ -1313,7 +1407,7 @@ }; if !known_implemented { let goal = generic_implements_goal(db, &table.trait_env, t, &canonical_self_ty); - if db.trait_solve(krate, block, goal.cast(Interner)).is_none() { + if db.trait_solve(krate, block, goal.cast(Interner)).no_solution() { continue 'traits; } } @@ -1496,9 +1590,9 @@ let deref_chain = autoderef_method_receiver(&mut table, ty); for (ty, adj) in deref_chain { let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ty); - if db + if !db .trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner)) - .is_some() + .no_solution() { return Some(adj); } @@ -1549,11 +1643,11 @@ check_that!(receiver_ty.is_none()); check_that!(name.is_none_or(|n| n == item_name)); - if let Some(from_module) = visible_from_module { - if !db.assoc_visibility(c.into()).is_visible_from(db, from_module) { - cov_mark::hit!(const_candidate_not_visible); - return IsValidCandidate::NotVisible; - } + if let Some(from_module) = visible_from_module + && !db.assoc_visibility(c.into()).is_visible_from(db, from_module) + { + cov_mark::hit!(const_candidate_not_visible); + return IsValidCandidate::NotVisible; } let self_ty_matches = table.run_in_snapshot(|table| { let expected_self_ty = @@ -1638,11 +1732,11 @@ let db = table.db; let data = db.function_signature(fn_id); - if let Some(from_module) = visible_from_module { - if !db.assoc_visibility(fn_id.into()).is_visible_from(db, from_module) { - cov_mark::hit!(autoderef_candidate_not_visible); - return IsValidCandidate::NotVisible; - } + if let Some(from_module) = visible_from_module + && !db.assoc_visibility(fn_id.into()).is_visible_from(db, from_module) + { + cov_mark::hit!(autoderef_candidate_not_visible); + return IsValidCandidate::NotVisible; } table.run_in_snapshot(|table| { let _p = tracing::info_span!("subst_for_def").entered(); @@ -1692,7 +1786,7 @@ ); match solution { - Some(Solution::Unique(canonical_subst)) => { + NextTraitSolveResult::Certain(canonical_subst) => { canonicalized.apply_solution( table, Canonical { @@ -1701,16 +1795,13 @@ }, ); } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(table, substs); - } - Some(_) => (), - None => return IsValidCandidate::No, + NextTraitSolveResult::Uncertain(..) => {} + NextTraitSolveResult::NoSolution => return IsValidCandidate::No, } } for goal in goals { - if table.try_obligation(goal).is_none() { + if table.try_obligation(goal).no_solution() { return IsValidCandidate::No; } } @@ -1726,9 +1817,7 @@ trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env, trait_, ty); - let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); - - solution.is_some() + !db.trait_solve(env.krate, env.block, goal.cast(Interner)).no_solution() } pub fn implements_trait_unique( @@ -1738,9 +1827,7 @@ trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env, trait_, ty); - let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); - - matches!(solution, Some(crate::Solution::Unique(_))) + db.trait_solve(env.krate, env.block, goal.cast(Interner)).certain() } /// This creates Substs for a trait with the given Self type and type variables
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index fb0c0de..52df851 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -559,10 +559,9 @@ }, p, ) = value + && place_case(db, body, p) != ProjectionCase::Indirect { - if place_case(db, body, p) != ProjectionCase::Indirect { - push_mut_span(p.local, statement.span, &mut result); - } + push_mut_span(p.local, statement.span, &mut result); } } StatementKind::FakeRead(p) => {
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 9a97bd6..dfb8ae7 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs
@@ -1082,18 +1082,18 @@ let stack_size = { let mut stack_ptr = self.stack.len(); for (id, it) in body.locals.iter() { - if id == return_slot() { - if let Some(destination) = destination { - locals.ptr.insert(id, destination); - continue; - } + if id == return_slot() + && let Some(destination) = destination + { + locals.ptr.insert(id, destination); + continue; } let (size, align) = self.size_align_of_sized( &it.ty, &locals, "no unsized local in extending stack", )?; - while stack_ptr % align != 0 { + while !stack_ptr.is_multiple_of(align) { stack_ptr += 1; } let my_ptr = stack_ptr; @@ -1673,14 +1673,14 @@ if let Some(it) = goal(kind) { return Ok(it); } - if let TyKind::Adt(id, subst) = kind { - if let AdtId::StructId(struct_id) = id.0 { - let field_types = self.db.field_types(struct_id.into()); - if let Some(ty) = - field_types.iter().last().map(|it| it.1.clone().substitute(Interner, subst)) - { - return self.coerce_unsized_look_through_fields(&ty, goal); - } + if let TyKind::Adt(id, subst) = kind + && let AdtId::StructId(struct_id) = id.0 + { + let field_types = self.db.field_types(struct_id.into()); + if let Some(ty) = + field_types.iter().last().map(|it| it.1.clone().substitute(Interner, subst)) + { + return self.coerce_unsized_look_through_fields(&ty, goal); } } Err(MirEvalError::CoerceUnsizedError(ty.clone())) @@ -1778,17 +1778,15 @@ locals: &Locals, ) -> Result<(usize, Arc<Layout>, Option<(usize, usize, i128)>)> { let adt = it.adt_id(self.db); - if let DefWithBodyId::VariantId(f) = locals.body.owner { - if let VariantId::EnumVariantId(it) = it { - if let AdtId::EnumId(e) = adt { - if f.lookup(self.db).parent == e { - // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and - // infinite sized type errors) we use a dummy layout - let i = self.const_eval_discriminant(it)?; - return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); - } - } - } + if let DefWithBodyId::VariantId(f) = locals.body.owner + && let VariantId::EnumVariantId(it) = it + && let AdtId::EnumId(e) = adt + && f.lookup(self.db).parent == e + { + // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and + // infinite sized type errors) we use a dummy layout + let i = self.const_eval_discriminant(it)?; + return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); } let layout = self.layout_adt(adt, subst)?; Ok(match &layout.variants { @@ -1909,10 +1907,10 @@ let name = const_id.name(self.db); MirEvalError::ConstEvalError(name, Box::new(e)) })?; - if let chalk_ir::ConstValue::Concrete(c) = &result_owner.data(Interner).value { - if let ConstScalar::Bytes(v, mm) = &c.interned { - break 'b (v, mm); - } + if let chalk_ir::ConstValue::Concrete(c) = &result_owner.data(Interner).value + && let ConstScalar::Bytes(v, mm) = &c.interned + { + break 'b (v, mm); } not_supported!("unevaluatable constant"); } @@ -2055,14 +2053,13 @@ .is_sized() .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize))); } - if let DefWithBodyId::VariantId(f) = locals.body.owner { - if let Some((AdtId::EnumId(e), _)) = ty.as_adt() { - if f.lookup(self.db).parent == e { - // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and - // infinite sized type errors) we use a dummy size - return Ok(Some((16, 16))); - } - } + if let DefWithBodyId::VariantId(f) = locals.body.owner + && let Some((AdtId::EnumId(e), _)) = ty.as_adt() + && f.lookup(self.db).parent == e + { + // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and + // infinite sized type errors) we use a dummy size + return Ok(Some((16, 16))); } let layout = self.layout(ty); if self.assert_placeholder_ty_is_unused @@ -2103,7 +2100,7 @@ if !align.is_power_of_two() || align > 10000 { return Err(MirEvalError::UndefinedBehavior(format!("Alignment {align} is invalid"))); } - while self.heap.len() % align != 0 { + while !self.heap.len().is_multiple_of(align) { self.heap.push(0); } if size.checked_add(self.heap.len()).is_none_or(|x| x > self.memory_limit) {
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index e9665d5..bb4c963 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -119,25 +119,25 @@ destination.write_from_bytes(self, &result)?; return Ok(true); } - if let ItemContainerId::TraitId(t) = def.lookup(self.db).container { - if self.db.lang_attr(t.into()) == Some(LangItem::Clone) { - let [self_ty] = generic_args.as_slice(Interner) else { - not_supported!("wrong generic arg count for clone"); - }; - let Some(self_ty) = self_ty.ty(Interner) else { - not_supported!("wrong generic arg kind for clone"); - }; - // Clone has special impls for tuples and function pointers - if matches!( - self_ty.kind(Interner), - TyKind::Function(_) | TyKind::Tuple(..) | TyKind::Closure(..) - ) { - self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?; - return Ok(true); - } - // Return early to prevent caching clone as non special fn. - return Ok(false); + if let ItemContainerId::TraitId(t) = def.lookup(self.db).container + && self.db.lang_attr(t.into()) == Some(LangItem::Clone) + { + let [self_ty] = generic_args.as_slice(Interner) else { + not_supported!("wrong generic arg count for clone"); + }; + let Some(self_ty) = self_ty.ty(Interner) else { + not_supported!("wrong generic arg kind for clone"); + }; + // Clone has special impls for tuples and function pointers + if matches!( + self_ty.kind(Interner), + TyKind::Function(_) | TyKind::Tuple(..) | TyKind::Closure(..) + ) { + self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?; + return Ok(true); } + // Return early to prevent caching clone as non special fn. + return Ok(false); } self.not_special_fn_cache.borrow_mut().insert(def); Ok(false) @@ -1256,23 +1256,22 @@ let addr = tuple.interval.addr.offset(offset); args.push(IntervalAndTy::new(addr, field, self, locals)?); } - if let Some(target) = LangItem::FnOnce.resolve_trait(self.db, self.crate_id) { - if let Some(def) = target + if let Some(target) = LangItem::FnOnce.resolve_trait(self.db, self.crate_id) + && let Some(def) = target .trait_items(self.db) .method_by_name(&Name::new_symbol_root(sym::call_once)) - { - self.exec_fn_trait( - def, - &args, - // FIXME: wrong for manual impls of `FnOnce` - Substitution::empty(Interner), - locals, - destination, - None, - span, - )?; - return Ok(true); - } + { + self.exec_fn_trait( + def, + &args, + // FIXME: wrong for manual impls of `FnOnce` + Substitution::empty(Interner), + locals, + destination, + None, + span, + )?; + return Ok(true); } not_supported!("FnOnce was not available for executing const_eval_select"); } @@ -1367,12 +1366,11 @@ break; } } - if signed { - if let Some((&l, &r)) = lhs.iter().zip(rhs).next_back() { - if l != r { - result = (l as i8).cmp(&(r as i8)); - } - } + if signed + && let Some((&l, &r)) = lhs.iter().zip(rhs).next_back() + && l != r + { + result = (l as i8).cmp(&(r as i8)); } if let Some(e) = LangItem::Ordering.resolve_enum(self.db, self.crate_id) { let ty = self.db.ty(e.into());
diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs index bc331a2..f554772 100644 --- a/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/crates/hir-ty/src/mir/eval/shim/simd.rs
@@ -114,12 +114,11 @@ break; } } - if is_signed { - if let Some((&l, &r)) = l.iter().zip(r).next_back() { - if l != r { - result = (l as i8).cmp(&(r as i8)); - } - } + if is_signed + && let Some((&l, &r)) = l.iter().zip(r).next_back() + && l != r + { + result = (l as i8).cmp(&(r as i8)); } let result = match result { Ordering::Less => ["lt", "le", "ne"].contains(&name),
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index c1f8696..eb5af58 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -5,7 +5,9 @@ use test_fixture::WithFixture; use crate::display::DisplayTarget; -use crate::{Interner, Substitution, db::HirDatabase, mir::MirLowerError, test_db::TestDB}; +use crate::{ + Interner, Substitution, db::HirDatabase, mir::MirLowerError, setup_tracing, test_db::TestDB, +}; use super::{MirEvalError, interpret_mir}; @@ -49,6 +51,7 @@ expected_stdout: &str, expected_stderr: &str, ) { + let _tracing = setup_tracing(); let (db, file_ids) = TestDB::with_many_files(ra_fixture); let file_id = *file_ids.last().unwrap(); let x = eval_main(&db, file_id);
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 07d8147..0bb8e6f 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs
@@ -320,11 +320,11 @@ expr_id: ExprId, current: BasicBlockId, ) -> Result<Option<(Operand, BasicBlockId)>> { - if !self.has_adjustments(expr_id) { - if let Expr::Literal(l) = &self.body[expr_id] { - let ty = self.expr_ty_without_adjust(expr_id); - return Ok(Some((self.lower_literal_to_operand(ty, l)?, current))); - } + if !self.has_adjustments(expr_id) + && let Expr::Literal(l) = &self.body[expr_id] + { + let ty = self.expr_ty_without_adjust(expr_id); + return Ok(Some((self.lower_literal_to_operand(ty, l)?, current))); } let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else { return Ok(None); @@ -1039,18 +1039,18 @@ && rhs_ty.is_scalar() && (lhs_ty == rhs_ty || builtin_inequal_impls) }; - if !is_builtin { - if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { - let func = Operand::from_fn(self.db, func_id, generic_args); - return self.lower_call_and_args( - func, - [*lhs, *rhs].into_iter(), - place, - current, - self.is_uninhabited(expr_id), - expr_id.into(), - ); - } + if !is_builtin + && let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) + { + let func = Operand::from_fn(self.db, func_id, generic_args); + return self.lower_call_and_args( + func, + [*lhs, *rhs].into_iter(), + place, + current, + self.is_uninhabited(expr_id), + expr_id.into(), + ); } if let hir_def::hir::BinaryOp::Assignment { op: Some(op) } = op { // last adjustment is `&mut` which we don't want it. @@ -1596,10 +1596,10 @@ fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { let mut ty = None; - if let Some(it) = self.infer.expr_adjustments.get(&e) { - if let Some(it) = it.last() { - ty = Some(it.target.clone()); - } + if let Some(it) = self.infer.expr_adjustments.get(&e) + && let Some(it) = it.last() + { + ty = Some(it.target.clone()); } ty.unwrap_or_else(|| self.expr_ty_without_adjust(e)) } @@ -1848,13 +1848,13 @@ self.result.param_locals.extend(params.clone().map(|(it, ty)| { let local_id = self.result.locals.alloc(Local { ty }); self.drop_scopes.last_mut().unwrap().locals.push(local_id); - if let Pat::Bind { id, subpat: None } = self.body[it] { - if matches!( + if let Pat::Bind { id, subpat: None } = self.body[it] + && matches!( self.body[id].mode, BindingAnnotation::Unannotated | BindingAnnotation::Mutable - ) { - self.result.binding_locals.insert(id, local_id); - } + ) + { + self.result.binding_locals.insert(id, local_id); } local_id })); @@ -1887,10 +1887,10 @@ .into_iter() .skip(base_param_count + self_binding.is_some() as usize); for ((param, _), local) in params.zip(local_params) { - if let Pat::Bind { id, .. } = self.body[param] { - if local == self.binding_local(id)? { - continue; - } + if let Pat::Bind { id, .. } = self.body[param] + && local == self.binding_local(id)? + { + continue; } let r = self.pattern_match(current, None, local.into(), param)?; if let Some(b) = r.1 { @@ -2163,7 +2163,9 @@ let _p = tracing::info_span!("mir_body_query", ?detail).entered(); let body = db.body(def); let infer = db.infer(def); - let mut result = lower_to_mir(db, def, &body, &infer, body.body_expr)?; + let mut result = crate::next_solver::with_new_cache(|| { + lower_to_mir(db, def, &body, &infer, body.body_expr) + })?; result.shrink_to_fit(); Ok(Arc::new(result)) }
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index e074c2d..42a1466 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -189,17 +189,14 @@ self.expr_ty_without_adjust(expr_id), expr_id.into(), 'b: { - if let Some((f, _)) = self.infer.method_resolution(expr_id) { - if let Some(deref_trait) = + if let Some((f, _)) = self.infer.method_resolution(expr_id) + && let Some(deref_trait) = self.resolve_lang_item(LangItem::DerefMut)?.as_trait() - { - if let Some(deref_fn) = deref_trait - .trait_items(self.db) - .method_by_name(&Name::new_symbol_root(sym::deref_mut)) - { - break 'b deref_fn == f; - } - } + && let Some(deref_fn) = deref_trait + .trait_items(self.db) + .method_by_name(&Name::new_symbol_root(sym::deref_mut)) + { + break 'b deref_fn == f; } false },
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 3325226..0440d85 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -317,27 +317,26 @@ (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } - if let &Some(slice) = slice { - if mode != MatchingMode::Check { - if let Pat::Bind { id, subpat: _ } = self.body[slice] { - let next_place = cond_place.project( - ProjectionElem::Subslice { - from: prefix.len() as u64, - to: suffix.len() as u64, - }, - &mut self.result.projection_store, - ); - let mode = self.infer.binding_modes[slice]; - (current, current_else) = self.pattern_match_binding( - id, - mode, - next_place, - (slice).into(), - current, - current_else, - )?; - } - } + if let &Some(slice) = slice + && mode != MatchingMode::Check + && let Pat::Bind { id, subpat: _ } = self.body[slice] + { + let next_place = cond_place.project( + ProjectionElem::Subslice { + from: prefix.len() as u64, + to: suffix.len() as u64, + }, + &mut self.result.projection_store, + ); + let mode = self.infer.binding_modes[slice]; + (current, current_else) = self.pattern_match_binding( + id, + mode, + next_place, + (slice).into(), + current, + current_else, + )?; } for (i, &pat) in suffix.iter().enumerate() { let next_place = cond_place.project( @@ -391,10 +390,10 @@ return Ok((current, current_else)); } let (c, subst) = 'b: { - if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) { - if let AssocItemId::ConstId(c) = x.0 { - break 'b (c, x.1); - } + if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) + && let AssocItemId::ConstId(c) = x.0 + { + break 'b (c, x.1); } if let ResolveValueResult::ValueNs(ValueNs::ConstId(c), _) = pr { break 'b (c, Substitution::empty(Interner));
diff --git a/crates/hir-ty/src/next_solver.rs b/crates/hir-ty/src/next_solver.rs new file mode 100644 index 0000000..21762aa --- /dev/null +++ b/crates/hir-ty/src/next_solver.rs
@@ -0,0 +1,46 @@ +//! Things relevant to the next trait solver. +#![allow(unused, unreachable_pub)] + +pub mod abi; +mod consts; +mod def_id; +pub mod fold; +mod fulfill; +mod generic_arg; +pub mod generics; +pub mod infer; +//mod infer_new; +pub mod interner; +mod ir_print; +pub mod mapping; +mod opaques; +pub mod predicate; +pub(crate) mod project; +mod region; +mod solver; +mod ty; +pub mod util; + +pub use consts::*; +pub use def_id::*; +pub use generic_arg::*; +//pub use infer_new::*; +pub use interner::*; +pub use opaques::*; +pub use predicate::*; +pub use region::*; +pub use solver::*; +pub use ty::*; + +pub type Binder<'db, T> = rustc_type_ir::Binder<DbInterner<'db>, T>; +pub type EarlyBinder<'db, T> = rustc_type_ir::EarlyBinder<DbInterner<'db>, T>; +pub type Canonical<'db, T> = rustc_type_ir::Canonical<DbInterner<'db>, T>; +pub type CanonicalVarValues<'db> = rustc_type_ir::CanonicalVarValues<DbInterner<'db>>; +pub type CanonicalVarKind<'db> = rustc_type_ir::CanonicalVarKind<DbInterner<'db>>; +pub type CanonicalQueryInput<'db, V> = rustc_type_ir::CanonicalQueryInput<DbInterner<'db>, V>; +pub type AliasTy<'db> = rustc_type_ir::AliasTy<DbInterner<'db>>; +pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig<DbInterner<'db>>>; +pub type TypingMode<'db> = rustc_type_ir::TypingMode<DbInterner<'db>>; + +pub type FxIndexMap<K, V> = + indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
diff --git a/crates/hir-ty/src/next_solver/abi.rs b/crates/hir-ty/src/next_solver/abi.rs new file mode 100644 index 0000000..80d1ea4 --- /dev/null +++ b/crates/hir-ty/src/next_solver/abi.rs
@@ -0,0 +1,68 @@ +//! ABI-related things in the next-trait-solver. +use rustc_type_ir::{error::TypeError, relate::Relate}; + +use crate::FnAbi; + +use super::interner::DbInterner; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Safety { + Unsafe, + Safe, +} + +impl<'db> Relate<DbInterner<'db>> for Safety { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + _relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + if a != b { + Err(TypeError::SafetyMismatch(rustc_type_ir::error::ExpectedFound::new(a, b))) + } else { + Ok(a) + } + } +} + +impl<'db> rustc_type_ir::inherent::Safety<DbInterner<'db>> for Safety { + fn safe() -> Self { + Self::Safe + } + + fn is_safe(self) -> bool { + matches!(self, Safety::Safe) + } + + fn prefix_str(self) -> &'static str { + match self { + Self::Unsafe => "unsafe ", + Self::Safe => "", + } + } +} + +impl<'db> Relate<DbInterner<'db>> for FnAbi { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + _relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + if a == b { + Ok(a) + } else { + Err(TypeError::AbiMismatch(rustc_type_ir::error::ExpectedFound::new(a, b))) + } + } +} + +impl<'db> rustc_type_ir::inherent::Abi<DbInterner<'db>> for FnAbi { + fn rust() -> Self { + FnAbi::Rust + } + + fn is_rust(self) -> bool { + // TODO: rustc does not consider `RustCall` to be true here, but Chalk does + matches!(self, FnAbi::Rust | FnAbi::RustCall) + } +}
diff --git a/crates/hir-ty/src/next_solver/consts.rs b/crates/hir-ty/src/next_solver/consts.rs new file mode 100644 index 0000000..5698ff2 --- /dev/null +++ b/crates/hir-ty/src/next_solver/consts.rs
@@ -0,0 +1,404 @@ +//! Things related to consts in the next-trait-solver. + +use std::hash::Hash; + +use intern::{Interned, Symbol}; +use rustc_ast_ir::try_visit; +use rustc_ast_ir::visit::VisitorResult; +use rustc_type_ir::{ + BoundVar, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitable, WithCachedTypeInfo, + inherent::{IntoKind, PlaceholderLike}, + relate::Relate, +}; + +use crate::{ConstScalar, MemoryMap, interner::InternedWrapperNoDebug}; + +use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty}; + +pub type ConstKind<'db> = rustc_type_ir::ConstKind<DbInterner<'db>>; +pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst<DbInterner<'db>>; + +#[salsa::interned(constructor = new_, debug)] +pub struct Const<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>>, +} + +impl<'db> Const<'db> { + pub fn new(interner: DbInterner<'db>, kind: ConstKind<'db>) -> Self { + let flags = FlagComputation::for_const_kind(&kind); + let cached = WithCachedTypeInfo { + internee: kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + Const::new_(interner.db(), InternedWrapperNoDebug(cached)) + } + + pub fn inner(&self) -> &WithCachedTypeInfo<ConstKind<'db>> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Const<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + pub fn error(interner: DbInterner<'db>) -> Self { + Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) + } + + pub fn new_param(interner: DbInterner<'db>, param: ParamConst) -> Self { + Const::new(interner, rustc_type_ir::ConstKind::Param(param)) + } + + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst) -> Self { + Const::new(interner, ConstKind::Placeholder(placeholder)) + } + + pub fn is_ct_infer(&self) -> bool { + matches!(&self.inner().internee, ConstKind::Infer(_)) + } + + pub fn is_trivially_wf(self) -> bool { + match self.kind() { + ConstKind::Param(_) | ConstKind::Placeholder(_) | ConstKind::Bound(..) => true, + ConstKind::Infer(_) + | ConstKind::Unevaluated(..) + | ConstKind::Value(_) + | ConstKind::Error(_) + | ConstKind::Expr(_) => false, + } + } +} + +impl<'db> std::fmt::Debug for InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.internee.fmt(f) + } +} + +pub type PlaceholderConst = Placeholder<rustc_type_ir::BoundVar>; + +#[derive(Copy, Clone, Hash, Eq, PartialEq)] +pub struct ParamConst { + pub index: u32, +} + +impl std::fmt::Debug for ParamConst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.index) + } +} + +/// A type-level constant value. +/// +/// Represents a typed, fully evaluated constant. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct ValueConst<'db> { + pub(crate) ty: Ty<'db>, + pub(crate) value: Valtree<'db>, +} + +impl<'db> ValueConst<'db> { + pub fn new(ty: Ty<'db>, bytes: ConstBytes) -> Self { + let value = Valtree::new(bytes); + ValueConst { ty, value } + } +} + +impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'db> { + fn ty(self) -> Ty<'db> { + self.ty + } + + fn valtree(self) -> Valtree<'db> { + self.value + } +} + +impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for ValueConst<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.ty.visit_with(visitor) + } +} + +impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for ValueConst<'db> { + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + ValueConst { ty: self.ty.fold_with(folder), value: self.value } + } + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(ValueConst { ty: self.ty.try_fold_with(folder)?, value: self.value }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConstBytes(pub Box<[u8]>, pub MemoryMap); + +impl Hash for ConstBytes { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.0.hash(state) + } +} + +#[salsa::interned(constructor = new_, debug)] +pub struct Valtree<'db> { + #[returns(ref)] + bytes_: ConstBytes, +} + +impl<'db> Valtree<'db> { + pub fn new(bytes: ConstBytes) -> Self { + salsa::with_attached_database(|db| unsafe { + // SAFETY: ¯\_(ツ)_/¯ + std::mem::transmute(Valtree::new_(db, bytes)) + }) + .unwrap() + } + + pub fn inner(&self) -> &ConstBytes { + salsa::with_attached_database(|db| { + let inner = self.bytes_(db); + // SAFETY: The caller already has access to a `Valtree<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ExprConst; + +impl rustc_type_ir::inherent::ParamLike for ParamConst { + fn index(self) -> u32 { + self.index + } +} + +impl<'db> IntoKind for Const<'db> { + type Kind = ConstKind<'db>; + + fn kind(self) -> Self::Kind { + self.inner().internee + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for Const<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_const(*self) + } +} + +impl<'db> TypeSuperVisitable<DbInterner<'db>> for Const<'db> { + fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + match self.kind() { + ConstKind::Unevaluated(uv) => uv.visit_with(visitor), + ConstKind::Value(v) => v.visit_with(visitor), + ConstKind::Expr(e) => e.visit_with(visitor), + ConstKind::Error(e) => e.visit_with(visitor), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) => V::Result::output(), + } + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for Const<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + folder.try_fold_const(self) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + folder.fold_const(self) + } +} + +impl<'db> TypeSuperFoldable<DbInterner<'db>> for Const<'db> { + fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + let kind = match self.kind() { + ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?), + ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), + ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => return Ok(self), + }; + if kind != self.kind() { Ok(Const::new(folder.cx(), kind)) } else { Ok(self) } + } + fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Self { + let kind = match self.kind() { + ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.fold_with(folder)), + ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)), + ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => return self, + }; + if kind != self.kind() { Const::new(folder.cx(), kind) } else { self } + } +} + +impl<'db> Relate<DbInterner<'db>> for Const<'db> { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + relation.consts(a, b) + } +} + +impl<'db> Flags for Const<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().outer_exclusive_binder + } +} + +impl<'db> rustc_type_ir::inherent::Const<DbInterner<'db>> for Const<'db> { + fn new_infer(interner: DbInterner<'db>, var: rustc_type_ir::InferConst) -> Self { + Const::new(interner, ConstKind::Infer(var)) + } + + fn new_var(interner: DbInterner<'db>, var: rustc_type_ir::ConstVid) -> Self { + Const::new(interner, ConstKind::Infer(rustc_type_ir::InferConst::Var(var))) + } + + fn new_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundVar, + ) -> Self { + Const::new(interner, ConstKind::Bound(debruijn, var)) + } + + fn new_anon_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: rustc_type_ir::BoundVar, + ) -> Self { + Const::new(interner, ConstKind::Bound(debruijn, var)) + } + + fn new_unevaluated( + interner: DbInterner<'db>, + uv: rustc_type_ir::UnevaluatedConst<DbInterner<'db>>, + ) -> Self { + Const::new(interner, ConstKind::Unevaluated(uv)) + } + + fn new_expr(interner: DbInterner<'db>, expr: ExprConst) -> Self { + Const::new(interner, ConstKind::Expr(expr)) + } + + fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self { + Const::new(interner, ConstKind::Error(guar)) + } + + fn new_placeholder( + interner: DbInterner<'db>, + param: <DbInterner<'db> as rustc_type_ir::Interner>::PlaceholderConst, + ) -> Self { + Const::new(interner, ConstKind::Placeholder(param)) + } +} + +impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderConst { + type Bound = rustc_type_ir::BoundVar; + + fn universe(self) -> rustc_type_ir::UniverseIndex { + self.universe + } + + fn var(self) -> rustc_type_ir::BoundVar { + self.bound + } + + fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { + Placeholder { universe: ui, bound: self.bound } + } + + fn new(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: var } + } + fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: var } + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for ExprConst { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + // Ensure we get back to this when we fill in the fields + let ExprConst = &self; + V::Result::output() + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for ExprConst { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(ExprConst) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + ExprConst + } +} + +impl<'db> Relate<DbInterner<'db>> for ExprConst { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + // Ensure we get back to this when we fill in the fields + let ExprConst = b; + Ok(a) + } +} + +impl<'db> rustc_type_ir::inherent::ExprConst<DbInterner<'db>> for ExprConst { + fn args(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs { + // Ensure we get back to this when we fill in the fields + let ExprConst = self; + GenericArgs::default() + } +}
diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs new file mode 100644 index 0000000..64eab2d --- /dev/null +++ b/crates/hir-ty/src/next_solver/def_id.rs
@@ -0,0 +1,92 @@ +//! Definition of `SolverDefId` + +use hir_def::{ + AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, StaticId, StructId, + TraitId, TypeAliasId, UnionId, +}; +use rustc_type_ir::inherent; +use stdx::impl_from; + +use crate::db::{InternedClosureId, InternedCoroutineId, InternedOpaqueTyId}; + +use super::DbInterner; + +#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +pub enum Ctor { + Struct(StructId), + Enum(EnumVariantId), +} + +#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +pub enum SolverDefId { + AdtId(AdtId), + ConstId(ConstId), + FunctionId(FunctionId), + ImplId(ImplId), + StaticId(StaticId), + TraitId(TraitId), + TypeAliasId(TypeAliasId), + ForeignId(TypeAliasId), + InternedClosureId(InternedClosureId), + InternedCoroutineId(InternedCoroutineId), + InternedOpaqueTyId(InternedOpaqueTyId), + Ctor(Ctor), +} + +impl_from!( + AdtId(StructId, EnumId, UnionId), + ConstId, + FunctionId, + ImplId, + StaticId, + TraitId, + TypeAliasId, + InternedClosureId, + InternedCoroutineId, + InternedOpaqueTyId + for SolverDefId +); + +impl From<GenericDefId> for SolverDefId { + fn from(value: GenericDefId) -> Self { + match value { + GenericDefId::AdtId(adt_id) => SolverDefId::AdtId(adt_id), + GenericDefId::ConstId(const_id) => SolverDefId::ConstId(const_id), + GenericDefId::FunctionId(function_id) => SolverDefId::FunctionId(function_id), + GenericDefId::ImplId(impl_id) => SolverDefId::ImplId(impl_id), + GenericDefId::StaticId(static_id) => SolverDefId::StaticId(static_id), + GenericDefId::TraitId(trait_id) => SolverDefId::TraitId(trait_id), + GenericDefId::TypeAliasId(type_alias_id) => SolverDefId::TypeAliasId(type_alias_id), + } + } +} + +impl TryFrom<SolverDefId> for GenericDefId { + type Error = SolverDefId; + + fn try_from(value: SolverDefId) -> Result<Self, Self::Error> { + Ok(match value { + SolverDefId::AdtId(adt_id) => GenericDefId::AdtId(adt_id), + SolverDefId::ConstId(const_id) => GenericDefId::ConstId(const_id), + SolverDefId::FunctionId(function_id) => GenericDefId::FunctionId(function_id), + SolverDefId::ImplId(impl_id) => GenericDefId::ImplId(impl_id), + SolverDefId::StaticId(static_id) => GenericDefId::StaticId(static_id), + SolverDefId::TraitId(trait_id) => GenericDefId::TraitId(trait_id), + SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id), + SolverDefId::ForeignId(_) => return Err(value), + SolverDefId::InternedClosureId(_) => return Err(value), + SolverDefId::InternedCoroutineId(_) => return Err(value), + SolverDefId::InternedOpaqueTyId(_) => return Err(value), + SolverDefId::Ctor(_) => return Err(value), + }) + } +} + +impl<'db> inherent::DefId<DbInterner<'db>> for SolverDefId { + fn as_local(self) -> Option<SolverDefId> { + Some(self) + } + fn is_local(self) -> bool { + true + } +}
diff --git a/crates/hir-ty/src/next_solver/fold.rs b/crates/hir-ty/src/next_solver/fold.rs new file mode 100644 index 0000000..3cc1e64 --- /dev/null +++ b/crates/hir-ty/src/next_solver/fold.rs
@@ -0,0 +1,129 @@ +//! Fold impls for the next-trait-solver. + +use rustc_type_ir::{ + BoundVar, DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, + inherent::{IntoKind, Region as _}, +}; + +use super::{ + Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, Ty, TyKind, +}; + +/// A delegate used when instantiating bound vars. +/// +/// Any implementation must make sure that each bound variable always +/// gets mapped to the same result. `BoundVarReplacer` caches by using +/// a `DelayedMap` which does not cache the first few types it encounters. +pub trait BoundVarReplacerDelegate<'db> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db>; + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db>; + fn replace_const(&mut self, bv: BoundVar) -> Const<'db>; +} + +/// A simple delegate taking 3 mutable functions. The used functions must +/// always return the same result for each bound variable, no matter how +/// frequently they are called. +pub struct FnMutDelegate<'db, 'a> { + pub regions: &'a mut (dyn FnMut(BoundRegion) -> Region<'db> + 'a), + pub types: &'a mut (dyn FnMut(BoundTy) -> Ty<'db> + 'a), + pub consts: &'a mut (dyn FnMut(BoundVar) -> Const<'db> + 'a), +} + +impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + (self.regions)(br) + } + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + (self.types)(bt) + } + fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + (self.consts)(bv) + } +} + +/// Replaces the escaping bound vars (late bound regions or bound types) in a type. +pub(crate) struct BoundVarReplacer<'db, D> { + interner: DbInterner<'db>, + /// As with `RegionFolder`, represents the index of a binder *just outside* + /// the ones we have visited. + current_index: DebruijnIndex, + + delegate: D, +} + +impl<'db, D: BoundVarReplacerDelegate<'db>> BoundVarReplacer<'db, D> { + pub fn new(tcx: DbInterner<'db>, delegate: D) -> Self { + BoundVarReplacer { interner: tcx, current_index: DebruijnIndex::ZERO, delegate } + } +} + +impl<'db, D> TypeFolder<DbInterner<'db>> for BoundVarReplacer<'db, D> +where + D: BoundVarReplacerDelegate<'db>, +{ + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_binder<T: TypeFoldable<DbInterner<'db>>>( + &mut self, + t: Binder<'db, T>, + ) -> Binder<'db, T> { + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + match t.kind() { + TyKind::Bound(debruijn, bound_ty) if debruijn == self.current_index => { + let ty = self.delegate.replace_ty(bound_ty); + debug_assert!(!ty.has_vars_bound_above(DebruijnIndex::ZERO)); + rustc_type_ir::shift_vars(self.interner, ty, self.current_index.as_u32()) + } + _ => { + if !t.has_vars_bound_at_or_above(self.current_index) { + t + } else { + t.super_fold_with(self) + } + } + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + match r.kind() { + RegionKind::ReBound(debruijn, br) if debruijn == self.current_index => { + let region = self.delegate.replace_region(br); + if let RegionKind::ReBound(debruijn1, br) = region.kind() { + // If the callback returns a bound region, + // that region should always use the INNERMOST + // debruijn index. Then we adjust it to the + // correct depth. + assert_eq!(debruijn1, DebruijnIndex::ZERO); + Region::new_bound(self.interner, debruijn, br) + } else { + region + } + } + _ => r, + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + match ct.kind() { + ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => { + let ct = self.delegate.replace_const(bound_const); + debug_assert!(!ct.has_vars_bound_above(DebruijnIndex::ZERO)); + rustc_type_ir::shift_vars(self.interner, ct, self.current_index.as_u32()) + } + _ => ct.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } +}
diff --git a/crates/hir-ty/src/next_solver/fulfill.rs b/crates/hir-ty/src/next_solver/fulfill.rs new file mode 100644 index 0000000..007a674 --- /dev/null +++ b/crates/hir-ty/src/next_solver/fulfill.rs
@@ -0,0 +1,229 @@ +//! Fulfill loop for next-solver. + +use std::marker::PhantomData; +use std::mem; +use std::ops::ControlFlow; +use std::vec::ExtractIf; + +use rustc_next_trait_solver::delegate::SolverDelegate; +use rustc_next_trait_solver::solve::{ + GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt, +}; +use rustc_type_ir::Interner; +use rustc_type_ir::inherent::Span as _; +use rustc_type_ir::solve::{Certainty, NoSolution}; + +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::infer::traits::{PredicateObligation, PredicateObligations}; +use crate::next_solver::{DbInterner, SolverContext, Span, TypingMode}; + +type PendingObligations<'db> = + Vec<(PredicateObligation<'db>, Option<GoalStalledOn<DbInterner<'db>>>)>; + +/// A trait engine using the new trait solver. +/// +/// This is mostly identical to how `evaluate_all` works inside of the +/// solver, except that the requirements are slightly different. +/// +/// Unlike `evaluate_all` it is possible to add new obligations later on +/// and we also have to track diagnostics information by using `Obligation` +/// instead of `Goal`. +/// +/// 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`. +pub struct FulfillmentCtxt<'db> { + obligations: ObligationStorage<'db>, + + /// The snapshot in which this context was created. Using the context + /// 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. + usable_in_snapshot: usize, +} + +#[derive(Default, Debug)] +struct ObligationStorage<'db> { + /// Obligations which resulted in an overflow in fulfillment itself. + /// + /// We cannot eagerly return these as error so we instead store them here + /// to avoid recomputing them each time `select_where_possible` is called. + /// This also allows us to return the correct `FulfillmentError` for them. + overflowed: Vec<PredicateObligation<'db>>, + pending: PendingObligations<'db>, +} + +impl<'db> ObligationStorage<'db> { + fn register( + &mut self, + obligation: PredicateObligation<'db>, + stalled_on: Option<GoalStalledOn<DbInterner<'db>>>, + ) { + self.pending.push((obligation, stalled_on)); + } + + fn has_pending_obligations(&self) -> bool { + !self.pending.is_empty() || !self.overflowed.is_empty() + } + + fn clone_pending(&self) -> PredicateObligations<'db> { + let mut obligations: PredicateObligations<'db> = + self.pending.iter().map(|(o, _)| o.clone()).collect(); + obligations.extend(self.overflowed.iter().cloned()); + obligations + } + + fn drain_pending( + &mut self, + cond: impl Fn(&PredicateObligation<'db>) -> bool, + ) -> PendingObligations<'db> { + let (not_stalled, pending) = + mem::take(&mut self.pending).into_iter().partition(|(o, _)| cond(o)); + self.pending = pending; + not_stalled + } + + fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'db>) { + infcx.probe(|_| { + // IMPORTANT: we must not use solve any inference variables in the obligations + // as this is all happening inside of a probe. We use a probe to make sure + // we get all obligations involved in the overflow. We pretty much check: if + // we were to do another step of `select_where_possible`, which goals would + // change. + // FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed. + self.overflowed.extend( + self.pending + .extract_if(.., |(o, stalled_on)| { + let goal = o.as_goal(); + let result = <&SolverContext<'db>>::from(infcx).evaluate_root_goal( + goal, + Span::dummy(), + stalled_on.take(), + ); + matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. })) + }) + .map(|(o, _)| o), + ); + }) + } +} + +impl<'db> FulfillmentCtxt<'db> { + pub fn new(infcx: &InferCtxt<'db>) -> FulfillmentCtxt<'db> { + FulfillmentCtxt { + obligations: Default::default(), + usable_in_snapshot: infcx.num_open_snapshots(), + } + } +} + +impl<'db> FulfillmentCtxt<'db> { + #[tracing::instrument(level = "trace", skip(self, infcx))] + pub(crate) fn register_predicate_obligation( + &mut self, + infcx: &InferCtxt<'db>, + obligation: PredicateObligation<'db>, + ) { + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + self.obligations.register(obligation, None); + } + + pub(crate) fn collect_remaining_errors( + &mut self, + infcx: &InferCtxt<'db>, + ) -> Vec<NextSolverError<'db>> { + self.obligations + .pending + .drain(..) + .map(|(obligation, _)| NextSolverError::Ambiguity(obligation)) + .chain(self.obligations.overflowed.drain(..).map(NextSolverError::Overflow)) + .collect() + } + + pub(crate) fn select_where_possible( + &mut self, + infcx: &InferCtxt<'db>, + ) -> Vec<NextSolverError<'db>> { + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + let mut errors = Vec::new(); + loop { + let mut any_changed = false; + for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) { + if obligation.recursion_depth >= infcx.interner.recursion_limit() { + self.obligations.on_fulfillment_overflow(infcx); + // Only return true errors that we have accumulated while processing. + return errors; + } + + let goal = obligation.as_goal(); + let delegate = <&SolverContext<'db>>::from(infcx); + if let Some(certainty) = delegate.compute_goal_fast_path(goal, Span::dummy()) { + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.obligations.register(obligation, None); + } + } + continue; + } + + let result = delegate.evaluate_root_goal(goal, Span::dummy(), stalled_on); + let GoalEvaluation { certainty, has_changed, stalled_on } = match result { + Ok(result) => result, + Err(NoSolution) => { + errors.push(NextSolverError::TrueError(obligation)); + continue; + } + }; + + if has_changed == HasChanged::Yes { + // We increment the recursion depth here to track the number of times + // this goal has resulted in inference progress. This doesn't precisely + // model the way that we track recursion depth in the old solver due + // to the fact that we only process root obligations, but it is a good + // approximation and should only result in fulfillment overflow in + // pathological cases. + obligation.recursion_depth += 1; + any_changed = true; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), + } + } + + if !any_changed { + break; + } + } + + errors + } + + pub(crate) fn select_all_or_error( + &mut self, + infcx: &InferCtxt<'db>, + ) -> Vec<NextSolverError<'db>> { + let errors = self.select_where_possible(infcx); + if !errors.is_empty() { + return errors; + } + + self.collect_remaining_errors(infcx) + } + + fn has_pending_obligations(&self) -> bool { + self.obligations.has_pending_obligations() + } + + fn pending_obligations(&self) -> PredicateObligations<'db> { + self.obligations.clone_pending() + } +} + +#[derive(Debug)] +pub enum NextSolverError<'db> { + TrueError(PredicateObligation<'db>), + Ambiguity(PredicateObligation<'db>), + Overflow(PredicateObligation<'db>), +}
diff --git a/crates/hir-ty/src/next_solver/generic_arg.rs b/crates/hir-ty/src/next_solver/generic_arg.rs new file mode 100644 index 0000000..85a7992 --- /dev/null +++ b/crates/hir-ty/src/next_solver/generic_arg.rs
@@ -0,0 +1,525 @@ +//! Things related to generic args in the next-trait-solver. + +use intern::{Interned, Symbol}; +use rustc_type_ir::{ + ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys, + GenericArgKind, IntTy, Interner, TermKind, TyKind, TyVid, TypeFoldable, TypeVisitable, + Variance, + inherent::{GenericArg as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _}, + relate::{Relate, VarianceDiagInfo}, +}; +use smallvec::SmallVec; + +use crate::db::HirDatabase; + +use super::{ + Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys, + generics::{GenericParamDef, GenericParamDefKind, Generics}, + interned_vec_db, +}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum GenericArg<'db> { + Ty(Ty<'db>), + Lifetime(Region<'db>), + Const(Const<'db>), +} + +impl<'db> std::fmt::Debug for GenericArg<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ty(t) => std::fmt::Debug::fmt(t, f), + Self::Lifetime(r) => std::fmt::Debug::fmt(r, f), + Self::Const(c) => std::fmt::Debug::fmt(c, f), + } + } +} + +impl<'db> From<Term<'db>> for GenericArg<'db> { + fn from(value: Term<'db>) -> Self { + match value { + Term::Ty(ty) => GenericArg::Ty(ty), + Term::Const(c) => GenericArg::Const(c), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum Term<'db> { + Ty(Ty<'db>), + Const(Const<'db>), +} + +impl<'db> std::fmt::Debug for Term<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ty(t) => std::fmt::Debug::fmt(t, f), + Self::Const(c) => std::fmt::Debug::fmt(c, f), + } + } +} + +impl<'db> Term<'db> { + pub fn expect_type(&self) -> Ty<'db> { + self.as_type().expect("expected a type, but found a const") + } + + pub fn is_trivially_wf(&self, tcx: DbInterner<'db>) -> bool { + match self.kind() { + TermKind::Ty(ty) => ty.is_trivially_wf(tcx), + TermKind::Const(ct) => ct.is_trivially_wf(), + } + } +} + +impl<'db> From<Ty<'db>> for GenericArg<'db> { + fn from(value: Ty<'db>) -> Self { + Self::Ty(value) + } +} + +impl<'db> From<Region<'db>> for GenericArg<'db> { + fn from(value: Region<'db>) -> Self { + Self::Lifetime(value) + } +} + +impl<'db> From<Const<'db>> for GenericArg<'db> { + fn from(value: Const<'db>) -> Self { + Self::Const(value) + } +} + +impl<'db> IntoKind for GenericArg<'db> { + type Kind = GenericArgKind<DbInterner<'db>>; + + fn kind(self) -> Self::Kind { + match self { + GenericArg::Ty(ty) => GenericArgKind::Type(ty), + GenericArg::Lifetime(region) => GenericArgKind::Lifetime(region), + GenericArg::Const(c) => GenericArgKind::Const(c), + } + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for GenericArg<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + match self { + GenericArg::Lifetime(lt) => lt.visit_with(visitor), + GenericArg::Ty(ty) => ty.visit_with(visitor), + GenericArg::Const(ct) => ct.visit_with(visitor), + } + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for GenericArg<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + match self.kind() { + GenericArgKind::Lifetime(lt) => lt.try_fold_with(folder).map(Into::into), + GenericArgKind::Type(ty) => ty.try_fold_with(folder).map(Into::into), + GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), + } + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + match self.kind() { + GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(), + GenericArgKind::Type(ty) => ty.fold_with(folder).into(), + GenericArgKind::Const(ct) => ct.fold_with(folder).into(), + } + } +} + +impl<'db> Relate<DbInterner<'db>> for GenericArg<'db> { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + match (a.kind(), b.kind()) { + (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => { + Ok(relation.relate(a_lt, b_lt)?.into()) + } + (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => { + Ok(relation.relate(a_ty, b_ty)?.into()) + } + (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => { + Ok(relation.relate(a_ct, b_ct)?.into()) + } + (GenericArgKind::Lifetime(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (GenericArgKind::Type(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (GenericArgKind::Const(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + } + } +} + +interned_vec_db!(GenericArgs, GenericArg); + +impl<'db> rustc_type_ir::inherent::GenericArg<DbInterner<'db>> for GenericArg<'db> {} + +impl<'db> GenericArgs<'db> { + /// Creates an `GenericArgs` for generic parameter definitions, + /// by calling closures to obtain each kind. + /// The closures get to observe the `GenericArgs` as they're + /// being built, which can be used to correctly + /// replace defaults of generic parameters. + pub fn for_item<F>( + interner: DbInterner<'db>, + def_id: SolverDefId, + mut mk_kind: F, + ) -> GenericArgs<'db> + where + F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let defs = interner.generics_of(def_id); + let count = defs.count(); + let mut args = SmallVec::with_capacity(count); + Self::fill_item(&mut args, interner, defs, &mut mk_kind); + interner.mk_args(&args) + } + + fn fill_item<F>( + args: &mut SmallVec<[GenericArg<'db>; 8]>, + interner: DbInterner<'_>, + defs: Generics, + mk_kind: &mut F, + ) where + F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let self_len = defs.own_params.len() as u32; + if let Some(def_id) = defs.parent { + let parent_defs = interner.generics_of(def_id.into()); + Self::fill_item(args, interner, parent_defs, mk_kind); + } + Self::fill_single(args, &defs, mk_kind); + } + + fn fill_single<F>(args: &mut SmallVec<[GenericArg<'db>; 8]>, defs: &Generics, mk_kind: &mut F) + where + F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let start_len = args.len(); + args.reserve(defs.own_params.len()); + for param in &defs.own_params { + let kind = mk_kind(¶m.name, args.len() as u32, param.kind, args); + args.push(kind); + } + } +} + +impl<'db> rustc_type_ir::relate::Relate<DbInterner<'db>> for GenericArgs<'db> { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + let interner = relation.cx(); + CollectAndApply::collect_and_apply( + std::iter::zip(a.iter(), b.iter()).map(|(a, b)| { + relation.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + a, + b, + ) + }), + |g| GenericArgs::new_from_iter(interner, g.iter().cloned()), + ) + } +} + +impl<'db> rustc_type_ir::inherent::GenericArgs<DbInterner<'db>> for GenericArgs<'db> { + fn as_closure(self) -> ClosureArgs<DbInterner<'db>> { + ClosureArgs { args: self } + } + fn as_coroutine(self) -> CoroutineArgs<DbInterner<'db>> { + CoroutineArgs { args: self } + } + fn as_coroutine_closure(self) -> CoroutineClosureArgs<DbInterner<'db>> { + CoroutineClosureArgs { args: self } + } + fn rebase_onto( + self, + interner: DbInterner<'db>, + source_def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId, + target: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs, + ) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs { + let defs = interner.generics_of(source_def_id); + interner.mk_args_from_iter(target.iter().chain(self.iter().skip(defs.count()))) + } + + fn identity_for_item( + interner: DbInterner<'db>, + def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId, + ) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs { + Self::for_item(interner, def_id, |name, index, kind, _| mk_param(index, name, kind)) + } + + fn extend_with_error( + interner: DbInterner<'db>, + def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId, + original_args: &[<DbInterner<'db> as rustc_type_ir::Interner>::GenericArg], + ) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs { + Self::for_item(interner, def_id, |name, index, kind, _| { + if let Some(arg) = original_args.get(index as usize) { + *arg + } else { + error_for_param_kind(kind, interner) + } + }) + } + fn type_at(self, i: usize) -> <DbInterner<'db> as rustc_type_ir::Interner>::Ty { + self.inner() + .get(i) + .and_then(|g| g.as_type()) + .unwrap_or_else(|| Ty::new_error(DbInterner::conjure(), ErrorGuaranteed)) + } + + fn region_at(self, i: usize) -> <DbInterner<'db> as rustc_type_ir::Interner>::Region { + self.inner() + .get(i) + .and_then(|g| g.as_region()) + .unwrap_or_else(|| Region::error(DbInterner::conjure())) + } + + fn const_at(self, i: usize) -> <DbInterner<'db> as rustc_type_ir::Interner>::Const { + self.inner() + .get(i) + .and_then(|g| g.as_const()) + .unwrap_or_else(|| Const::error(DbInterner::conjure())) + } + + fn split_closure_args(self) -> rustc_type_ir::ClosureArgsParts<DbInterner<'db>> { + // FIXME: should use `ClosureSubst` when possible + match self.inner().as_slice() { + [parent_args @ .., sig_ty] => { + let interner = DbInterner::conjure(); + // This is stupid, but the next solver expects the first input to actually be a tuple + let sig_ty = match sig_ty.expect_ty().kind() { + TyKind::FnPtr(sig_tys, header) => Ty::new( + interner, + TyKind::FnPtr( + sig_tys.map_bound(|s| { + let inputs = Ty::new_tup_from_iter(interner, s.inputs().iter()); + let output = s.output(); + FnSigTys { + inputs_and_output: Tys::new_from_iter( + interner, + [inputs, output], + ), + } + }), + header, + ), + ), + _ => unreachable!("sig_ty should be last"), + }; + rustc_type_ir::ClosureArgsParts { + parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), + closure_sig_as_fn_ptr_ty: sig_ty, + closure_kind_ty: Ty::new(interner, TyKind::Int(IntTy::I8)), + tupled_upvars_ty: Ty::new_unit(interner), + } + } + _ => { + unreachable!("unexpected closure sig"); + } + } + } + + fn split_coroutine_closure_args( + self, + ) -> rustc_type_ir::CoroutineClosureArgsParts<DbInterner<'db>> { + match self.inner().as_slice() { + [ + parent_args @ .., + closure_kind_ty, + signature_parts_ty, + tupled_upvars_ty, + coroutine_captures_by_ref_ty, + coroutine_witness_ty, + ] => rustc_type_ir::CoroutineClosureArgsParts { + parent_args: GenericArgs::new_from_iter( + DbInterner::conjure(), + parent_args.iter().cloned(), + ), + closure_kind_ty: closure_kind_ty.expect_ty(), + signature_parts_ty: signature_parts_ty.expect_ty(), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), + coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(), + coroutine_witness_ty: coroutine_witness_ty.expect_ty(), + }, + _ => panic!("GenericArgs were likely not for a CoroutineClosure."), + } + } + + fn split_coroutine_args(self) -> rustc_type_ir::CoroutineArgsParts<DbInterner<'db>> { + let interner = DbInterner::conjure(); + match self.inner().as_slice() { + [parent_args @ .., resume_ty, yield_ty, return_ty] => { + rustc_type_ir::CoroutineArgsParts { + parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), + kind_ty: Ty::new_unit(interner), + resume_ty: resume_ty.expect_ty(), + yield_ty: yield_ty.expect_ty(), + return_ty: return_ty.expect_ty(), + witness: Ty::new_unit(interner), + tupled_upvars_ty: Ty::new_unit(interner), + } + } + _ => panic!("GenericArgs were likely not for a Coroutine."), + } + } +} + +pub fn mk_param<'db>(index: u32, name: &Symbol, kind: GenericParamDefKind) -> GenericArg<'db> { + let name = name.clone(); + match kind { + GenericParamDefKind::Lifetime => { + Region::new_early_param(DbInterner::conjure(), EarlyParamRegion { index }).into() + } + GenericParamDefKind::Type => Ty::new_param(DbInterner::conjure(), index, name).into(), + GenericParamDefKind::Const => { + Const::new_param(DbInterner::conjure(), ParamConst { index }).into() + } + } +} + +pub fn error_for_param_kind<'db>( + kind: GenericParamDefKind, + interner: DbInterner<'db>, +) -> GenericArg<'db> { + match kind { + GenericParamDefKind::Lifetime => Region::error(interner).into(), + GenericParamDefKind::Type => Ty::new_error(interner, ErrorGuaranteed).into(), + GenericParamDefKind::Const => Const::error(interner).into(), + } +} + +impl<'db> IntoKind for Term<'db> { + type Kind = TermKind<DbInterner<'db>>; + + fn kind(self) -> Self::Kind { + match self { + Term::Ty(ty) => TermKind::Ty(ty), + Term::Const(c) => TermKind::Const(c), + } + } +} + +impl<'db> From<Ty<'db>> for Term<'db> { + fn from(value: Ty<'db>) -> Self { + Self::Ty(value) + } +} + +impl<'db> From<Const<'db>> for Term<'db> { + fn from(value: Const<'db>) -> Self { + Self::Const(value) + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for Term<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + match self { + Term::Ty(ty) => ty.visit_with(visitor), + Term::Const(ct) => ct.visit_with(visitor), + } + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for Term<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + match self.kind() { + TermKind::Ty(ty) => ty.try_fold_with(folder).map(Into::into), + TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), + } + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + match self.kind() { + TermKind::Ty(ty) => ty.fold_with(folder).into(), + TermKind::Const(ct) => ct.fold_with(folder).into(), + } + } +} + +impl<'db> Relate<DbInterner<'db>> for Term<'db> { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + match (a.kind(), b.kind()) { + (TermKind::Ty(a_ty), TermKind::Ty(b_ty)) => Ok(relation.relate(a_ty, b_ty)?.into()), + (TermKind::Const(a_ct), TermKind::Const(b_ct)) => { + Ok(relation.relate(a_ct, b_ct)?.into()) + } + (TermKind::Ty(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (TermKind::Const(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + } + } +} + +impl<'db> rustc_type_ir::inherent::Term<DbInterner<'db>> for Term<'db> {} + +#[derive(Clone, Eq, PartialEq, Debug)] +pub enum TermVid { + Ty(TyVid), + Const(ConstVid), +} + +impl From<TyVid> for TermVid { + fn from(value: TyVid) -> Self { + TermVid::Ty(value) + } +} + +impl From<ConstVid> for TermVid { + fn from(value: ConstVid) -> Self { + TermVid::Const(value) + } +} + +impl<'db> DbInterner<'db> { + pub(super) fn mk_args(self, args: &[GenericArg<'db>]) -> GenericArgs<'db> { + GenericArgs::new_from_iter(self, args.iter().cloned()) + } + + pub(super) fn mk_args_from_iter<I, T>(self, iter: I) -> T::Output + where + I: Iterator<Item = T>, + T: rustc_type_ir::CollectAndApply<GenericArg<'db>, GenericArgs<'db>>, + { + T::collect_and_apply(iter, |xs| self.mk_args(xs)) + } + + pub(super) fn check_args_compatible(self, def_id: SolverDefId, args: GenericArgs<'db>) -> bool { + // TODO + true + } + + pub(super) fn debug_assert_args_compatible(self, def_id: SolverDefId, args: GenericArgs<'db>) { + // TODO + } +}
diff --git a/crates/hir-ty/src/next_solver/generics.rs b/crates/hir-ty/src/next_solver/generics.rs new file mode 100644 index 0000000..48f5e73 --- /dev/null +++ b/crates/hir-ty/src/next_solver/generics.rs
@@ -0,0 +1,147 @@ +//! Things related to generics in the next-trait-solver. + +use hir_def::{ + ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId, Lookup, + TypeOrConstParamId, TypeParamId, + db::DefDatabase, + expr_store::ExpressionStore, + hir::generics::{ + GenericParamDataRef, GenericParams, LifetimeParamData, LocalLifetimeParamId, + LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamData, TypeParamProvenance, + WherePredicate, + }, +}; +use hir_expand::name::Name; +use intern::Symbol; +use la_arena::Arena; +use rustc_type_ir::inherent::Ty as _; +use triomphe::Arc; + +use crate::{db::HirDatabase, generics::parent_generic_def, next_solver::Ty}; + +use super::{Const, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId}; + +use super::{DbInterner, GenericArg}; + +pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { + let mk_lt = |(index, (_, lt)): (usize, (_, &LifetimeParamData))| { + let name = lt.name.symbol().clone(); + let index = index as u32; + let kind = GenericParamDefKind::Lifetime; + GenericParamDef { name, index, kind } + }; + let mk_ty = |len_lt, (index, p): (usize, &TypeOrConstParamData)| { + let name = p + .name() + .map(|n| n.symbol().clone()) + .unwrap_or_else(|| Name::missing().symbol().clone()); + let index = (len_lt + index) as u32; + let kind = match p { + TypeOrConstParamData::TypeParamData(_) => GenericParamDefKind::Type, + TypeOrConstParamData::ConstParamData(_) => GenericParamDefKind::Const, + }; + GenericParamDef { name, index, kind } + }; + let own_params_for_generic_params = |params: &GenericParams| { + if params.trait_self_param().is_some() { + let len_lt = params.len_lifetimes() + 1; + params + .iter_type_or_consts() + .take(1) + .enumerate() + .map(|t| mk_ty(0, (t.0, t.1.1))) + .chain(params.iter_lt().enumerate().map(mk_lt)) + .chain( + params + .iter_type_or_consts() + .skip(1) + .enumerate() + .map(|t| mk_ty(len_lt, (t.0, t.1.1))), + ) + .collect() + } else { + let len_lt = params.len_lifetimes(); + params + .iter_lt() + .enumerate() + .map(mk_lt) + .chain( + params.iter_type_or_consts().enumerate().map(|t| mk_ty(len_lt, (t.0, t.1.1))), + ) + .collect() + } + }; + + let (parent, own_params) = match (def.try_into(), def) { + (Ok(def), _) => { + (parent_generic_def(db, def), own_params_for_generic_params(&db.generic_params(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, _) => ( + None, + own_params_for_generic_params( + &db.generic_params(GenericDefId::TypeAliasId(type_alias_id)), + ), + ), + crate::ImplTraitId::AsyncBlockTypeImplTrait(def, _) => { + let param = TypeOrConstParamData::TypeParamData(TypeParamData { + name: None, + default: None, + provenance: TypeParamProvenance::TypeParamList, + }); + // Yes, there is a parent but we don't include it in the generics + (None, vec![mk_ty(0, (0, ¶m))]) + } + } + } + _ => panic!("No generics for {def:?}"), + }; + let parent_generics = parent.map(|def| Box::new(generics(db, def.into()))); + + Generics { + parent, + parent_count: parent_generics.map_or(0, |g| g.parent_count + g.own_params.len()), + own_params, + } +} + +#[derive(Debug)] +pub struct Generics { + pub parent: Option<GenericDefId>, + pub parent_count: usize, + pub own_params: Vec<GenericParamDef>, +} + +#[derive(Debug)] +pub struct GenericParamDef { + pub(crate) name: Symbol, + //def_id: GenericDefId, + index: u32, + pub(crate) kind: GenericParamDefKind, +} + +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 + } +} + +#[derive(Copy, Clone, Debug)] +pub enum GenericParamDefKind { + Lifetime, + Type, + Const, +} + +impl<'db> rustc_type_ir::inherent::GenericsOf<DbInterner<'db>> for Generics { + fn count(&self) -> usize { + self.parent_count + self.own_params.len() + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/at.rs b/crates/hir-ty/src/next_solver/infer/at.rs new file mode 100644 index 0000000..d64c7ed --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/at.rs
@@ -0,0 +1,333 @@ +//! A nice interface for working with the infcx. The basic idea is to +//! do `infcx.at(cause, param_env)`, which sets the "cause" of the +//! operation as well as the surrounding parameter environment. Then +//! you can do something like `.sub(a, b)` or `.eq(a, b)` to create a +//! subtype or equality relationship respectively. The first argument +//! is always the "expected" output from the POV of diagnostics. +//! +//! Examples: +//! ```ignore (fragment) +//! infcx.at(cause, param_env).sub(a, b) +//! // requires that `a <: b`, with `a` considered the "expected" type +//! +//! infcx.at(cause, param_env).sup(a, b) +//! // requires that `b <: a`, with `a` considered the "expected" type +//! +//! infcx.at(cause, param_env).eq(a, b) +//! // requires that `a == b`, with `a` considered the "expected" type +//! ``` +//! For finer-grained control, you can also do use `trace`: +//! ```ignore (fragment) +//! infcx.at(...).trace(a, b).sub(&c, &d) +//! ``` +//! This will set `a` and `b` as the "root" values for +//! error-reporting, but actually operate on `c` and `d`. This is +//! sometimes useful when the types of `c` and `d` are not traceable +//! things. (That system should probably be refactored.) + +use rustc_type_ir::{ + FnSig, GenericArgKind, TypingMode, Variance, + error::ExpectedFound, + inherent::{IntoKind, 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, + TraitRef, Ty, +}; + +use super::{ + InferCtxt, InferOk, InferResult, TypeTrace, ValuePairs, + traits::{Obligation, ObligationCause}, +}; + +/// Whether we should define opaque types or just treat them opaquely. +/// +/// Currently only used to prevent predicate matching from matching anything +/// against opaque types. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum DefineOpaqueTypes { + Yes, + No, +} + +#[derive(Clone, Copy)] +pub struct At<'a, 'db> { + pub infcx: &'a InferCtxt<'db>, + pub cause: &'a ObligationCause, + pub param_env: ParamEnv<'db>, +} + +impl<'db> InferCtxt<'db> { + #[inline] + pub fn at<'a>(&'a self, cause: &'a ObligationCause, param_env: ParamEnv<'db>) -> At<'a, 'db> { + At { infcx: self, cause, param_env } + } + + /// Forks the inference context, creating a new inference context with the same inference + /// variables in the same state. This can be used to "branch off" many tests from the same + /// common state. + pub fn fork(&self) -> Self { + Self { + interner: self.interner, + typing_mode: self.typing_mode, + inner: self.inner.clone(), + tainted_by_errors: self.tainted_by_errors.clone(), + universe: self.universe.clone(), + } + } + + /// Forks the inference context, creating a new inference context with the same inference + /// variables in the same state, except possibly changing the intercrate mode. This can be + /// used to "branch off" many tests from the same common state. Used in negative coherence. + pub fn fork_with_typing_mode(&self, typing_mode: TypingMode<DbInterner<'db>>) -> Self { + // Unlike `fork`, this invalidates all cache entries as they may depend on the + // typing mode. + + Self { + interner: self.interner, + typing_mode, + inner: self.inner.clone(), + tainted_by_errors: self.tainted_by_errors.clone(), + universe: self.universe.clone(), + } + } +} + +pub trait ToTrace<'db>: Relate<DbInterner<'db>> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db>; +} + +impl<'a, 'db> At<'a, 'db> { + /// Makes `actual <: expected`. For example, if type-checking a + /// call like `foo(x)`, where `foo: fn(i32)`, you might have + /// `sup(i32, x)`, since the "expected" type is the type that + /// appears in the signature. + pub fn sup<T>( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + RelateExt::relate( + self.infcx, + self.param_env, + expected, + Variance::Contravariant, + actual, + Span::dummy(), + ) + .map(|goals| self.goals_to_obligations(goals)) + } + + /// Makes `expected <: actual`. + pub fn sub<T>( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + RelateExt::relate( + self.infcx, + self.param_env, + expected, + Variance::Covariant, + actual, + Span::dummy(), + ) + .map(|goals| self.goals_to_obligations(goals)) + } + + /// Makes `expected == actual`. + pub fn eq<T>( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + self.eq_trace( + define_opaque_types, + ToTrace::to_trace(self.cause, expected, actual), + expected, + actual, + ) + } + + /// Makes `expected == actual`. + pub fn eq_trace<T>( + self, + define_opaque_types: DefineOpaqueTypes, + trace: TypeTrace<'db>, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: Relate<DbInterner<'db>>, + { + RelateExt::relate( + self.infcx, + self.param_env, + expected, + Variance::Invariant, + actual, + Span::dummy(), + ) + .map(|goals| self.goals_to_obligations(goals)) + } + + pub fn relate<T>( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + variance: Variance, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + match variance { + Variance::Covariant => self.sub(define_opaque_types, expected, actual), + Variance::Invariant => self.eq(define_opaque_types, expected, actual), + Variance::Contravariant => self.sup(define_opaque_types, expected, actual), + + // We could make this make sense but it's not readily + // exposed and I don't feel like dealing with it. Note + // that bivariance in general does a bit more than just + // *nothing*, it checks that the types are the same + // "modulo variance" basically. + Variance::Bivariant => panic!("Bivariant given to `relate()`"), + } + } + + fn goals_to_obligations(&self, goals: Vec<Goal<'db, Predicate<'db>>>) -> InferOk<'db, ()> { + InferOk { + value: (), + obligations: goals + .into_iter() + .map(|goal| { + Obligation::new( + self.infcx.interner, + self.cause.clone(), + goal.param_env, + goal.predicate, + ) + }) + .collect(), + } + } +} + +impl<'db> ToTrace<'db> for Ty<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } +} + +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)) } + } +} + +impl<'db> ToTrace<'db> for Const<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } +} + +impl<'db> ToTrace<'db> for GenericArg<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: match (a.kind(), b.kind()) { + (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => { + ValuePairs::Regions(ExpectedFound::new(a, b)) + } + (GenericArgKind::Type(a), GenericArgKind::Type(b)) => { + ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())) + } + (GenericArgKind::Const(a), GenericArgKind::Const(b)) => { + ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())) + } + _ => panic!("relating different kinds: {a:?} {b:?}"), + }, + } + } +} + +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)) } + } +} + +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)) } + } +} + +impl<'db> ToTrace<'db> for AliasTy<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())), + } + } +} + +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)) } + } +} + +impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))), + } + } +} + +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)) } + } +} + +impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)), + } + } +} + +impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)), + } + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs new file mode 100644 index 0000000..0448f03 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
@@ -0,0 +1,106 @@ +//! This module contains code to instantiate new values into a +//! `Canonical<'tcx, T>`. +//! +//! For an overview of what canonicalization is and how it fits into +//! rustc, check out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html + +use crate::next_solver::{ + AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal, + ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind, + fold::FnMutDelegate, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + traits::{Obligation, PredicateObligations}, + }, +}; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, BoundVar, GenericArgKind, InferTy, TypeFoldable, Upcast, + Variance, + inherent::{IntoKind, SliceLike}, + relate::{ + Relate, TypeRelation, VarianceDiagInfo, + combine::{super_combine_consts, super_combine_tys}, + }, +}; + +pub trait CanonicalExt<'db, V> { + fn instantiate(&self, tcx: DbInterner<'db>, var_values: &CanonicalVarValues<'db>) -> V + where + V: TypeFoldable<DbInterner<'db>>; + fn instantiate_projected<T>( + &self, + tcx: DbInterner<'db>, + var_values: &CanonicalVarValues<'db>, + projection_fn: impl FnOnce(&V) -> T, + ) -> T + where + T: TypeFoldable<DbInterner<'db>>; +} + +/// FIXME(-Znext-solver): This or public because it is shared with the +/// new trait solver implementation. We should deduplicate canonicalization. +impl<'db, V> CanonicalExt<'db, V> for Canonical<'db, V> { + /// Instantiate the wrapped value, replacing each canonical value + /// with the value given in `var_values`. + fn instantiate(&self, tcx: DbInterner<'db>, var_values: &CanonicalVarValues<'db>) -> V + where + V: TypeFoldable<DbInterner<'db>>, + { + self.instantiate_projected(tcx, var_values, |value| value.clone()) + } + + /// Allows one to apply a instantiation to some subset of + /// `self.value`. Invoke `projection_fn` with `self.value` to get + /// a value V that is expressed in terms of the same canonical + /// variables bound in `self` (usually this extracts from subset + /// of `self`). Apply the instantiation `var_values` to this value + /// V, replacing each of the canonical variables. + fn instantiate_projected<T>( + &self, + tcx: DbInterner<'db>, + var_values: &CanonicalVarValues<'db>, + projection_fn: impl FnOnce(&V) -> T, + ) -> T + where + T: TypeFoldable<DbInterner<'db>>, + { + assert_eq!(self.variables.len(), var_values.len()); + let value = projection_fn(&self.value); + instantiate_value(tcx, var_values, value) + } +} + +/// Instantiate the values from `var_values` into `value`. `var_values` +/// must be values for the set of canonical variables that appear in +/// `value`. +pub(super) fn instantiate_value<'db, T>( + tcx: DbInterner<'db>, + var_values: &CanonicalVarValues<'db>, + value: T, +) -> T +where + T: TypeFoldable<DbInterner<'db>>, +{ + if var_values.var_values.is_empty() { + value + } else { + let delegate = FnMutDelegate { + regions: &mut |br: BoundRegion| match var_values[br.var].kind() { + GenericArgKind::Lifetime(l) => l, + r => panic!("{br:?} is a region but value is {r:?}"), + }, + types: &mut |bound_ty: BoundTy| match var_values[bound_ty.var].kind() { + GenericArgKind::Type(ty) => ty, + r => panic!("{bound_ty:?} is a type but value is {r:?}"), + }, + consts: &mut |bound_ct: BoundVar| match var_values[bound_ct].kind() { + GenericArgKind::Const(ct) => ct, + c => panic!("{bound_ct:?} is a const but value is {c:?}"), + }, + }; + + tcx.replace_escaping_bound_vars_uncached(value, delegate) + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs new file mode 100644 index 0000000..4457e13 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
@@ -0,0 +1,156 @@ +//! **Canonicalization** is the key to constructing a query in the +//! middle of type inference. Ordinarily, it is not possible to store +//! types from type inference in query keys, because they contain +//! references to inference variables whose lifetimes are too short +//! and so forth. Canonicalizing a value T1 using `canonicalize_query` +//! produces two things: +//! +//! - a value T2 where each unbound inference variable has been +//! replaced with a **canonical variable**; +//! - a map M (of type `CanonicalVarValues`) from those canonical +//! variables back to the original. +//! +//! We can then do queries using T2. These will give back constraints +//! on the canonical variables which can be translated, using the map +//! M, into constraints in our source context. This process of +//! translating the results back is done by the +//! `instantiate_query_result` method. +//! +//! For a more detailed look at what is happening here, check +//! out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html + +use crate::next_solver::{ + AliasTy, Binder, Canonical, CanonicalVarValues, CanonicalVars, Const, DbInterner, GenericArg, + Goal, ParamEnv, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Predicate, PredicateKind, + Region, Ty, TyKind, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + traits::{Obligation, PredicateObligations}, + }, +}; +use instantiate::CanonicalExt; +use rustc_index::IndexVec; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, CanonicalTyVarKind, CanonicalVarKind, InferTy, + TypeFoldable, UniverseIndex, Upcast, Variance, + inherent::{SliceLike, Ty as _}, + relate::{ + Relate, TypeRelation, VarianceDiagInfo, + combine::{super_combine_consts, super_combine_tys}, + }, +}; + +pub mod instantiate; + +impl<'db> InferCtxt<'db> { + /// Creates an instantiation S for the canonical value with fresh inference + /// variables and placeholders then applies it to the canonical value. + /// Returns both the instantiated result *and* the instantiation S. + /// + /// This can be invoked as part of constructing an + /// inference context at the start of a query (see + /// `InferCtxtBuilder::build_with_canonical`). It basically + /// brings the canonical value "into scope" within your new infcx. + /// + /// At the end of processing, the instantiation S (once + /// canonicalized) then represents the values that you computed + /// for each of the canonical inputs to your query. + pub fn instantiate_canonical<T>( + &self, + canonical: &Canonical<'db, T>, + ) -> (T, CanonicalVarValues<'db>) + where + T: TypeFoldable<DbInterner<'db>>, + { + // For each universe that is referred to in the incoming + // query, create a universe in our local inference context. In + // practice, as of this writing, all queries have no universes + // in them, so this code has no effect, but it is looking + // forward to the day when we *do* want to carry universes + // through into queries. + // + // Instantiate the root-universe content into the current universe, + // and create fresh universes for the higher universes. + let universes: IndexVec<UniverseIndex, _> = std::iter::once(self.universe()) + .chain((1..=canonical.max_universe.as_u32()).map(|_| self.create_next_universe())) + .collect(); + + let canonical_inference_vars = + self.instantiate_canonical_vars(canonical.variables, |ui| universes[ui]); + let result = canonical.instantiate(self.interner, &canonical_inference_vars); + (result, canonical_inference_vars) + } + + /// Given the "infos" about the canonical variables from some + /// canonical, creates fresh variables with the same + /// characteristics (see `instantiate_canonical_var` for + /// details). You can then use `instantiate` to instantiate the + /// canonical variable with these inference variables. + fn instantiate_canonical_vars( + &self, + variables: CanonicalVars<'db>, + universe_map: impl Fn(UniverseIndex) -> UniverseIndex, + ) -> CanonicalVarValues<'db> { + CanonicalVarValues { + var_values: self.interner.mk_args_from_iter( + variables.iter().map(|info| self.instantiate_canonical_var(info, &universe_map)), + ), + } + } + + /// Given the "info" about a canonical variable, creates a fresh + /// variable for it. If this is an existentially quantified + /// variable, then you'll get a new inference variable; if it is a + /// universally quantified variable, you get a placeholder. + /// + /// FIXME(-Znext-solver): This is public because it's used by the + /// new trait solver which has a different canonicalization routine. + /// We should somehow deduplicate all of this. + pub fn instantiate_canonical_var( + &self, + cv_info: CanonicalVarKind<DbInterner<'db>>, + universe_map: impl Fn(UniverseIndex) -> UniverseIndex, + ) -> GenericArg<'db> { + match cv_info { + CanonicalVarKind::Ty(ty_kind) => { + let ty = match ty_kind { + CanonicalTyVarKind::General(ui) => { + self.next_ty_var_in_universe(universe_map(ui)) + } + + CanonicalTyVarKind::Int => self.next_int_var(), + + CanonicalTyVarKind::Float => self.next_float_var(), + }; + ty.into() + } + + CanonicalVarKind::PlaceholderTy(PlaceholderTy { universe, bound }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = PlaceholderTy { universe: universe_mapped, bound }; + Ty::new_placeholder(self.interner, placeholder_mapped).into() + } + + CanonicalVarKind::Region(ui) => { + self.next_region_var_in_universe(universe_map(ui)).into() + } + + CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped: crate::next_solver::Placeholder< + crate::next_solver::BoundRegion, + > = PlaceholderRegion { universe: universe_mapped, bound }; + Region::new_placeholder(self.interner, placeholder_mapped).into() + } + + CanonicalVarKind::Const(ui) => self.next_const_var_in_universe(universe_map(ui)).into(), + CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = PlaceholderConst { universe: universe_mapped, bound }; + Const::new_placeholder(self.interner, placeholder_mapped).into() + } + } + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/context.rs b/crates/hir-ty/src/next_solver/infer/context.rs new file mode 100644 index 0000000..93fd6ee --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/context.rs
@@ -0,0 +1,316 @@ +//! Definition of `InferCtxtLike` from the librarified type layer. + +use rustc_type_ir::{ + ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy, IntTy, IntVarValue, + IntVid, RegionVid, TyVid, TypeFoldable, TypingMode, UniverseIndex, + inherent::{Const as _, IntoKind, Span as _, Ty as _}, + relate::combine::PredicateEmittingRelation, +}; + +use crate::next_solver::{ + Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, ParamEnv, + Region, SolverDefId, Span, Ty, TyKind, + infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries}, +}; + +use super::{BoundRegionConversionTime, InferCtxt, relate::RelateResult, traits::ObligationCause}; + +impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { + type Interner = DbInterner<'db>; + + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn next_trait_solver(&self) -> bool { + true + } + + fn typing_mode(&self) -> TypingMode<DbInterner<'db>> { + self.typing_mode() + } + + fn universe(&self) -> UniverseIndex { + self.universe() + } + + fn create_next_universe(&self) -> UniverseIndex { + self.create_next_universe() + } + + fn universe_of_ty(&self, vid: TyVid) -> Option<UniverseIndex> { + self.probe_ty_var(vid).err() + } + + fn universe_of_lt(&self, lt: RegionVid) -> Option<UniverseIndex> { + self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt).err() + } + + fn universe_of_ct(&self, ct: ConstVid) -> Option<UniverseIndex> { + self.probe_const_var(ct).err() + } + + fn root_ty_var(&self, var: TyVid) -> TyVid { + self.root_var(var) + } + + fn root_const_var(&self, var: ConstVid) -> ConstVid { + self.root_const_var(var) + } + + fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> Ty<'db> { + match self.probe_ty_var(vid) { + Ok(ty) => ty, + Err(_) => Ty::new_var(self.interner, self.root_var(vid)), + } + } + + fn opportunistic_resolve_int_var(&self, vid: IntVid) -> Ty<'db> { + self.opportunistic_resolve_int_var(vid) + } + + fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> { + self.opportunistic_resolve_float_var(vid) + } + + fn opportunistic_resolve_ct_var(&self, vid: ConstVid) -> Const<'db> { + match self.probe_const_var(vid) { + Ok(ct) => ct, + Err(_) => Const::new_var(self.interner, self.root_const_var(vid)), + } + } + + fn opportunistic_resolve_lt_var(&self, vid: RegionVid) -> Region<'db> { + self.inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.interner, vid) + } + + fn is_changed_arg(&self, arg: <Self::Interner as rustc_type_ir::Interner>::GenericArg) -> bool { + match arg.kind() { + GenericArgKind::Lifetime(_) => { + // Lifetimes should not change affect trait selection. + false + } + GenericArgKind::Type(ty) => { + if let TyKind::Infer(infer_ty) = ty.kind() { + match infer_ty { + InferTy::TyVar(vid) => { + !self.probe_ty_var(vid).is_err_and(|_| self.root_var(vid) == vid) + } + InferTy::IntVar(vid) => { + let mut inner = self.inner.borrow_mut(); + !matches!( + inner.int_unification_table().probe_value(vid), + IntVarValue::Unknown + if inner.int_unification_table().find(vid) == vid + ) + } + InferTy::FloatVar(vid) => { + let mut inner = self.inner.borrow_mut(); + !matches!( + inner.float_unification_table().probe_value(vid), + FloatVarValue::Unknown + if inner.float_unification_table().find(vid) == vid + ) + } + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => { + true + } + } + } else { + true + } + } + GenericArgKind::Const(ct) => { + if let ConstKind::Infer(infer_ct) = ct.kind() { + match infer_ct { + InferConst::Var(vid) => !self + .probe_const_var(vid) + .is_err_and(|_| self.root_const_var(vid) == vid), + InferConst::Fresh(_) => true, + } + } else { + true + } + } + } + } + + fn next_ty_infer(&self) -> Ty<'db> { + self.next_ty_var() + } + + fn next_region_infer(&self) -> <Self::Interner as rustc_type_ir::Interner>::Region { + self.next_region_var() + } + + fn next_const_infer(&self) -> Const<'db> { + self.next_const_var() + } + + fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> { + self.fresh_args_for_item(def_id) + } + + fn instantiate_binder_with_infer<T: TypeFoldable<DbInterner<'db>> + Clone>( + &self, + value: Binder<'db, T>, + ) -> T { + self.instantiate_binder_with_fresh_vars(BoundRegionConversionTime::HigherRankedType, value) + } + + fn enter_forall<T: TypeFoldable<DbInterner<'db>> + Clone, U>( + &self, + value: Binder<'db, T>, + f: impl FnOnce(T) -> U, + ) -> U { + self.enter_forall(value, f) + } + + fn equate_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) { + self.inner.borrow_mut().type_variables().equate(a, b); + } + + fn equate_int_vids_raw(&self, a: rustc_type_ir::IntVid, b: rustc_type_ir::IntVid) { + self.inner.borrow_mut().int_unification_table().union(a, b); + } + + fn equate_float_vids_raw(&self, a: rustc_type_ir::FloatVid, b: rustc_type_ir::FloatVid) { + self.inner.borrow_mut().float_unification_table().union(a, b); + } + + fn equate_const_vids_raw(&self, a: rustc_type_ir::ConstVid, b: rustc_type_ir::ConstVid) { + self.inner.borrow_mut().const_unification_table().union(a, b); + } + + fn instantiate_ty_var_raw<R: PredicateEmittingRelation<Self>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: rustc_type_ir::TyVid, + instantiation_variance: rustc_type_ir::Variance, + source_ty: Ty<'db>, + ) -> RelateResult<'db, ()> { + self.instantiate_ty_var( + relation, + target_is_expected, + target_vid, + instantiation_variance, + source_ty, + ) + } + + fn instantiate_int_var_raw( + &self, + vid: rustc_type_ir::IntVid, + value: rustc_type_ir::IntVarValue, + ) { + self.inner.borrow_mut().int_unification_table().union_value(vid, value); + } + + fn instantiate_float_var_raw( + &self, + vid: rustc_type_ir::FloatVid, + value: rustc_type_ir::FloatVarValue, + ) { + self.inner.borrow_mut().float_unification_table().union_value(vid, value); + } + + fn instantiate_const_var_raw<R: PredicateEmittingRelation<Self>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: rustc_type_ir::ConstVid, + source_ct: Const<'db>, + ) -> RelateResult<'db, ()> { + self.instantiate_const_var(relation, target_is_expected, target_vid, source_ct) + } + + fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + self.set_tainted_by_errors(e) + } + + fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> { + self.shallow_resolve(ty) + } + fn shallow_resolve_const(&self, ct: Const<'db>) -> Const<'db> { + self.shallow_resolve_const(ct) + } + + fn resolve_vars_if_possible<T>(&self, value: T) -> T + where + T: TypeFoldable<DbInterner<'db>>, + { + self.resolve_vars_if_possible(value) + } + + fn probe<T>(&self, probe: impl FnOnce() -> T) -> T { + self.probe(|_| probe()) + } + + fn sub_regions(&self, sub: Region<'db>, sup: Region<'db>, 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) { + self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(a, b); + } + + fn register_ty_outlives(&self, ty: Ty<'db>, r: Region<'db>, span: Span) { + //self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(Span::dummy())); + } + + type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries; + + fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries { + self.inner.borrow_mut().opaque_types().num_entries() + } + fn clone_opaque_types_lookup_table(&self) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> { + self.inner.borrow_mut().opaque_types().iter_lookup_table().map(|(k, h)| (k, h.ty)).collect() + } + fn clone_duplicate_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> { + self.inner + .borrow_mut() + .opaque_types() + .iter_duplicate_entries() + .map(|(k, h)| (k, h.ty)) + .collect() + } + fn clone_opaque_types_added_since( + &self, + prev_entries: OpaqueTypeStorageEntries, + ) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> { + self.inner + .borrow_mut() + .opaque_types() + .opaque_types_added_since(prev_entries) + .map(|(k, h)| (k, h.ty)) + .collect() + } + + fn register_hidden_type_in_storage( + &self, + opaque_type_key: OpaqueTypeKey<'db>, + hidden_ty: Ty<'db>, + _span: Span, + ) -> Option<Ty<'db>> { + self.register_hidden_type_in_storage(opaque_type_key, OpaqueHiddenType { ty: hidden_ty }) + } + fn add_duplicate_opaque_type( + &self, + opaque_type_key: OpaqueTypeKey<'db>, + hidden_ty: Ty<'db>, + _span: Span, + ) { + self.inner + .borrow_mut() + .opaque_types() + .add_duplicate(opaque_type_key, OpaqueHiddenType { ty: hidden_ty }) + } + + fn reset_opaque_types(&self) { + let _ = self.take_opaque_types(); + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs new file mode 100644 index 0000000..19ae76f --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/mod.rs
@@ -0,0 +1,1066 @@ +//! Infer context the next-trait-solver. + +use std::cell::{Cell, RefCell}; +use std::fmt; +use std::sync::Arc; + +pub use BoundRegionConversionTime::*; +pub use at::DefineOpaqueTypes; +use ena::undo_log::UndoLogs; +use ena::unify as ut; +use intern::Symbol; +use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage}; +use region_constraints::{ + GenericKind, RegionConstraintCollector, RegionConstraintStorage, UndoLog, VarInfos, VerifyBound, +}; +pub use relate::StructurallyRelateAliases; +pub use relate::combine::PredicateEmittingRelation; +use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_pattern_analysis::Captures; +use rustc_type_ir::error::{ExpectedFound, TypeError}; +use rustc_type_ir::inherent::{ + Const as _, GenericArg as _, GenericArgs as _, IntoKind, ParamEnv as _, SliceLike, Term as _, + Ty as _, +}; +use rustc_type_ir::{ + BoundVar, ClosureKind, ConstVid, FloatTy, FloatVarValue, FloatVid, GenericArgKind, InferConst, + InferTy, IntTy, IntVarValue, IntVid, OutlivesPredicate, RegionVid, TyVid, UniverseIndex, +}; +use rustc_type_ir::{TermKind, TypeVisitableExt}; +use rustc_type_ir::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use snapshot::undo_log::InferCtxtUndoLogs; +use tracing::{debug, instrument}; +use traits::{ObligationCause, PredicateObligations}; +use type_variable::TypeVariableOrigin; +use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; + +use crate::next_solver::fold::BoundVarReplacerDelegate; +use crate::next_solver::infer::opaque_types::table::OpaqueTypeStorageEntries; +use crate::next_solver::{BoundRegion, BoundTy, BoundVarKind}; + +use super::generics::{GenericParamDef, GenericParamDefKind}; +use super::{ + AliasTerm, Binder, BoundRegionKind, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind, + DbInterner, ErrorGuaranteed, FxIndexMap, GenericArg, GenericArgs, OpaqueTypeKey, ParamEnv, + PlaceholderRegion, PolyCoercePredicate, PolyExistentialProjection, PolyExistentialTraitRef, + PolyFnSig, PolyRegionOutlivesPredicate, PolySubtypePredicate, Predicate, Region, SolverDefId, + SubtypePredicate, Term, TraitPredicate, TraitRef, Ty, TyKind, TypingMode, +}; + +pub mod at; +pub mod canonical; +mod context; +mod opaque_types; +pub mod region_constraints; +pub mod relate; +pub mod resolve; +pub(crate) mod snapshot; +pub(crate) mod traits; +mod type_variable; +mod unify_key; + +/// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper +/// around `PredicateObligations`, but it has one important property: +/// because `InferOk` is marked with `#[must_use]`, if you have a method +/// `InferCtxt::f` that returns `InferResult<()>` and you call it with +/// `infcx.f()?;` you'll get a warning about the obligations being discarded +/// without use, which is probably unintentional and has been a source of bugs +/// in the past. +#[must_use] +#[derive(Debug)] +pub struct InferOk<'db, T> { + pub value: T, + pub obligations: PredicateObligations<'db>, +} +pub type InferResult<'db, T> = Result<InferOk<'db, T>, TypeError<DbInterner<'db>>>; + +pub(crate) type FixupResult<T> = Result<T, FixupError>; // "fixup result" + +pub(crate) type UnificationTable<'a, 'db, T> = ut::UnificationTable< + ut::InPlace<T, &'a mut ut::UnificationStorage<T>, &'a mut InferCtxtUndoLogs<'db>>, +>; + +/// This type contains all the things within `InferCtxt` that sit within a +/// `RefCell` and are involved with taking/rolling back snapshots. Snapshot +/// operations are hot enough that we want only one call to `borrow_mut` per +/// call to `start_snapshot` and `rollback_to`. +#[derive(Clone)] +pub struct InferCtxtInner<'db> { + pub(crate) undo_log: InferCtxtUndoLogs<'db>, + + /// We instantiate `UnificationTable` with `bounds<Ty>` because the types + /// that might instantiate a general type variable have an order, + /// represented by its upper and lower bounds. + pub(crate) type_variable_storage: type_variable::TypeVariableStorage<'db>, + + /// Map from const parameter variable to the kind of const it represents. + pub(crate) const_unification_storage: ut::UnificationTableStorage<ConstVidKey<'db>>, + + /// Map from integral variable to the kind of integer it represents. + pub(crate) int_unification_storage: ut::UnificationTableStorage<IntVid>, + + /// Map from floating variable to the kind of float it represents. + pub(crate) float_unification_storage: ut::UnificationTableStorage<FloatVid>, + + /// Tracks the set of region variables and the constraints between them. + /// + /// This is initially `Some(_)` but when + /// `resolve_regions_and_report_errors` is invoked, this gets set to `None` + /// -- further attempts to perform unification, etc., may fail if new + /// region constraints would've been added. + pub(crate) region_constraint_storage: Option<RegionConstraintStorage<'db>>, + + /// A set of constraints that regionck must validate. + /// + /// Each constraint has the form `T:'a`, meaning "some type `T` must + /// outlive the lifetime 'a". These constraints derive from + /// instantiated type parameters. So if you had a struct defined + /// like the following: + /// ```ignore (illustrative) + /// struct Foo<T: 'static> { ... } + /// ``` + /// In some expression `let x = Foo { ... }`, it will + /// instantiate the type parameter `T` with a fresh type `$0`. At + /// the same time, it will record a region obligation of + /// `$0: 'static`. This will get checked later by regionck. (We + /// can't generally check these things right away because we have + /// to wait until types are resolved.) + /// + /// These are stored in a map keyed to the id of the innermost + /// enclosing fn body / static initializer expression. This is + /// because the location where the obligation was incurred can be + /// relevant with respect to which sublifetime assumptions are in + /// place. The reason that we store under the fn-id, and not + /// something more fine-grained, is so that it is easier for + /// regionck to be sure that it has found *all* the region + /// obligations (otherwise, it's easy to fail to walk to a + /// particular node-id). + /// + /// Before running `resolve_regions_and_report_errors`, the creator + /// of the inference context is expected to invoke + /// [`InferCtxt::process_registered_region_obligations`] + /// for each body-id in this map, which will process the + /// obligations within. This is expected to be done 'late enough' + /// that all type inference variables have been bound and so forth. + pub(crate) region_obligations: Vec<RegionObligation<'db>>, + + /// Caches for opaque type inference. + pub(crate) opaque_type_storage: OpaqueTypeStorage<'db>, +} + +impl<'db> InferCtxtInner<'db> { + fn new() -> InferCtxtInner<'db> { + InferCtxtInner { + undo_log: InferCtxtUndoLogs::default(), + + type_variable_storage: Default::default(), + const_unification_storage: Default::default(), + int_unification_storage: Default::default(), + float_unification_storage: Default::default(), + region_constraint_storage: Some(Default::default()), + region_obligations: vec![], + opaque_type_storage: Default::default(), + } + } + + #[inline] + pub fn region_obligations(&self) -> &[RegionObligation<'db>] { + &self.region_obligations + } + + #[inline] + fn try_type_variables_probe_ref( + &self, + vid: TyVid, + ) -> Option<&type_variable::TypeVariableValue<'db>> { + // Uses a read-only view of the unification table, this way we don't + // need an undo log. + self.type_variable_storage.eq_relations_ref().try_probe_value(vid) + } + + #[inline] + fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'db> { + self.type_variable_storage.with_log(&mut self.undo_log) + } + + #[inline] + pub(crate) fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'db> { + self.opaque_type_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn int_unification_table(&mut self) -> UnificationTable<'_, 'db, IntVid> { + self.int_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn float_unification_table(&mut self) -> UnificationTable<'_, 'db, FloatVid> { + self.float_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn const_unification_table(&mut self) -> UnificationTable<'_, 'db, ConstVidKey<'db>> { + self.const_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'db, '_> { + self.region_constraint_storage + .as_mut() + .expect("region constraints already solved") + .with_log(&mut self.undo_log) + } +} + +pub struct InferCtxt<'db> { + pub interner: DbInterner<'db>, + + /// The mode of this inference context, see the struct documentation + /// for more details. + typing_mode: TypingMode<'db>, + + pub inner: RefCell<InferCtxtInner<'db>>, + + /// When an error occurs, we want to avoid reporting "derived" + /// errors that are due to this original failure. We have this + /// flag that one can set whenever one creates a type-error that + /// is due to an error in a prior pass. + /// + /// Don't read this flag directly, call `is_tainted_by_errors()` + /// and `set_tainted_by_errors()`. + tainted_by_errors: Cell<Option<ErrorGuaranteed>>, + + /// What is the innermost universe we have created? Starts out as + /// `UniverseIndex::root()` but grows from there as we enter + /// universal quantifiers. + /// + /// N.B., at present, we exclude the universal quantifiers on the + /// item we are type-checking, and just consider those names as + /// part of the root universe. So this would only get incremented + /// when we enter into a higher-ranked (`for<..>`) type or trait + /// bound. + universe: Cell<UniverseIndex>, +} + +/// See the `error_reporting` module for more details. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ValuePairs<'db> { + Regions(ExpectedFound<Region<'db>>), + Terms(ExpectedFound<Term<'db>>), + Aliases(ExpectedFound<AliasTerm<'db>>), + TraitRefs(ExpectedFound<TraitRef<'db>>), + PolySigs(ExpectedFound<PolyFnSig<'db>>), + ExistentialTraitRef(ExpectedFound<PolyExistentialTraitRef<'db>>), + ExistentialProjection(ExpectedFound<PolyExistentialProjection<'db>>), +} + +impl<'db> ValuePairs<'db> { + pub fn ty(&self) -> Option<(Ty<'db>, Ty<'db>)> { + if let ValuePairs::Terms(ExpectedFound { expected, found }) = self + && let Some(expected) = expected.as_type() + && let Some(found) = found.as_type() + { + return Some((expected, found)); + } + None + } +} + +/// The trace designates the path through inference that we took to +/// encounter an error or subtyping constraint. +/// +/// See the `error_reporting` module for more details. +#[derive(Clone, Debug)] +pub struct TypeTrace<'db> { + pub cause: ObligationCause, + pub values: ValuePairs<'db>, +} + +/// Times when we replace bound regions with existentials: +#[derive(Clone, Copy, Debug)] +pub enum BoundRegionConversionTime { + /// when a fn is called + FnCall, + + /// when two higher-ranked types are compared + HigherRankedType, + + /// when projecting an associated type + AssocTypeProjection(SolverDefId), +} + +#[derive(Copy, Clone, Debug)] +pub struct FixupError { + unresolved: TyOrConstInferVar, +} + +impl fmt::Display for FixupError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use TyOrConstInferVar::*; + + match self.unresolved { + TyInt(_) => write!( + f, + "cannot determine the type of this integer; \ + add a suffix to specify the type explicitly" + ), + TyFloat(_) => write!( + f, + "cannot determine the type of this number; \ + add a suffix to specify the type explicitly" + ), + Ty(_) => write!(f, "unconstrained type"), + Const(_) => write!(f, "unconstrained const value"), + } + } +} + +/// See the `region_obligations` field for more information. +#[derive(Clone, Debug)] +pub struct RegionObligation<'db> { + pub sub_region: Region<'db>, + pub sup_type: Ty<'db>, +} + +/// Used to configure inference contexts before their creation. +pub struct InferCtxtBuilder<'db> { + interner: DbInterner<'db>, +} + +pub trait DbInternerInferExt<'db> { + fn infer_ctxt(self) -> InferCtxtBuilder<'db>; +} + +impl<'db> DbInternerInferExt<'db> for DbInterner<'db> { + fn infer_ctxt(self) -> InferCtxtBuilder<'db> { + InferCtxtBuilder { interner: self } + } +} + +impl<'db> InferCtxtBuilder<'db> { + /// Given a canonical value `C` as a starting point, create an + /// inference context that contains each of the bound values + /// within instantiated as a fresh variable. The `f` closure is + /// invoked with the new infcx, along with the instantiated value + /// `V` and a instantiation `S`. This instantiation `S` maps from + /// the bound values in `C` to their instantiated values in `V` + /// (in other words, `S(C) = V`). + pub fn build_with_canonical<T>( + mut self, + input: &CanonicalQueryInput<'db, T>, + ) -> (InferCtxt<'db>, T, CanonicalVarValues<'db>) + where + T: TypeFoldable<DbInterner<'db>>, + { + let infcx = self.build(input.typing_mode); + let (value, args) = infcx.instantiate_canonical(&input.canonical); + (infcx, value, args) + } + + pub fn build(&mut self, typing_mode: TypingMode<'db>) -> InferCtxt<'db> { + let InferCtxtBuilder { interner } = *self; + InferCtxt { + interner, + typing_mode, + inner: RefCell::new(InferCtxtInner::new()), + tainted_by_errors: Cell::new(None), + universe: Cell::new(UniverseIndex::ROOT), + } + } +} + +impl<'db> InferOk<'db, ()> { + pub fn into_obligations(self) -> PredicateObligations<'db> { + self.obligations + } +} + +impl<'db> InferCtxt<'db> { + #[inline(always)] + pub fn typing_mode(&self) -> TypingMode<'db> { + self.typing_mode + } + + #[inline(always)] + pub fn typing_mode_unchecked(&self) -> TypingMode<'db> { + self.typing_mode + } + + pub fn unresolved_variables(&self) -> Vec<Ty<'db>> { + let mut inner = self.inner.borrow_mut(); + let mut vars: Vec<Ty<'db>> = inner + .type_variables() + .unresolved_variables() + .into_iter() + .map(|t| Ty::new_var(self.interner, t)) + .collect(); + vars.extend( + (0..inner.int_unification_table().len()) + .map(IntVid::from_usize) + .filter(|&vid| inner.int_unification_table().probe_value(vid).is_unknown()) + .map(|v| Ty::new_int_var(self.interner, v)), + ); + vars.extend( + (0..inner.float_unification_table().len()) + .map(FloatVid::from_usize) + .filter(|&vid| inner.float_unification_table().probe_value(vid).is_unknown()) + .map(|v| Ty::new_float_var(self.interner, v)), + ); + vars + } + + #[instrument(skip(self), level = "debug")] + pub fn sub_regions(&self, a: Region<'db>, b: Region<'db>) { + self.inner.borrow_mut().unwrap_region_constraints().make_subregion(a, b); + } + + /// Processes a `Coerce` predicate from the fulfillment context. + /// This is NOT the preferred way to handle coercion, which is to + /// invoke `FnCtxt::coerce` or a similar method (see `coercion.rs`). + /// + /// This method here is actually a fallback that winds up being + /// invoked when `FnCtxt::coerce` encounters unresolved type variables + /// and records a coercion predicate. Presently, this method is equivalent + /// to `subtype_predicate` -- that is, "coercing" `a` to `b` winds up + /// actually requiring `a <: b`. This is of course a valid coercion, + /// but it's not as flexible as `FnCtxt::coerce` would be. + /// + /// (We may refactor this in the future, but there are a number of + /// practical obstacles. Among other things, `FnCtxt::coerce` presently + /// records adjustments that are required on the HIR in order to perform + /// the coercion, and we don't currently have a way to manage that.) + pub fn coerce_predicate( + &self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + predicate: PolyCoercePredicate<'db>, + ) -> Result<InferResult<'db, ()>, (TyVid, TyVid)> { + let subtype_predicate = predicate.map_bound(|p| SubtypePredicate { + a_is_expected: false, // when coercing from `a` to `b`, `b` is expected + a: p.a, + b: p.b, + }); + self.subtype_predicate(cause, param_env, subtype_predicate) + } + + pub fn subtype_predicate( + &self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + predicate: PolySubtypePredicate<'db>, + ) -> Result<InferResult<'db, ()>, (TyVid, TyVid)> { + // Check for two unresolved inference variables, in which case we can + // make no progress. This is partly a micro-optimization, but it's + // also an opportunity to "sub-unify" the variables. This isn't + // *necessary* to prevent cycles, because they would eventually be sub-unified + // anyhow during generalization, but it helps with diagnostics (we can detect + // earlier that they are sub-unified). + // + // Note that we can just skip the binders here because + // type variables can't (at present, at + // least) capture any of the things bound by this binder. + // + // Note that this sub here is not just for diagnostics - it has semantic + // effects as well. + let r_a = self.shallow_resolve(predicate.skip_binder().a); + let r_b = self.shallow_resolve(predicate.skip_binder().b); + match (r_a.kind(), r_b.kind()) { + (TyKind::Infer(InferTy::TyVar(a_vid)), TyKind::Infer(InferTy::TyVar(b_vid))) => { + return Err((a_vid, b_vid)); + } + _ => {} + } + + self.enter_forall(predicate, |SubtypePredicate { a_is_expected, a, b }| { + if a_is_expected { + Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::Yes, a, b)) + } else { + Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::Yes, b, a)) + } + }) + } + + pub fn region_outlives_predicate( + &self, + cause: &traits::ObligationCause, + predicate: PolyRegionOutlivesPredicate<'db>, + ) { + self.enter_forall(predicate, |OutlivesPredicate(r_a, r_b)| { + self.sub_regions(r_b, r_a); // `b : a` ==> `a <= b` + }) + } + + /// Number of type variables created so far. + pub fn num_ty_vars(&self) -> usize { + self.inner.borrow_mut().type_variables().num_vars() + } + + pub fn next_ty_var(&self) -> Ty<'db> { + self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None }) + } + + 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); + 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) -> Const<'db> { + self.next_const_var_with_origin(ConstVariableOrigin { param_def_id: None }) + } + + 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 { param_def_id: None }; + let vid = self + .inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin, universe }) + .vid; + 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) + } + + pub fn next_float_var(&self) -> Ty<'db> { + let next_float_var_id = + self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown); + Ty::new_float_var(self.interner, next_float_var_id) + } + + /// 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()) + } + + /// 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> { + let region_var = + self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe); + Region::new_var(self.interner, region_var) + } + + pub fn next_term_var_of_kind(&self, term: Term<'db>) -> Term<'db> { + match term.kind() { + TermKind::Ty(_) => self.next_ty_var().into(), + TermKind::Const(_) => self.next_const_var().into(), + } + } + + /// Return the universe that the region `r` was created in. For + /// most regions (e.g., `'static`, named regions from the user, + /// etc) this is the root universe U0. For inference variables or + /// placeholders, however, it will return the universe which they + /// are associated. + pub fn universe_of_region(&self, r: Region<'db>) -> UniverseIndex { + self.inner.borrow_mut().unwrap_region_constraints().universe(r) + } + + /// Number of region variables created so far. + pub fn num_region_vars(&self) -> usize { + 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, kind: GenericParamDefKind, name: &Symbol) -> GenericArg<'db> { + match kind { + GenericParamDefKind::Lifetime => { + // Create a region inference variable for the given + // region parameter definition. + self.next_region_var().into() + } + GenericParamDefKind::Type => { + // Create a type inference variable for the given + // type parameter definition. The generic parameters are + // for actual parameters that may be referred to by + // the default of this type parameter, if it exists. + // e.g., `struct Foo<A, B, C = (A, B)>(...);` when + // used in a path such as `Foo::<T, U>::new()` will + // use an inference variable for `C` with `[T, U]` + // as the generic parameters for the default, `(T, U)`. + let ty_var_id = self + .inner + .borrow_mut() + .type_variables() + .new_var(self.universe(), TypeVariableOrigin { param_def_id: None }); + + Ty::new_var(self.interner, ty_var_id).into() + } + GenericParamDefKind::Const => { + let origin = ConstVariableOrigin { param_def_id: None }; + 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() + } + } + } + + /// 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, |name, index, kind, _| { + self.var_for_def(kind, name) + }) + } + + /// Returns `true` if errors have been reported since this infcx was + /// created. This is sometimes used as a heuristic to skip + /// reporting errors that often occur as a result of earlier + /// errors, but where it's hard to be 100% sure (e.g., unresolved + /// inference variables, regionck errors). + #[must_use = "this method does not have any side effects"] + pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> { + self.tainted_by_errors.get() + } + + /// Set the "tainted by errors" flag to true. We call this when we + /// observe an error from a prior pass. + pub fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + debug!("set_tainted_by_errors(ErrorGuaranteed)"); + self.tainted_by_errors.set(Some(e)); + } + + #[instrument(level = "debug", skip(self), ret)] + pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect() + } + + #[instrument(level = "debug", skip(self), ret)] + pub fn clone_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect() + } + + #[inline(always)] + pub fn can_define_opaque_ty(&self, id: impl Into<SolverDefId>) -> bool { + match self.typing_mode_unchecked() { + TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators.contains(&id.into()) + } + TypingMode::Coherence | TypingMode::PostAnalysis => false, + TypingMode::Borrowck { defining_opaque_types } => unimplemented!(), + TypingMode::PostBorrowckAnalysis { defined_opaque_types } => unimplemented!(), + } + } + + /// If `TyVar(vid)` resolves to a type, return that type. Else, return the + /// universe index of `TyVar(vid)`. + pub fn probe_ty_var(&self, vid: TyVid) -> Result<Ty<'db>, UniverseIndex> { + use self::type_variable::TypeVariableValue; + + match self.inner.borrow_mut().type_variables().probe(vid) { + TypeVariableValue::Known { value } => Ok(value), + TypeVariableValue::Unknown { universe } => Err(universe), + } + } + + pub fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> { + if let TyKind::Infer(v) = ty.kind() { + match v { + InferTy::TyVar(v) => { + // Not entirely obvious: if `typ` is a type variable, + // it can be resolved to an int/float variable, which + // can then be recursively resolved, hence the + // recursion. Note though that we prevent type + // variables from unifying to other type variables + // directly (though they may be embedded + // structurally), and we prevent cycles in any case, + // so this recursion should always be of very limited + // depth. + // + // Note: if these two lines are combined into one we get + // dynamic borrow errors on `self.inner`. + let known = self.inner.borrow_mut().type_variables().probe(v).known(); + known.map_or(ty, |t| self.shallow_resolve(t)) + } + + InferTy::IntVar(v) => { + match self.inner.borrow_mut().int_unification_table().probe_value(v) { + IntVarValue::IntType(ty) => Ty::new_int(self.interner, ty), + IntVarValue::UintType(ty) => Ty::new_uint(self.interner, ty), + IntVarValue::Unknown => ty, + } + } + + InferTy::FloatVar(v) => { + match self.inner.borrow_mut().float_unification_table().probe_value(v) { + FloatVarValue::Known(ty) => Ty::new_float(self.interner, ty), + FloatVarValue::Unknown => ty, + } + } + + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => ty, + } + } else { + ty + } + } + + pub fn shallow_resolve_const(&self, ct: Const<'db>) -> Const<'db> { + match ct.kind() { + ConstKind::Infer(infer_ct) => match infer_ct { + InferConst::Var(vid) => self + .inner + .borrow_mut() + .const_unification_table() + .probe_value(vid) + .known() + .unwrap_or(ct), + InferConst::Fresh(_) => ct, + }, + ConstKind::Param(_) + | ConstKind::Bound(_, _) + | ConstKind::Placeholder(_) + | ConstKind::Unevaluated(_) + | ConstKind::Value(_) + | ConstKind::Error(_) + | ConstKind::Expr(_) => ct, + } + } + + pub fn root_var(&self, var: TyVid) -> TyVid { + self.inner.borrow_mut().type_variables().root_var(var) + } + + pub fn root_const_var(&self, var: ConstVid) -> ConstVid { + self.inner.borrow_mut().const_unification_table().find(var).vid + } + + /// Resolves an int var to a rigid int type, if it was constrained to one, + /// or else the root int var in the unification table. + pub fn opportunistic_resolve_int_var(&self, vid: IntVid) -> Ty<'db> { + let mut inner = self.inner.borrow_mut(); + let value = inner.int_unification_table().probe_value(vid); + match value { + IntVarValue::IntType(ty) => Ty::new_int(self.interner, ty), + IntVarValue::UintType(ty) => Ty::new_uint(self.interner, ty), + IntVarValue::Unknown => { + Ty::new_int_var(self.interner, inner.int_unification_table().find(vid)) + } + } + } + + /// Resolves a float var to a rigid int type, if it was constrained to one, + /// or else the root float var in the unification table. + pub fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> { + let mut inner = self.inner.borrow_mut(); + let value = inner.float_unification_table().probe_value(vid); + match value { + FloatVarValue::Known(ty) => Ty::new_float(self.interner, ty), + FloatVarValue::Unknown => { + Ty::new_float_var(self.interner, inner.float_unification_table().find(vid)) + } + } + } + + /// Where possible, replaces type/const variables in + /// `value` with their final value. Note that region variables + /// are unaffected. If a type/const variable has not been unified, it + /// is left as is. This is an idempotent operation that does + /// not affect inference state in any way and so you can do it + /// at will. + pub fn resolve_vars_if_possible<T>(&self, value: T) -> T + where + T: TypeFoldable<DbInterner<'db>>, + { + if let Err(guar) = value.error_reported() { + self.set_tainted_by_errors(guar); + } + if !value.has_non_region_infer() { + return value; + } + let mut r = resolve::OpportunisticVarResolver::new(self); + value.fold_with(&mut r) + } + + pub fn probe_const_var(&self, vid: ConstVid) -> Result<Const<'db>, UniverseIndex> { + match self.inner.borrow_mut().const_unification_table().probe_value(vid) { + ConstVariableValue::Known { value } => Ok(value), + ConstVariableValue::Unknown { origin: _, universe } => Err(universe), + } + } + + // Instantiates the bound variables in a given binder with fresh inference + // variables in the current universe. + // + // Use this method if you'd like to find some generic parameters of the binder's + // variables (e.g. during a method call). If there isn't a [`BoundRegionConversionTime`] + // that corresponds to your use case, consider whether or not you should + // use [`InferCtxt::enter_forall`] instead. + pub fn instantiate_binder_with_fresh_vars<T>( + &self, + lbrct: BoundRegionConversionTime, + value: Binder<'db, T>, + ) -> T + where + T: TypeFoldable<DbInterner<'db>> + Clone, + { + if let Some(inner) = value.clone().no_bound_vars() { + return inner; + } + + let bound_vars = value.clone().bound_vars(); + let mut args = Vec::with_capacity(bound_vars.len()); + + for bound_var_kind in bound_vars { + let arg: GenericArg<'db> = match bound_var_kind { + BoundVarKind::Ty(_) => self.next_ty_var().into(), + BoundVarKind::Region(br) => self.next_region_var().into(), + BoundVarKind::Const => self.next_const_var().into(), + }; + args.push(arg); + } + + struct ToFreshVars<'db> { + args: Vec<GenericArg<'db>>, + } + + impl<'db> BoundVarReplacerDelegate<'db> for ToFreshVars<'db> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + self.args[br.var.index()].expect_region() + } + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + self.args[bt.var.index()].expect_ty() + } + fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + self.args[bv.index()].expect_const() + } + } + let delegate = ToFreshVars { args }; + self.interner.replace_bound_vars_uncached(value, delegate) + } + + /// Obtains the latest type of the given closure; this may be a + /// closure in the current function, in which case its + /// `ClosureKind` may not yet be known. + pub fn closure_kind(&self, closure_ty: Ty<'db>) -> Option<ClosureKind> { + let unresolved_kind_ty = match closure_ty.kind() { + TyKind::Closure(_, args) => args.as_closure().kind_ty(), + TyKind::CoroutineClosure(_, args) => args.as_coroutine_closure().kind_ty(), + _ => panic!("unexpected type {closure_ty:?}"), + }; + let closure_kind_ty = self.shallow_resolve(unresolved_kind_ty); + closure_kind_ty.to_opt_closure_kind() + } + + pub fn universe(&self) -> UniverseIndex { + self.universe.get() + } + + /// Creates and return a fresh universe that extends all previous + /// universes. Updates `self.universe` to that new universe. + pub fn create_next_universe(&self) -> UniverseIndex { + let u = self.universe.get().next_universe(); + debug!("create_next_universe {u:?}"); + self.universe.set(u); + u + } + + /// The returned function is used in a fast path. If it returns `true` the variable is + /// unchanged, `false` indicates that the status is unknown. + #[inline] + pub fn is_ty_infer_var_definitely_unchanged<'a>( + &'a self, + ) -> (impl Fn(TyOrConstInferVar) -> bool + Captures<'db> + 'a) { + // This hoists the borrow/release out of the loop body. + let inner = self.inner.try_borrow(); + + move |infer_var: TyOrConstInferVar| match (infer_var, &inner) { + (TyOrConstInferVar::Ty(ty_var), Ok(inner)) => { + use self::type_variable::TypeVariableValue; + + matches!( + inner.try_type_variables_probe_ref(ty_var), + Some(TypeVariableValue::Unknown { .. }) + ) + } + _ => false, + } + } + + /// `ty_or_const_infer_var_changed` is equivalent to one of these two: + /// * `shallow_resolve(ty) != ty` (where `ty.kind = Infer(_)`) + /// * `shallow_resolve(ct) != ct` (where `ct.kind = ConstKind::Infer(_)`) + /// + /// However, `ty_or_const_infer_var_changed` is more efficient. It's always + /// inlined, despite being large, because it has only two call sites that + /// are extremely hot (both in `traits::fulfill`'s checking of `stalled_on` + /// inference variables), and it handles both `Ty` and `Const` without + /// having to resort to storing full `GenericArg`s in `stalled_on`. + #[inline(always)] + pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar) -> bool { + match infer_var { + TyOrConstInferVar::Ty(v) => { + use self::type_variable::TypeVariableValue; + + // If `inlined_probe` returns a `Known` value, it never equals + // `Infer(TyVar(v))`. + match self.inner.borrow_mut().type_variables().inlined_probe(v) { + TypeVariableValue::Unknown { .. } => false, + TypeVariableValue::Known { .. } => true, + } + } + + TyOrConstInferVar::TyInt(v) => { + // If `inlined_probe_value` returns a value it's always a + // `Int(_)` or `UInt(_)`, which never matches a + // `Infer(_)`. + self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_known() + } + + TyOrConstInferVar::TyFloat(v) => { + // If `probe_value` returns a value it's always a + // `Float(_)`, which never matches a `Infer(_)`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + self.inner.borrow_mut().float_unification_table().probe_value(v).is_known() + } + + TyOrConstInferVar::Const(v) => { + // If `probe_value` returns a `Known` value, it never equals + // `ConstKind::Infer(InferConst::Var(v))`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + match self.inner.borrow_mut().const_unification_table().probe_value(v) { + ConstVariableValue::Unknown { .. } => false, + ConstVariableValue::Known { .. } => true, + } + } + } + } +} + +/// Helper for [InferCtxt::ty_or_const_infer_var_changed] (see comment on that), currently +/// used only for `traits::fulfill`'s list of `stalled_on` inference variables. +#[derive(Copy, Clone, Debug)] +pub enum TyOrConstInferVar { + /// Equivalent to `Infer(TyVar(_))`. + Ty(TyVid), + /// Equivalent to `Infer(IntVar(_))`. + TyInt(IntVid), + /// Equivalent to `Infer(FloatVar(_))`. + TyFloat(FloatVid), + + /// Equivalent to `ConstKind::Infer(InferConst::Var(_))`. + Const(ConstVid), +} + +impl TyOrConstInferVar { + /// Tries to extract an inference variable from a type or a constant, returns `None` + /// for types other than `Infer(_)` (or `InferTy::Fresh*`) and + /// for constants other than `ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_generic_arg<'db>(arg: GenericArg<'db>) -> Option<Self> { + match arg.kind() { + GenericArgKind::Type(ty) => Self::maybe_from_ty(ty), + GenericArgKind::Const(ct) => Self::maybe_from_const(ct), + GenericArgKind::Lifetime(_) => None, + } + } + + /// Tries to extract an inference variable from a type, returns `None` + /// for types other than `Infer(_)` (or `InferTy::Fresh*`). + fn maybe_from_ty<'db>(ty: Ty<'db>) -> Option<Self> { + match ty.kind() { + TyKind::Infer(InferTy::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)), + TyKind::Infer(InferTy::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)), + TyKind::Infer(InferTy::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)), + _ => None, + } + } + + /// Tries to extract an inference variable from a constant, returns `None` + /// for constants other than `ConstKind::Infer(_)` (or `InferConst::Fresh`). + fn maybe_from_const<'db>(ct: Const<'db>) -> Option<Self> { + match ct.kind() { + ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), + _ => None, + } + } +} + +impl<'db> TypeTrace<'db> { + pub fn types(cause: &ObligationCause, a: Ty<'db>, b: Ty<'db>) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } + + pub fn trait_refs( + cause: &ObligationCause, + a: TraitRef<'db>, + b: TraitRef<'db>, + ) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } + } + + pub fn consts(cause: &ObligationCause, a: Const<'db>, b: Const<'db>) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } +} + +/// Requires that `region` must be equal to one of the regions in `choice_regions`. +/// We often denote this using the syntax: +/// +/// ```text +/// R0 member of [O1..On] +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct MemberConstraint<'db> { + /// The `DefId` and args of the opaque type causing this constraint. + /// Used for error reporting. + pub key: OpaqueTypeKey<'db>, + + /// The hidden type in which `member_region` appears: used for error reporting. + pub hidden_ty: Ty<'db>, + + /// The region `R0`. + pub member_region: Region<'db>, + + /// The options `O1..On`. + pub choice_regions: Arc<Vec<Region<'db>>>, +}
diff --git a/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs b/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs new file mode 100644 index 0000000..0f68ec8 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs
@@ -0,0 +1,56 @@ +//! Things related to the infer context of the next-trait-solver. + +use std::sync::Arc; + +use tracing::{debug, instrument}; + +use crate::next_solver::{ + Clause, ClauseKind, FxIndexMap, GenericArgs, OpaqueTypeKey, ProjectionPredicate, SolverDefId, + TypingMode, util::BottomUpFolder, +}; + +pub(crate) mod table; + +pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable}; + +use crate::next_solver::{ + AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal, + ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind, + fold::FnMutDelegate, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + traits::{Obligation, PredicateObligations}, + }, +}; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, BoundConstness, BoundVar, Flags, GenericArgKind, InferTy, + Interner, RegionKind, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, Upcast, Variance, + error::{ExpectedFound, TypeError}, + inherent::{DefId, GenericArgs as _, IntoKind, SliceLike}, + relate::{ + Relate, TypeRelation, VarianceDiagInfo, + combine::{super_combine_consts, super_combine_tys}, + }, +}; + +use super::{InferOk, traits::ObligationCause}; + +#[derive(Copy, Clone, Debug)] +pub struct OpaqueHiddenType<'db> { + pub ty: Ty<'db>, +} + +impl<'db> InferCtxt<'db> { + /// Insert a hidden type into the opaque type storage, making sure + /// it hasn't previously been defined. This does not emit any + /// constraints and it's the responsibility of the caller to make + /// sure that the item bounds of the opaque are checked. + pub fn register_hidden_type_in_storage( + &self, + opaque_type_key: OpaqueTypeKey<'db>, + hidden_ty: OpaqueHiddenType<'db>, + ) -> Option<Ty<'db>> { + self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty) + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs b/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs new file mode 100644 index 0000000..8ab409d --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs
@@ -0,0 +1,166 @@ +//! Things related to storage opaques in the infer context of the next-trait-solver. + +use std::ops::Deref; + +use ena::undo_log::UndoLogs; +use tracing::instrument; + +use super::OpaqueHiddenType; +use crate::next_solver::{ + FxIndexMap, OpaqueTypeKey, Ty, + infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog}, +}; + +#[derive(Default, Debug, Clone)] +pub(crate) struct OpaqueTypeStorage<'db> { + opaque_types: FxIndexMap<OpaqueTypeKey<'db>, OpaqueHiddenType<'db>>, + duplicate_entries: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>, +} + +/// The number of entries in the opaque type storage at a given point. +/// +/// Used to check that we haven't added any new opaque types after checking +/// the opaque types currently in the storage. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub struct OpaqueTypeStorageEntries { + opaque_types: usize, + duplicate_entries: usize, +} + +impl rustc_type_ir::inherent::OpaqueTypeStorageEntries for OpaqueTypeStorageEntries { + fn needs_reevaluation(self, canonicalized: usize) -> bool { + self.opaque_types != canonicalized + } +} + +impl<'db> OpaqueTypeStorage<'db> { + #[instrument(level = "debug")] + pub(crate) fn remove(&mut self, key: OpaqueTypeKey<'db>, prev: Option<OpaqueHiddenType<'db>>) { + if let Some(prev) = prev { + *self.opaque_types.get_mut(&key).unwrap() = prev; + } else { + // FIXME(#120456) - is `swap_remove` correct? + match self.opaque_types.swap_remove(&key) { + None => { + panic!("reverted opaque type inference that was never registered: {key:?}") + } + Some(_) => {} + } + } + } + + pub(crate) fn pop_duplicate_entry(&mut self) { + let entry = self.duplicate_entries.pop(); + assert!(entry.is_some()); + } + + pub fn is_empty(&self) -> bool { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + opaque_types.is_empty() && duplicate_entries.is_empty() + } + + pub(crate) fn take_opaque_types( + &mut self, + ) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries)) + } + + pub fn num_entries(&self) -> OpaqueTypeStorageEntries { + OpaqueTypeStorageEntries { + opaque_types: self.opaque_types.len(), + duplicate_entries: self.duplicate_entries.len(), + } + } + + pub fn opaque_types_added_since( + &self, + prev_entries: OpaqueTypeStorageEntries, + ) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + self.opaque_types + .iter() + .skip(prev_entries.opaque_types) + .map(|(k, v)| (*k, *v)) + .chain(self.duplicate_entries.iter().skip(prev_entries.duplicate_entries).copied()) + } + + /// Only returns the opaque types from the lookup table. These are used + /// when normalizing opaque types and have a unique key. + /// + /// Outside of canonicalization one should generally use `iter_opaque_types` + /// to also consider duplicate entries. + pub fn iter_lookup_table( + &self, + ) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + self.opaque_types.iter().map(|(k, v)| (*k, *v)) + } + + /// Only returns the opaque types which are stored in `duplicate_entries`. + /// + /// These have to considered when checking all opaque type uses but are e.g. + /// irrelevant for canonical inputs as nested queries never meaningfully + /// accesses them. + pub fn iter_duplicate_entries( + &self, + ) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + self.duplicate_entries.iter().copied() + } + + pub fn iter_opaque_types( + &self, + ) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + opaque_types.iter().map(|(k, v)| (*k, *v)).chain(duplicate_entries.iter().copied()) + } + + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'db>, + ) -> OpaqueTypeTable<'a, 'db> { + OpaqueTypeTable { storage: self, undo_log } + } +} + +impl<'db> Drop for OpaqueTypeStorage<'db> { + fn drop(&mut self) { + if !self.opaque_types.is_empty() { + panic!("{:?}", self.opaque_types) + } + } +} + +pub(crate) struct OpaqueTypeTable<'a, 'db> { + storage: &'a mut OpaqueTypeStorage<'db>, + + undo_log: &'a mut InferCtxtUndoLogs<'db>, +} +impl<'db> Deref for OpaqueTypeTable<'_, 'db> { + type Target = OpaqueTypeStorage<'db>; + fn deref(&self) -> &Self::Target { + self.storage + } +} + +impl<'a, 'db> OpaqueTypeTable<'a, 'db> { + #[instrument(skip(self), level = "debug")] + pub fn register( + &mut self, + key: OpaqueTypeKey<'db>, + hidden_type: OpaqueHiddenType<'db>, + ) -> Option<Ty<'db>> { + if let Some(entry) = self.storage.opaque_types.get_mut(&key) { + let prev = std::mem::replace(entry, hidden_type); + self.undo_log.push(UndoLog::OpaqueTypes(key, Some(prev))); + return Some(prev.ty); + } + self.storage.opaque_types.insert(key, hidden_type); + self.undo_log.push(UndoLog::OpaqueTypes(key, None)); + None + } + + pub fn add_duplicate(&mut self, key: OpaqueTypeKey<'db>, hidden_type: OpaqueHiddenType<'db>) { + self.storage.duplicate_entries.push((key, hidden_type)); + self.undo_log.push(UndoLog::DuplicateOpaqueType); + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs new file mode 100644 index 0000000..5054969 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs
@@ -0,0 +1,640 @@ +//! See `README.md`. + +use std::ops::Range; +use std::sync::Arc; +use std::{cmp, fmt, mem}; + +use ena::undo_log::{Rollback, UndoLogs}; +use ena::unify as ut; +use rustc_hash::FxHashMap; +use rustc_index::IndexVec; +use rustc_type_ir::inherent::IntoKind; +use rustc_type_ir::{RegionKind, RegionVid, UniverseIndex}; +use tracing::{debug, instrument}; + +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, OpaqueTypeKey, ParamTy, PlaceholderTy, Region, Ty, +}; + +#[derive(Clone, Default)] +pub struct RegionConstraintStorage<'db> { + /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. + pub(super) var_infos: IndexVec<RegionVid, RegionVariableInfo>, + + pub(super) data: RegionConstraintData<'db>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their LUB (edges R1 <= R3 and R2 <= R3 + /// exist). This prevents us from making many such regions. + lubs: CombineMap<'db>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their GLB (edges R3 <= R1 and R3 <= R2 + /// exist). This prevents us from making many such regions. + glbs: CombineMap<'db>, + + /// When we add a R1 == R2 constraint, we currently add (a) edges + /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this + /// table. You can then call `opportunistic_resolve_var` early + /// which will map R1 and R2 to some common region (i.e., either + /// R1 or R2). This is important when fulfillment, dropck and other such + /// code is iterating to a fixed point, because otherwise we sometimes + /// would wind up with a fresh stream of region variables that have been + /// equated but appear distinct. + pub(super) unification_table: ut::UnificationTableStorage<RegionVidKey<'db>>, + + /// a flag set to true when we perform any unifications; this is used + /// to micro-optimize `take_and_reset_data` + any_unifications: bool, +} + +pub struct RegionConstraintCollector<'db, 'a> { + storage: &'a mut RegionConstraintStorage<'db>, + undo_log: &'a mut InferCtxtUndoLogs<'db>, +} + +pub type VarInfos = IndexVec<RegionVid, RegionVariableInfo>; + +/// The full set of region constraints gathered up by the collector. +/// Describes constraints between the region variables and other +/// regions, as well as other conditions that must be verified, or +/// assumptions that can be made. +#[derive(Debug, Default, Clone)] +pub struct RegionConstraintData<'db> { + /// Constraints of the form `A <= B`, where either `A` or `B` can + /// be a region variable (or neither, as it happens). + pub constraints: Vec<Constraint<'db>>, + + /// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that + /// `R0` must be equal to one of the regions `R1..Rn`. These occur + /// with `impl Trait` quite frequently. + pub member_constraints: Vec<MemberConstraint<'db>>, + + /// A "verify" is something that we need to verify after inference + /// is done, but which does not directly affect inference in any + /// way. + /// + /// An example is a `A <= B` where neither `A` nor `B` are + /// inference variables. + pub verifys: Vec<Verify<'db>>, +} + +/// Represents a constraint that influences the inference process. +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum Constraint<'db> { + /// A region variable is a subregion of another. + VarSubVar(RegionVid, RegionVid), + + /// A concrete region is a subregion of region variable. + RegSubVar(Region<'db>, RegionVid), + + /// A region variable is a subregion of a concrete region. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + VarSubReg(RegionVid, Region<'db>), + + /// A constraint where neither side is a variable. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + RegSubReg(Region<'db>, Region<'db>), +} + +impl<'db> Constraint<'db> { + pub fn involves_placeholders(&self) -> bool { + match self { + Constraint::VarSubVar(_, _) => false, + Constraint::VarSubReg(_, r) | Constraint::RegSubVar(r, _) => r.is_placeholder(), + Constraint::RegSubReg(r, s) => r.is_placeholder() || s.is_placeholder(), + } + } +} + +#[derive(Debug, Clone)] +pub struct Verify<'db> { + pub kind: GenericKind<'db>, + pub region: Region<'db>, + pub bound: VerifyBound<'db>, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum GenericKind<'db> { + Param(ParamTy), + Placeholder(PlaceholderTy), + Alias(AliasTy<'db>), +} + +/// Describes the things that some `GenericKind` value `G` is known to +/// outlive. Each variant of `VerifyBound` can be thought of as a +/// function: +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { .. } +/// ``` +/// where `true` means that the region `min` meets that `G: min`. +/// (False means nothing.) +/// +/// So, for example, if we have the type `T` and we have in scope that +/// `T: 'a` and `T: 'b`, then the verify bound might be: +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { +/// ('a: min) || ('b: min) +/// } +/// ``` +/// This is described with an `AnyRegion('a, 'b)` node. +#[derive(Debug, Clone)] +pub enum VerifyBound<'db> { + /// See [`VerifyIfEq`] docs + IfEq(Binder<'db, VerifyIfEq<'db>>), + + /// Given a region `R`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// R: min + /// } + /// ``` + /// + /// This is used when we can establish that `G: R` -- therefore, + /// if `R: min`, then by transitivity `G: min`. + OutlivedBy(Region<'db>), + + /// Given a region `R`, true if it is `'empty`. + IsEmpty, + + /// Given a set of bounds `B`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// exists (b in B) { b(min) } + /// } + /// ``` + /// + /// In other words, if we meet some bound in `B`, that suffices. + /// This is used when all the bounds in `B` are known to apply to `G`. + AnyBound(Vec<VerifyBound<'db>>), + + /// Given a set of bounds `B`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// forall (b in B) { b(min) } + /// } + /// ``` + /// + /// In other words, if we meet *all* bounds in `B`, that suffices. + /// This is used when *some* bound in `B` is known to suffice, but + /// we don't know which. + AllBounds(Vec<VerifyBound<'db>>), +} + +/// This is a "conditional bound" that checks the result of inference +/// and supplies a bound if it ended up being relevant. It's used in situations +/// like this: +/// +/// ```rust,ignore (pseudo-Rust) +/// fn foo<'a, 'b, T: SomeTrait<'a>> +/// where +/// <T as SomeTrait<'a>>::Item: 'b +/// ``` +/// +/// If we have an obligation like `<T as SomeTrait<'?x>>::Item: 'c`, then +/// we don't know yet whether it suffices to show that `'b: 'c`. If `'?x` winds +/// up being equal to `'a`, then the where-clauses on function applies, and +/// in that case we can show `'b: 'c`. But if `'?x` winds up being something +/// else, the bound isn't relevant. +/// +/// In the [`VerifyBound`], this struct is enclosed in `Binder` to account +/// for cases like +/// +/// ```rust,ignore (pseudo-Rust) +/// where for<'a> <T as SomeTrait<'a>::Item: 'a +/// ``` +/// +/// The idea is that we have to find some instantiation of `'a` that can +/// make `<T as SomeTrait<'a>>::Item` equal to the final value of `G`, +/// the generic we are checking. +/// +/// ```ignore (pseudo-rust) +/// fn(min) -> bool { +/// exists<'a> { +/// if G == K { +/// B(min) +/// } else { +/// false +/// } +/// } +/// } +/// ``` +#[derive(Debug, Clone)] +pub struct VerifyIfEq<'db> { + /// Type which must match the generic `G` + pub ty: Ty<'db>, + + /// Bound that applies if `ty` is equal. + pub bound: Region<'db>, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub(crate) struct TwoRegions<'db> { + a: Region<'db>, + b: Region<'db>, +} + +#[derive(Clone, PartialEq)] +pub(crate) enum UndoLog<'db> { + /// We added `RegionVid`. + AddVar(RegionVid), + + /// We added the given `constraint`. + AddConstraint(usize), + + /// We added the given `verify`. + AddVerify(usize), + + /// We added a GLB/LUB "combination variable". + AddCombination(CombineMapType, TwoRegions<'db>), +} + +#[derive(Clone, PartialEq)] +pub(crate) enum CombineMapType { + Lub, + Glb, +} + +type CombineMap<'db> = FxHashMap<TwoRegions<'db>, RegionVid>; + +#[derive(Debug, Clone)] +pub struct RegionVariableInfo { + // FIXME: This is only necessary for `fn take_and_reset_data` and + // `lexical_region_resolve`. We should rework `lexical_region_resolve` + // in the near/medium future anyways and could move the unverse info + // for `fn take_and_reset_data` into a separate table which is + // only populated when needed. + // + // For both of these cases it is fine that this can diverge from the + // actual universe of the variable, which is directly stored in the + // unification table for unknown region variables. At some point we could + // stop emitting bidirectional outlives constraints if equate succeeds. + // This would be currently unsound as it would cause us to drop the universe + // changes in `lexical_region_resolve`. + pub universe: UniverseIndex, +} + +pub(crate) struct RegionSnapshot { + any_unifications: bool, +} + +impl<'db> RegionConstraintStorage<'db> { + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'db>, + ) -> RegionConstraintCollector<'db, 'a> { + RegionConstraintCollector { storage: self, undo_log } + } +} + +impl<'db> RegionConstraintCollector<'db, '_> { + pub fn num_region_vars(&self) -> usize { + self.storage.var_infos.len() + } + + pub fn region_constraint_data(&self) -> &RegionConstraintData<'db> { + &self.storage.data + } + + /// Takes (and clears) the current set of constraints. Note that + /// the set of variables remains intact, but all relationships + /// between them are reset. This is used during NLL checking to + /// grab the set of constraints that arose from a particular + /// operation. + /// + /// We don't want to leak relationships between variables between + /// points because just because (say) `r1 == r2` was true at some + /// point P in the graph doesn't imply that it will be true at + /// some other point Q, in NLL. + /// + /// Not legal during a snapshot. + pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'db> { + assert!(!UndoLogs::<UndoLog<'db>>::in_snapshot(&self.undo_log)); + + // If you add a new field to `RegionConstraintCollector`, you + // should think carefully about whether it needs to be cleared + // or updated in some way. + let RegionConstraintStorage { + var_infos: _, + data, + lubs, + glbs, + unification_table: _, + any_unifications, + } = self.storage; + + // Clear the tables of (lubs, glbs), so that we will create + // fresh regions if we do a LUB operation. As it happens, + // LUB/GLB are not performed by the MIR type-checker, which is + // the one that uses this method, but it's good to be correct. + lubs.clear(); + glbs.clear(); + + let data = mem::take(data); + + // Clear all unifications and recreate the variables a "now + // un-unified" state. Note that when we unify `a` and `b`, we + // also insert `a <= b` and a `b <= a` edges, so the + // `RegionConstraintData` contains the relationship here. + if *any_unifications { + *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, + }); + } + + data + } + + pub fn data(&self) -> &RegionConstraintData<'db> { + &self.storage.data + } + + pub(super) fn start_snapshot(&self) -> RegionSnapshot { + debug!("RegionConstraintCollector: start_snapshot"); + RegionSnapshot { any_unifications: self.storage.any_unifications } + } + + pub(super) fn rollback_to(&mut self, snapshot: RegionSnapshot) { + debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); + 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 }); + + let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe }); + assert_eq!(vid, u_vid.vid); + self.undo_log.push(AddVar(vid)); + debug!("created new region variable {:?} in {:?}", vid, universe); + vid + } + + fn add_constraint(&mut self, constraint: Constraint<'db>) { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: add_constraint({:?})", constraint); + + let index = self.storage.data.constraints.len(); + self.storage.data.constraints.push(constraint); + self.undo_log.push(AddConstraint(index)); + } + + pub(super) fn make_eqregion(&mut self, a: Region<'db>, b: Region<'db>) { + if a != b { + // Eventually, it would be nice to add direct support for + // equating regions. + self.make_subregion(a, b); + self.make_subregion(b, a); + + match (a.kind(), b.kind()) { + (RegionKind::ReVar(a), RegionKind::ReVar(b)) => { + debug!("make_eqregion: unifying {:?} with {:?}", a, b); + if self.unification_table_mut().unify_var_var(a, b).is_ok() { + self.storage.any_unifications = true; + } + } + (RegionKind::ReVar(vid), _) => { + debug!("make_eqregion: unifying {:?} with {:?}", vid, b); + if self + .unification_table_mut() + .unify_var_value(vid, RegionVariableValue::Known { value: b }) + .is_ok() + { + self.storage.any_unifications = true; + }; + } + (_, RegionKind::ReVar(vid)) => { + debug!("make_eqregion: unifying {:?} with {:?}", a, vid); + if self + .unification_table_mut() + .unify_var_value(vid, RegionVariableValue::Known { value: a }) + .is_ok() + { + self.storage.any_unifications = true; + }; + } + (_, _) => {} + } + } + } + + #[instrument(skip(self), level = "debug")] + pub(super) fn make_subregion(&mut self, sub: Region<'db>, sup: Region<'db>) { + // cannot add constraints once regions are resolved + + match (sub.kind(), sup.kind()) { + (RegionKind::ReBound(..), _) | (_, RegionKind::ReBound(..)) => { + panic!("cannot relate bound region: {sub:?} <= {sup:?}"); + } + (_, RegionKind::ReStatic) => { + // all regions are subregions of static, so we can ignore this + } + (RegionKind::ReVar(sub_id), RegionKind::ReVar(sup_id)) => { + self.add_constraint(Constraint::VarSubVar(sub_id, sup_id)); + } + (_, RegionKind::ReVar(sup_id)) => { + self.add_constraint(Constraint::RegSubVar(sub, sup_id)); + } + (RegionKind::ReVar(sub_id), _) => { + self.add_constraint(Constraint::VarSubReg(sub_id, sup)); + } + _ => { + self.add_constraint(Constraint::RegSubReg(sub, sup)); + } + } + } + + /// Resolves a region var to its value in the unification table, if it exists. + /// Otherwise, it is resolved to the root `ReVar` in the table. + pub fn opportunistic_resolve_var( + &mut self, + cx: DbInterner<'db>, + vid: RegionVid, + ) -> Region<'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::Unknown { .. } => Region::new_var(cx, root_vid), + } + } + + pub fn probe_value(&mut self, vid: RegionVid) -> Result<Region<'db>, UniverseIndex> { + match self.unification_table_mut().probe_value(vid) { + RegionVariableValue::Known { value } => Ok(value), + RegionVariableValue::Unknown { universe } => Err(universe), + } + } + + fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'db> { + match t { + Glb => &mut self.storage.glbs, + Lub => &mut self.storage.lubs, + } + } + + fn combine_vars( + &mut self, + cx: DbInterner<'db>, + t: CombineMapType, + a: Region<'db>, + b: Region<'db>, + ) -> Region<'db> { + let vars = TwoRegions { a, b }; + if let Some(c) = self.combine_map(t.clone()).get(&vars) { + return Region::new_var(cx, *c); + } + 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); + 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); + for old_r in [a, b] { + match t { + Glb => self.make_subregion(new_r, old_r), + Lub => self.make_subregion(old_r, new_r), + } + } + debug!("combine_vars() c={:?}", c); + new_r + } + + pub fn universe(&mut self, region: Region<'db>) -> UniverseIndex { + match region.kind() { + RegionKind::ReStatic + | RegionKind::ReErased + | RegionKind::ReLateParam(..) + | RegionKind::ReEarlyParam(..) + | RegionKind::ReError(_) => UniverseIndex::ROOT, + RegionKind::RePlaceholder(placeholder) => placeholder.universe, + RegionKind::ReVar(vid) => match self.probe_value(vid) { + Ok(value) => self.universe(value), + Err(universe) => universe, + }, + RegionKind::ReBound(..) => panic!("universe(): encountered bound region {region:?}"), + } + } + + #[inline] + fn unification_table_mut(&mut self) -> super::UnificationTable<'_, 'db, RegionVidKey<'db>> { + ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log) + } +} + +impl fmt::Debug for RegionSnapshot { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RegionSnapshot") + } +} + +impl<'db> fmt::Debug for GenericKind<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{p:?}"), + GenericKind::Placeholder(ref p) => write!(f, "{p:?}"), + GenericKind::Alias(ref p) => write!(f, "{p:?}"), + } + } +} + +impl<'db> fmt::Display for GenericKind<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{p:?}"), + GenericKind::Placeholder(ref p) => write!(f, "{p:?}"), + GenericKind::Alias(ref p) => write!(f, "{p}"), + } + } +} + +impl<'db> GenericKind<'db> { + pub fn to_ty(&self, interner: DbInterner<'db>) -> Ty<'db> { + match *self { + GenericKind::Param(ref p) => (*p).to_ty(interner), + GenericKind::Placeholder(ref p) => Ty::new_placeholder(interner, *p), + GenericKind::Alias(ref p) => (*p).to_ty(interner), + } + } +} + +impl<'db> VerifyBound<'db> { + pub fn must_hold(&self) -> bool { + match self { + VerifyBound::IfEq(..) => false, + VerifyBound::OutlivedBy(re) => re.is_static(), + VerifyBound::IsEmpty => false, + VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), + VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()), + } + } + + pub fn cannot_hold(&self) -> bool { + match self { + VerifyBound::IfEq(..) => false, + VerifyBound::IsEmpty => false, + VerifyBound::OutlivedBy(_) => false, + VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), + VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()), + } + } + + pub fn or(self, vb: VerifyBound<'db>) -> VerifyBound<'db> { + if self.must_hold() || vb.cannot_hold() { + self + } else if self.cannot_hold() || vb.must_hold() { + vb + } else { + VerifyBound::AnyBound(vec![self, vb]) + } + } +} + +impl<'db> RegionConstraintData<'db> { + /// Returns `true` if this region constraint data contains no constraints, and `false` + /// otherwise. + pub fn is_empty(&self) -> bool { + let RegionConstraintData { constraints, member_constraints, verifys } = self; + constraints.is_empty() && member_constraints.is_empty() && verifys.is_empty() + } +} + +impl<'db> Rollback<UndoLog<'db>> for RegionConstraintStorage<'db> { + fn reverse(&mut self, undo: UndoLog<'db>) { + match undo { + AddVar(vid) => { + self.var_infos.pop().unwrap(); + assert_eq!(self.var_infos.len(), vid.index()); + } + AddConstraint(index) => { + self.data.constraints.pop().unwrap(); + assert_eq!(self.data.constraints.len(), index); + } + AddVerify(index) => { + self.data.verifys.pop(); + assert_eq!(self.data.verifys.len(), index); + } + AddCombination(Glb, ref regions) => { + self.glbs.remove(regions); + } + AddCombination(Lub, ref regions) => { + self.lubs.remove(regions); + } + } + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs new file mode 100644 index 0000000..de336c6 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
@@ -0,0 +1,720 @@ +//! Type generation code. + +use std::mem; + +use rustc_hash::FxHashMap; +use rustc_type_ir::error::TypeError; +use rustc_type_ir::inherent::{Const as _, IntoKind, Ty as _}; +use rustc_type_ir::relate::VarianceDiagInfo; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, ConstVid, InferConst, InferCtxtLike, InferTy, RegionKind, + TermKind, TyVid, UniverseIndex, Variance, +}; +use rustc_type_ir::{Interner, TypeVisitable, TypeVisitableExt}; +use tracing::{debug, instrument, warn}; + +use super::{ + PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation, +}; +use crate::next_solver::infer::type_variable::TypeVariableValue; +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::{ + AliasTy, Binder, ClauseKind, Const, ConstKind, DbInterner, GenericArgs, PredicateKind, + ProjectionPredicate, Region, SolverDefId, Term, TermVid, Ty, TyKind, TypingMode, + UnevaluatedConst, +}; + +impl<'db> InferCtxt<'db> { + /// The idea is that we should ensure that the type variable `target_vid` + /// is equal to, a subtype of, or a supertype of `source_ty`. + /// + /// For this, we will instantiate `target_vid` with a *generalized* version + /// of `source_ty`. Generalization introduces other inference variables wherever + /// subtyping could occur. This also does the occurs checks, detecting whether + /// instantiating `target_vid` would result in a cyclic type. We eagerly error + /// in this case. + /// + /// This is *not* expected to be used anywhere except for an implementation of + /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all + /// other usecases (i.e. setting the value of a type var). + #[instrument(level = "debug", skip(self, relation))] + pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'db>>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: TyVid, + instantiation_variance: Variance, + source_ty: Ty<'db>, + ) -> RelateResult<'db, ()> { + debug_assert!(self.inner.borrow_mut().type_variables().probe(target_vid).is_unknown()); + + // Generalize `source_ty` depending on the current variance. As an example, assume + // `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference + // variable. + // + // Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh + // region/type inference variables. + // + // We then relate `generalized_ty <: source_ty`, adding constraints like `'x: '?2` and + // `?1 <: ?3`. + let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = self + .generalize( + relation.structurally_relate_aliases(), + target_vid, + instantiation_variance, + source_ty, + )?; + + // Constrain `b_vid` to the generalized type `generalized_ty`. + if let TyKind::Infer(InferTy::TyVar(generalized_vid)) = generalized_ty.kind() { + self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid); + } else { + self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty); + } + + // See the comment on `Generalization::has_unconstrained_ty_var`. + if has_unconstrained_ty_var { + relation.register_predicates([ClauseKind::WellFormed(generalized_ty.into())]); + } + + // Finally, relate `generalized_ty` to `source_ty`, as described in previous comment. + // + // FIXME(#16847): This code is non-ideal because all these subtype + // relations wind up attributed to the same spans. We need + // to associate causes/spans with each of the relations in + // the stack to get this right. + if generalized_ty.is_ty_var() { + // This happens for cases like `<?0 as Trait>::Assoc == ?0`. + // We can't instantiate `?0` here as that would result in a + // cyclic type. We instead delay the unification in case + // the alias can be normalized to something which does not + // mention `?0`. + let (lhs, rhs, direction) = match instantiation_variance { + Variance::Invariant => { + (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate) + } + Variance::Covariant => { + (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Subtype) + } + Variance::Contravariant => { + (source_ty.into(), generalized_ty.into(), AliasRelationDirection::Subtype) + } + Variance::Bivariant => unreachable!("bivariant generalization"), + }; + + relation.register_predicates([PredicateKind::AliasRelate(lhs, rhs, direction)]); + } else { + // NOTE: The `instantiation_variance` is not the same variance as + // used by the relation. When instantiating `b`, `target_is_expected` + // is flipped and the `instantiation_variance` is also flipped. To + // constrain the `generalized_ty` while using the original relation, + // we therefore only have to flip the arguments. + // + // ```ignore (not code) + // ?a rel B + // instantiate_ty_var(?a, B) # expected and variance not flipped + // B' rel B + // ``` + // or + // ```ignore (not code) + // A rel ?b + // instantiate_ty_var(?b, A) # expected and variance flipped + // A rel A' + // ``` + if target_is_expected { + relation.relate(generalized_ty, source_ty)?; + } else { + debug!("flip relation"); + relation.relate(source_ty, generalized_ty)?; + } + } + + Ok(()) + } + + /// Instantiates the const variable `target_vid` with the given constant. + /// + /// This also tests if the given const `ct` contains an inference variable which was previously + /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` + /// would result in an infinite type as we continuously replace an inference variable + /// in `ct` with `ct` itself. + /// + /// This is especially important as unevaluated consts use their parents generics. + /// They therefore often contain unused args, making these errors far more likely. + /// + /// A good example of this is the following: + /// + /// ```compile_fail,E0308 + /// #![feature(generic_const_exprs)] + /// + /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] { + /// todo!() + /// } + /// + /// fn main() { + /// let mut arr = Default::default(); + /// arr = bind(arr); + /// } + /// ``` + /// + /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// of `fn bind` (meaning that its args contain `N`). + /// + /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. + /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. + /// + /// As `3 + 4` contains `N` in its args, this must not succeed. + /// + /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. + #[instrument(level = "debug", skip(self, relation))] + pub(crate) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'db>>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: ConstVid, + source_ct: Const<'db>, + ) -> RelateResult<'db, ()> { + // FIXME(generic_const_exprs): Occurs check failures for unevaluated + // constants and generic expressions are not yet handled correctly. + let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } = self + .generalize( + relation.structurally_relate_aliases(), + target_vid, + Variance::Invariant, + source_ct, + )?; + + debug_assert!(!generalized_ct.is_ct_infer()); + if has_unconstrained_ty_var { + panic!("unconstrained ty var when generalizing `{source_ct:?}`"); + } + + self.inner + .borrow_mut() + .const_unification_table() + .union_value(target_vid, ConstVariableValue::Known { value: generalized_ct }); + + // Make sure that the order is correct when relating the + // generalized const and the source. + if target_is_expected { + relation.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + generalized_ct, + source_ct, + )?; + } else { + relation.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + source_ct, + generalized_ct, + )?; + } + + Ok(()) + } + + /// Attempts to generalize `source_term` for the type variable `target_vid`. + /// This checks for cycles -- that is, whether `source_term` references `target_vid`. + fn generalize<T: Into<Term<'db>> + Relate<DbInterner<'db>>>( + &self, + structurally_relate_aliases: StructurallyRelateAliases, + target_vid: impl Into<TermVid>, + ambient_variance: Variance, + source_term: T, + ) -> RelateResult<'db, Generalization<T>> { + assert!(!source_term.clone().has_escaping_bound_vars()); + let (for_universe, root_vid) = match target_vid.into() { + TermVid::Ty(ty_vid) => { + (self.probe_ty_var(ty_vid).unwrap_err(), TermVid::Ty(self.root_var(ty_vid))) + } + TermVid::Const(ct_vid) => ( + self.probe_const_var(ct_vid).unwrap_err(), + TermVid::Const(self.inner.borrow_mut().const_unification_table().find(ct_vid).vid), + ), + }; + + let mut generalizer = Generalizer { + infcx: self, + structurally_relate_aliases, + root_vid, + for_universe, + root_term: source_term.into(), + ambient_variance, + in_alias: false, + cache: Default::default(), + has_unconstrained_ty_var: false, + }; + + let value_may_be_infer = generalizer.relate(source_term, source_term)?; + let has_unconstrained_ty_var = generalizer.has_unconstrained_ty_var; + Ok(Generalization { value_may_be_infer, has_unconstrained_ty_var }) + } +} + +/// The "generalizer" is used when handling inference variables. +/// +/// The basic strategy for handling a constraint like `?A <: B` is to +/// apply a "generalization strategy" to the term `B` -- this replaces +/// all the lifetimes in the term `B` with fresh inference variables. +/// (You can read more about the strategy in this [blog post].) +/// +/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x +/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the +/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which +/// establishes `'0: 'x` as a constraint. +/// +/// [blog post]: https://is.gd/0hKvIr +struct Generalizer<'me, 'db> { + infcx: &'me InferCtxt<'db>, + + /// Whether aliases should be related structurally. If not, we have to + /// be careful when generalizing aliases. + structurally_relate_aliases: StructurallyRelateAliases, + + /// The vid of the type variable that is in the process of being + /// instantiated. If we find this within the value we are folding, + /// that means we would have created a cyclic value. + root_vid: TermVid, + + /// The universe of the type variable that is in the process of being + /// instantiated. If we find anything that this universe cannot name, + /// we reject the relation. + for_universe: UniverseIndex, + + /// The root term (const or type) we're generalizing. Used for cycle errors. + root_term: Term<'db>, + + /// After we generalize this type, we are going to relate it to + /// some other type. What will be the variance at this point? + ambient_variance: Variance, + + /// This is set once we're generalizing the arguments of an alias. + /// + /// This is necessary to correctly handle + /// `<T as Bar<<?0 as Foo>::Assoc>::Assoc == ?0`. This equality can + /// hold by either normalizing the outer or the inner associated type. + in_alias: bool, + + cache: FxHashMap<(Ty<'db>, Variance, bool), Ty<'db>>, + + /// See the field `has_unconstrained_ty_var` in `Generalization`. + has_unconstrained_ty_var: bool, +} + +impl<'db> Generalizer<'_, 'db> { + /// Create an error that corresponds to the term kind in `root_term` + fn cyclic_term_error(&self) -> TypeError<DbInterner<'db>> { + match self.root_term.kind() { + TermKind::Ty(ty) => TypeError::CyclicTy(ty), + TermKind::Const(ct) => TypeError::CyclicConst(ct), + } + } + + /// Create a new type variable in the universe of the target when + /// generalizing an alias. This has to set `has_unconstrained_ty_var` + /// 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) + } + + /// An occurs check failure inside of an alias does not mean + /// that the types definitely don't unify. We may be able + /// to normalize the alias after all. + /// + /// We handle this by lazily equating the alias and generalizing + /// it to an inference variable. In the new solver, we always + /// generalize to an infer var unless the alias contains escaping + /// bound variables. + /// + /// Correctly handling aliases with escaping bound variables is + /// difficult and currently incomplete in two opposite ways: + /// - if we get an occurs check failure in the alias, replace it with a new infer var. + /// This causes us to later emit an alias-relate goal and is incomplete in case the + /// alias normalizes to type containing one of the bound variables. + /// - if the alias contains an inference variable not nameable by `for_universe`, we + /// continue generalizing the alias. This ends up pulling down the universe of the + /// inference variable and is incomplete in case the alias would normalize to a type + /// which does not mention that inference variable. + fn generalize_alias_ty( + &mut self, + alias: AliasTy<'db>, + ) -> Result<Ty<'db>, TypeError<DbInterner<'db>>> { + // We do not eagerly replace aliases with inference variables if they have + // escaping bound vars, see the method comment for details. However, when we + // are inside of an alias with escaping bound vars replacing nested aliases + // with inference variables can cause incorrect ambiguity. + // + // cc trait-system-refactor-initiative#110 + if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() && !self.in_alias { + return Ok(self.next_ty_var_for_alias()); + } + + let is_nested_alias = mem::replace(&mut self.in_alias, true); + let result = match self.relate(alias, alias) { + Ok(alias) => Ok(alias.to_ty(self.cx())), + Err(e) => { + if is_nested_alias { + return Err(e); + } else { + let mut visitor = MaxUniverse::new(); + alias.visit_with(&mut visitor); + let infer_replacement_is_complete = + self.for_universe.can_name(visitor.max_universe()) + && !alias.has_escaping_bound_vars(); + if !infer_replacement_is_complete { + warn!("may incompletely handle alias type: {alias:?}"); + } + + debug!("generalization failure in alias"); + Ok(self.next_ty_var_for_alias()) + } + } + }; + self.in_alias = is_nested_alias; + result + } +} + +impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn relate_item_args( + &mut self, + item_def_id: SolverDefId, + a_arg: GenericArgs<'db>, + b_arg: GenericArgs<'db>, + ) -> RelateResult<'db, GenericArgs<'db>> { + if self.ambient_variance == Variance::Invariant { + // Avoid fetching the variance if we are in an invariant + // context; no need, and it can induce dependency cycles + // (e.g., #41849). + relate::relate_args_invariantly(self, a_arg, b_arg) + } else { + let tcx = self.cx(); + let opt_variances = tcx.variances_of(item_def_id); + relate::relate_args_with_variances( + self, + item_def_id, + opt_variances, + a_arg, + b_arg, + false, + ) + } + } + + #[instrument(level = "debug", skip(self, variance, b), ret)] + fn relate_with_variance<T: Relate<DbInterner<'db>>>( + &mut self, + variance: Variance, + _info: VarianceDiagInfo<DbInterner<'db>>, + a: T, + b: T, + ) -> RelateResult<'db, T> { + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + debug!(?self.ambient_variance, "new ambient variance"); + // Recursive calls to `relate` can overflow the stack. For example a deeper version of + // `ui/associated-consts/issue-93775.rs`. + let r = self.relate(a, b); + self.ambient_variance = old_ambient_variance; + r + } + + #[instrument(level = "debug", skip(self, t2), ret)] + fn tys(&mut self, t: Ty<'db>, t2: Ty<'db>) -> RelateResult<'db, Ty<'db>> { + assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + if let Some(result) = self.cache.get(&(t, self.ambient_variance, self.in_alias)) { + return Ok(*result); + } + + // Check to see whether the type we are generalizing references + // any other type variable related to `vid` via + // subtyping. This is basically our "occurs check", preventing + // us from creating infinitely sized types. + let g = match t.kind() { + TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { + panic!("unexpected infer type: {t:?}") + } + + TyKind::Infer(InferTy::TyVar(vid)) => { + let mut inner = self.infcx.inner.borrow_mut(); + let vid = inner.type_variables().root_var(vid); + if TermVid::Ty(vid) == self.root_vid { + // If sub-roots are equal, then `root_vid` and + // `vid` are related via subtyping. + Err(self.cyclic_term_error()) + } else { + let probe = inner.type_variables().probe(vid); + match probe { + TypeVariableValue::Known { value: u } => { + drop(inner); + self.relate(u, u) + } + TypeVariableValue::Unknown { universe } => { + match self.ambient_variance { + // Invariant: no need to make a fresh type variable + // if we can name the universe. + Variance::Invariant => { + if self.for_universe.can_name(universe) { + return Ok(t); + } + } + + // Bivariant: make a fresh var, but remember that + // it is unconstrained. See the comment in + // `Generalization`. + Variance::Bivariant => self.has_unconstrained_ty_var = true, + + // Co/contravariant: this will be + // sufficiently constrained later on. + Variance::Covariant | Variance::Contravariant => (), + } + + let origin = inner.type_variables().var_origin(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 + // variable inside of an alias we eagerly constrain that + // inference variable to prevent unexpected ambiguity errors. + // + // This is incomplete as it pulls down the universe of the + // original inference variable, even though the alias could + // normalize to a type which does not refer to that type at + // all. I don't expect this to cause unexpected errors in + // practice. + // + // We only need to do so for type and const variables, as + // region variables do not impact normalization, and will get + // correctly constrained by `AliasRelate` later on. + // + // cc trait-system-refactor-initiative#108 + if self.infcx.next_trait_solver() + && !matches!( + self.infcx.typing_mode_unchecked(), + TypingMode::Coherence + ) + && self.in_alias + { + inner.type_variables().equate(vid, new_var_id); + } + + debug!("replacing original vid={:?} with new={:?}", vid, new_var_id); + Ok(Ty::new_var(self.infcx.interner, new_var_id)) + } + } + } + } + + TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => { + // No matter what mode we are in, + // integer/floating-point types must be equal to be + // relatable. + Ok(t) + } + + TyKind::Placeholder(placeholder) => { + if self.for_universe.can_name(placeholder.universe) { + Ok(t) + } else { + debug!( + "root universe {:?} cannot name placeholder in universe {:?}", + self.for_universe, placeholder.universe + ); + Err(TypeError::Mismatch) + } + } + + TyKind::Alias(_, data) => match self.structurally_relate_aliases { + StructurallyRelateAliases::No => self.generalize_alias_ty(data), + StructurallyRelateAliases::Yes => relate::structurally_relate_tys(self, t, t), + }, + + _ => relate::structurally_relate_tys(self, t, t), + }?; + + self.cache.insert((t, self.ambient_variance, self.in_alias), g); + Ok(g) + } + + #[instrument(level = "debug", skip(self, r2), ret)] + fn regions(&mut self, r: Region<'db>, r2: Region<'db>) -> RelateResult<'db, Region<'db>> { + assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + match r.kind() { + // Never make variables for regions bound within the type itself, + // nor for erased regions. + RegionKind::ReBound(..) | RegionKind::ReErased => { + return Ok(r); + } + + // It doesn't really matter for correctness if we generalize ReError, + // since we're already on a doomed compilation path. + RegionKind::ReError(_) => { + return Ok(r); + } + + RegionKind::RePlaceholder(..) + | RegionKind::ReVar(..) + | RegionKind::ReStatic + | RegionKind::ReEarlyParam(..) + | RegionKind::ReLateParam(..) => { + // see common code below + } + } + + // If we are in an invariant context, we can re-use the region + // as is, unless it happens to be in some universe that we + // can't name. + if let Variance::Invariant = self.ambient_variance { + let r_universe = self.infcx.universe_of_region(r); + if self.for_universe.can_name(r_universe) { + return Ok(r); + } + } + + Ok(self.infcx.next_region_var_in_universe(self.for_universe)) + } + + #[instrument(level = "debug", skip(self, c2), ret)] + fn consts(&mut self, c: Const<'db>, c2: Const<'db>) -> RelateResult<'db, Const<'db>> { + assert_eq!(c, c2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + match c.kind() { + ConstKind::Infer(InferConst::Var(vid)) => { + // If root const vids are equal, then `root_vid` and + // `vid` are related and we'd be inferring an infinitely + // deep const. + if TermVid::Const( + self.infcx.inner.borrow_mut().const_unification_table().find(vid).vid, + ) == self.root_vid + { + return Err(self.cyclic_term_error()); + } + + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); + match variable_table.probe_value(vid) { + ConstVariableValue::Known { value: u } => { + drop(inner); + self.relate(u, u) + } + ConstVariableValue::Unknown { origin, universe } => { + if self.for_universe.can_name(universe) { + Ok(c) + } else { + let new_var_id = variable_table + .new_key(ConstVariableValue::Unknown { + origin, + universe: self.for_universe, + }) + .vid; + + // See the comment for type inference variables + // for more details. + if self.infcx.next_trait_solver() + && !matches!( + self.infcx.typing_mode_unchecked(), + TypingMode::Coherence + ) + && self.in_alias + { + variable_table.union(vid, new_var_id); + } + Ok(Const::new_var(self.infcx.interner, new_var_id)) + } + } + } + } + // FIXME: Unevaluated constants are also not rigid, so the current + // approach of always relating them structurally is incomplete. + // + // FIXME: remove this branch once `structurally_relate_consts` is fully + // structural. + ConstKind::Unevaluated(UnevaluatedConst { def, args }) => { + let args = self.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + args, + args, + )?; + Ok(Const::new_unevaluated(self.infcx.interner, UnevaluatedConst { def, args })) + } + ConstKind::Placeholder(placeholder) => { + if self.for_universe.can_name(placeholder.universe) { + Ok(c) + } else { + debug!( + "root universe {:?} cannot name placeholder in universe {:?}", + self.for_universe, placeholder.universe + ); + Err(TypeError::Mismatch) + } + } + _ => relate::structurally_relate_consts(self, c, c), + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn binders<T>( + &mut self, + a: Binder<'db, T>, + _: Binder<'db, T>, + ) -> RelateResult<'db, Binder<'db, T>> + where + T: Relate<DbInterner<'db>>, + { + let result = self.relate(a.skip_binder(), a.skip_binder())?; + Ok(a.rebind(result)) + } +} + +/// Result from a generalization operation. This includes +/// not only the generalized type, but also a bool flag +/// indicating whether further WF checks are needed. +#[derive(Debug)] +struct Generalization<T> { + /// When generalizing `<?0 as Trait>::Assoc` or + /// `<T as Bar<<?0 as Foo>::Assoc>>::Assoc` + /// for `?0` generalization returns an inference + /// variable. + /// + /// This has to be handled wotj care as it can + /// otherwise very easily result in infinite + /// recursion. + pub value_may_be_infer: T, + + /// In general, we do not check whether all types which occur during + /// type checking are well-formed. We only check wf of user-provided types + /// and when actually using a type, e.g. for method calls. + /// + /// This means that when subtyping, we may end up with unconstrained + /// inference variables if a generalized type has bivariant parameters. + /// A parameter may only be bivariant if it is constrained by a projection + /// bound in a where-clause. As an example, imagine a type: + /// + /// struct Foo<A, B> where A: Iterator<Item = B> { + /// data: A + /// } + /// + /// here, `A` will be covariant, but `B` is unconstrained. + /// + /// However, whatever it is, for `Foo` to be WF, it must be equal to `A::Item`. + /// If we have an input `Foo<?A, ?B>`, then after generalization we will wind + /// up with a type like `Foo<?C, ?D>`. When we enforce `Foo<?A, ?B> <: Foo<?C, ?D>`, + /// we will wind up with the requirement that `?A <: ?C`, but no particular + /// relationship between `?B` and `?D` (after all, these types may be completely + /// different). If we do nothing else, this may mean that `?D` goes unconstrained + /// (as in #41677). To avoid this we emit a `WellFormed` obligation in these cases. + pub has_unconstrained_ty_var: bool, +}
diff --git a/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs b/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs new file mode 100644 index 0000000..bb80c51 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs
@@ -0,0 +1,89 @@ +//! Helper routines for higher-ranked things. See the `doc` module at +//! the end of the file for details. + +use rustc_type_ir::TypeFoldable; +use rustc_type_ir::{BoundVar, UniverseIndex}; +use tracing::{debug, instrument}; + +use super::RelateResult; +use crate::next_solver::fold::FnMutDelegate; +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::infer::snapshot::CombinedSnapshot; +use crate::next_solver::{ + Binder, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst, PlaceholderRegion, + PlaceholderTy, Region, Ty, +}; + +impl<'db> InferCtxt<'db> { + /// Replaces all bound variables (lifetimes, types, and constants) bound by + /// `binder` with placeholder variables in a new universe. This means that the + /// new placeholders can only be named by inference variables created after + /// this method has been called. + /// + /// This is the first step of checking subtyping when higher-ranked things are involved. + /// For more details visit the relevant sections of the [rustc dev guide]. + /// + /// `fn enter_forall` should be preferred over this method. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html + #[instrument(level = "debug", skip(self), ret)] + pub fn enter_forall_and_leak_universe<T>(&self, binder: Binder<'db, T>) -> T + where + T: TypeFoldable<DbInterner<'db>> + Clone, + { + if let Some(inner) = binder.clone().no_bound_vars() { + return inner; + } + + let next_universe = self.create_next_universe(); + + let delegate = FnMutDelegate { + regions: &mut |br: BoundRegion| { + Region::new_placeholder( + self.interner, + PlaceholderRegion { universe: next_universe, bound: br }, + ) + }, + types: &mut |bound_ty: BoundTy| { + Ty::new_placeholder( + self.interner, + PlaceholderTy { universe: next_universe, bound: bound_ty }, + ) + }, + consts: &mut |bound_var: BoundVar| { + Const::new_placeholder( + self.interner, + PlaceholderConst { universe: next_universe, bound: bound_var }, + ) + }, + }; + + debug!(?next_universe); + self.interner.replace_bound_vars_uncached(binder, delegate) + } + + /// Replaces all bound variables (lifetimes, types, and constants) bound by + /// `binder` with placeholder variables in a new universe and then calls the + /// closure `f` with the instantiated value. The new placeholders can only be + /// named by inference variables created inside of the closure `f` or afterwards. + /// + /// This is the first step of checking subtyping when higher-ranked things are involved. + /// For more details visit the relevant sections of the [rustc dev guide]. + /// + /// This method should be preferred over `fn enter_forall_and_leak_universe`. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html + #[instrument(level = "debug", skip(self, f))] + pub fn enter_forall<T, U>(&self, forall: Binder<'db, T>, f: impl FnOnce(T) -> U) -> U + where + T: TypeFoldable<DbInterner<'db>> + Clone, + { + // FIXME: currently we do nothing to prevent placeholders with the new universe being + // used after exiting `f`. For example region subtyping can result in outlives constraints + // that name placeholders created in this function. Nested goals from type relations can + // also contain placeholders created by this function. + let value = self.enter_forall_and_leak_universe(forall); + debug!(?value); + f(value) + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/relate/mod.rs b/crates/hir-ty/src/next_solver/infer/relate/mod.rs new file mode 100644 index 0000000..836ae39 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/relate/mod.rs
@@ -0,0 +1,13 @@ +//! This module contains the definitions of most `TypeRelation`s in the type system +//! (except for some relations used for diagnostics and heuristics in the compiler). +//! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc). + +pub use rustc_type_ir::relate::combine::PredicateEmittingRelation; +pub use rustc_type_ir::relate::*; + +use crate::next_solver::DbInterner; + +mod generalize; +mod higher_ranked; + +pub type RelateResult<'db, T> = rustc_type_ir::relate::RelateResult<DbInterner<'db>, T>;
diff --git a/crates/hir-ty/src/next_solver/infer/resolve.rs b/crates/hir-ty/src/next_solver/infer/resolve.rs new file mode 100644 index 0000000..84338ad --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/resolve.rs
@@ -0,0 +1,62 @@ +//! Things for resolving vars in the infer context of the next-trait-solver. + +use rustc_type_ir::{ + ConstKind, FallibleTypeFolder, InferConst, InferTy, RegionKind, TyKind, TypeFoldable, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, data_structures::DelayedMap, + inherent::IntoKind, +}; + +use crate::next_solver::{Const, DbInterner, Region, Ty}; + +use super::{FixupError, FixupResult, InferCtxt}; + +/////////////////////////////////////////////////////////////////////////// +// OPPORTUNISTIC VAR RESOLVER + +/// The opportunistic resolver can be used at any time. It simply replaces +/// type/const variables that have been unified with the things they have +/// been unified with (similar to `shallow_resolve`, but deep). This is +/// useful for printing messages etc but also required at various +/// points for correctness. +pub struct OpportunisticVarResolver<'a, 'db> { + infcx: &'a InferCtxt<'db>, + /// We're able to use a cache here as the folder does + /// not have any mutable state. + cache: DelayedMap<Ty<'db>, Ty<'db>>, +} + +impl<'a, 'db> OpportunisticVarResolver<'a, 'db> { + #[inline] + pub fn new(infcx: &'a InferCtxt<'db>) -> Self { + OpportunisticVarResolver { infcx, cache: Default::default() } + } +} + +impl<'a, 'db> TypeFolder<DbInterner<'db>> for OpportunisticVarResolver<'a, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + #[inline] + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + if !t.has_non_region_infer() { + t // micro-optimize -- if there is nothing in this type that this fold affects... + } else if let Some(ty) = self.cache.get(&t) { + *ty + } else { + let shallow = self.infcx.shallow_resolve(t); + let res = shallow.super_fold_with(self); + assert!(self.cache.insert(t, res)); + res + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + if !ct.has_non_region_infer() { + ct // micro-optimize -- if there is nothing in this const that this fold affects... + } else { + let ct = self.infcx.shallow_resolve_const(ct); + ct.super_fold_with(self) + } + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs new file mode 100644 index 0000000..eb42620 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs
@@ -0,0 +1,111 @@ +//! Snapshotting in the infer ctxt of the next-trait-solver. + +use ena::undo_log::UndoLogs; +use rustc_type_ir::UniverseIndex; +use tracing::{debug, instrument}; + +use super::InferCtxt; +use super::region_constraints::RegionSnapshot; + +pub(crate) mod undo_log; + +use undo_log::{Snapshot, UndoLog}; + +#[must_use = "once you start a snapshot, you should always consume it"] +pub struct CombinedSnapshot { + pub(super) undo_snapshot: Snapshot, + region_constraints_snapshot: RegionSnapshot, + universe: UniverseIndex, +} + +struct VariableLengths { + region_constraints_len: usize, + type_var_len: usize, + int_var_len: usize, + float_var_len: usize, + const_var_len: usize, +} + +impl<'db> InferCtxt<'db> { + fn variable_lengths(&self) -> VariableLengths { + let mut inner = self.inner.borrow_mut(); + VariableLengths { + region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), + type_var_len: inner.type_variables().num_vars(), + int_var_len: inner.int_unification_table().len(), + float_var_len: inner.float_unification_table().len(), + const_var_len: inner.const_unification_table().len(), + } + } + + pub fn in_snapshot(&self) -> bool { + UndoLogs::<UndoLog<'db>>::in_snapshot(&self.inner.borrow_mut().undo_log) + } + + pub fn num_open_snapshots(&self) -> usize { + UndoLogs::<UndoLog<'db>>::num_open_snapshots(&self.inner.borrow_mut().undo_log) + } + + fn start_snapshot(&self) -> CombinedSnapshot { + debug!("start_snapshot()"); + + let mut inner = self.inner.borrow_mut(); + + CombinedSnapshot { + undo_snapshot: inner.undo_log.start_snapshot(), + region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(), + universe: self.universe(), + } + } + + #[instrument(skip(self, snapshot), level = "debug")] + fn rollback_to(&self, snapshot: CombinedSnapshot) { + let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot; + + self.universe.set(universe); + + let mut inner = self.inner.borrow_mut(); + inner.rollback_to(undo_snapshot); + inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot); + } + + #[instrument(skip(self, snapshot), level = "debug")] + fn commit_from(&self, snapshot: CombinedSnapshot) { + let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } = + snapshot; + + self.inner.borrow_mut().commit(undo_snapshot); + } + + /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`. + #[instrument(skip(self, f), level = "debug")] + pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E> + where + F: FnOnce(&CombinedSnapshot) -> Result<T, E>, + { + let snapshot = self.start_snapshot(); + let r = f(&snapshot); + debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok()); + match r { + Ok(_) => { + self.commit_from(snapshot); + } + Err(_) => { + self.rollback_to(snapshot); + } + } + r + } + + /// Execute `f` then unroll any bindings it creates. + #[instrument(skip(self, f), level = "debug")] + pub fn probe<R, F>(&self, f: F) -> R + where + F: FnOnce(&CombinedSnapshot) -> R, + { + let snapshot = self.start_snapshot(); + let r = f(&snapshot); + self.rollback_to(snapshot); + r + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs b/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs new file mode 100644 index 0000000..0fa6421 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs
@@ -0,0 +1,198 @@ +//! Snapshotting in the infer ctxt of the next-trait-solver. + +use std::marker::PhantomData; + +use ena::snapshot_vec as sv; +use ena::undo_log::{Rollback, UndoLogs}; +use ena::unify as ut; +use rustc_type_ir::FloatVid; +use rustc_type_ir::IntVid; +use tracing::debug; + +use crate::next_solver::OpaqueTypeKey; +use crate::next_solver::infer::opaque_types::OpaqueHiddenType; +use crate::next_solver::infer::unify_key::ConstVidKey; +use crate::next_solver::infer::unify_key::RegionVidKey; +use crate::next_solver::infer::{InferCtxtInner, region_constraints, type_variable}; +use crate::traits; + +pub struct Snapshot { + pub(crate) undo_len: usize, +} + +/// Records the "undo" data for a single operation that affects some form of inference variable. +#[derive(Clone)] +pub(crate) enum UndoLog<'db> { + DuplicateOpaqueType, + OpaqueTypes(OpaqueTypeKey<'db>, Option<OpaqueHiddenType<'db>>), + TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'db>>>), + ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'db>>>), + IntUnificationTable(sv::UndoLog<ut::Delegate<IntVid>>), + FloatUnificationTable(sv::UndoLog<ut::Delegate<FloatVid>>), + RegionConstraintCollector(region_constraints::UndoLog<'db>), + RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'db>>>), + PushRegionObligation, +} + +macro_rules! impl_from { + ($($ctor:ident ($ty:ty),)*) => { + $( + impl<'db> From<$ty> for UndoLog<'db> { + fn from(x: $ty) -> Self { + UndoLog::$ctor(x.into()) + } + } + )* + } +} + +// Upcast from a single kind of "undoable action" to the general enum +impl_from! { + RegionConstraintCollector(region_constraints::UndoLog<'db>), + + TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'db>>>), + IntUnificationTable(sv::UndoLog<ut::Delegate<IntVid>>), + FloatUnificationTable(sv::UndoLog<ut::Delegate<FloatVid>>), + + ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'db>>>), + + RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'db>>>), +} + +/// The Rollback trait defines how to rollback a particular action. +impl<'db> Rollback<UndoLog<'db>> for InferCtxtInner<'db> { + fn reverse(&mut self, undo: UndoLog<'db>) { + match undo { + UndoLog::DuplicateOpaqueType => self.opaque_type_storage.pop_duplicate_entry(), + UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx), + UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo), + UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo), + UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo), + UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo), + UndoLog::RegionConstraintCollector(undo) => { + self.region_constraint_storage.as_mut().unwrap().reverse(undo) + } + UndoLog::RegionUnificationTable(undo) => { + self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo) + } + UndoLog::PushRegionObligation => { + self.region_obligations.pop(); + } + } + } +} + +/// The combined undo log for all the various unification tables. For each change to the storage +/// for any kind of inference variable, we record an UndoLog entry in the vector here. +#[derive(Clone, Default)] +pub(crate) struct InferCtxtUndoLogs<'db> { + logs: Vec<UndoLog<'db>>, + num_open_snapshots: usize, +} + +/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any +/// action that is convertible into an UndoLog (per the From impls above). +impl<'db, T> UndoLogs<T> for InferCtxtUndoLogs<'db> +where + UndoLog<'db>: From<T>, +{ + #[inline] + fn num_open_snapshots(&self) -> usize { + self.num_open_snapshots + } + + #[inline] + fn push(&mut self, undo: T) { + if self.in_snapshot() { + self.logs.push(undo.into()) + } + } + + fn clear(&mut self) { + self.logs.clear(); + self.num_open_snapshots = 0; + } + + fn extend<J>(&mut self, undos: J) + where + Self: Sized, + J: IntoIterator<Item = T>, + { + if self.in_snapshot() { + self.logs.extend(undos.into_iter().map(UndoLog::from)) + } + } +} + +impl<'db> InferCtxtInner<'db> { + pub fn rollback_to(&mut self, snapshot: Snapshot) { + debug!("rollback_to({})", snapshot.undo_len); + self.undo_log.assert_open_snapshot(&snapshot); + + while self.undo_log.logs.len() > snapshot.undo_len { + let undo = self.undo_log.logs.pop().unwrap(); + self.reverse(undo); + } + + self.type_variable_storage.finalize_rollback(); + + if self.undo_log.num_open_snapshots == 1 { + // After the root snapshot the undo log should be empty. + assert!(snapshot.undo_len == 0); + assert!(self.undo_log.logs.is_empty()); + } + + self.undo_log.num_open_snapshots -= 1; + } + + pub fn commit(&mut self, snapshot: Snapshot) { + debug!("commit({})", snapshot.undo_len); + + if self.undo_log.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.undo_len == 0); + self.undo_log.logs.clear(); + } + + self.undo_log.num_open_snapshots -= 1; + } +} + +impl<'db> InferCtxtUndoLogs<'db> { + pub(crate) fn start_snapshot(&mut self) -> Snapshot { + self.num_open_snapshots += 1; + Snapshot { undo_len: self.logs.len() } + } + + pub(crate) fn region_constraints_in_snapshot( + &self, + s: &Snapshot, + ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'db>> + Clone { + self.logs[s.undo_len..].iter().filter_map(|log| match log { + UndoLog::RegionConstraintCollector(log) => Some(log), + _ => None, + }) + } + + fn assert_open_snapshot(&self, snapshot: &Snapshot) { + // Failures here may indicate a failure to follow a stack discipline. + assert!(self.logs.len() >= snapshot.undo_len); + assert!(self.num_open_snapshots > 0); + } +} + +impl<'db> std::ops::Index<usize> for InferCtxtUndoLogs<'db> { + type Output = UndoLog<'db>; + + fn index(&self, key: usize) -> &Self::Output { + &self.logs[key] + } +} + +impl<'db> std::ops::IndexMut<usize> for InferCtxtUndoLogs<'db> { + fn index_mut(&mut self, key: usize) -> &mut Self::Output { + &mut self.logs[key] + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/traits.rs b/crates/hir-ty/src/next_solver/infer/traits.rs new file mode 100644 index 0000000..f1df806 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/traits.rs
@@ -0,0 +1,175 @@ +//! Trait Resolution. See the [rustc-dev-guide] for more information on how this works. +//! +//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html + +use std::{ + cmp, + hash::{Hash, Hasher}, +}; + +use rustc_type_ir::{ + PredicatePolarity, Upcast, + solve::{Certainty, NoSolution}, +}; + +use crate::next_solver::{ + Binder, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, SolverDefId, TraitPredicate, + 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)] +pub struct ObligationCause { + /// The ID of the fn body that triggered this obligation. This is + /// used for region obligations to determine the precise + /// environment in which the region obligation should be evaluated + /// (in particular, closures can add new assumptions). See the + /// field `region_obligations` of the `FulfillmentContext` for more + /// information. + pub body_id: Option<SolverDefId>, +} + +impl ObligationCause { + #[inline] + pub fn new(body_id: SolverDefId) -> ObligationCause { + ObligationCause { body_id: Some(body_id) } + } + + #[inline(always)] + pub fn dummy() -> ObligationCause { + ObligationCause { body_id: None } + } +} + +/// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for +/// which the "impl_source" must be found. The process of finding an "impl_source" is +/// called "resolving" the `Obligation`. This process consists of +/// either identifying an `impl` (e.g., `impl Eq for i32`) that +/// satisfies the obligation, or else finding a bound that is in +/// scope. The eventual result is usually a `Selection` (defined below). +#[derive(Clone, Debug)] +pub struct Obligation<'db, T> { + /// The reason we have to prove this thing. + pub cause: ObligationCause, + + /// The environment in which we should prove this thing. + pub param_env: ParamEnv<'db>, + + /// The thing we are trying to prove. + pub predicate: T, + + /// If we started proving this as a result of trying to prove + /// something else, track the total depth to ensure termination. + /// If this goes over a certain threshold, we abort compilation -- + /// in such cases, we can not say whether or not the predicate + /// holds for certain. Stupid halting problem; such a drag. + pub recursion_depth: usize, +} + +impl<'db, T: Copy> Obligation<'db, T> { + pub fn as_goal(&self) -> Goal<'db, T> { + Goal { param_env: self.param_env, predicate: self.predicate } + } +} + +impl<'db, T: PartialEq> PartialEq<Obligation<'db, T>> for Obligation<'db, T> { + #[inline] + fn eq(&self, other: &Obligation<'db, T>) -> bool { + // Ignore `cause` and `recursion_depth`. This is a small performance + // win for a few crates, and a huge performance win for the crate in + // https://github.com/rust-lang/rustc-perf/pull/1680, which greatly + // stresses the trait system. + self.param_env == other.param_env && self.predicate == other.predicate + } +} + +impl<'db, T: Eq> Eq for Obligation<'db, T> {} + +impl<'db, T: Hash> Hash for Obligation<'db, T> { + fn hash<H: Hasher>(&self, state: &mut H) { + // See the comment on `Obligation::eq`. + self.param_env.hash(state); + self.predicate.hash(state); + } +} + +impl<'db, P> From<Obligation<'db, P>> for Goal<'db, P> { + fn from(value: Obligation<'db, P>) -> Self { + Goal { param_env: value.param_env, predicate: value.predicate } + } +} + +pub type PredicateObligation<'db> = Obligation<'db, Predicate<'db>>; +pub type TraitObligation<'db> = Obligation<'db, TraitPredicate<'db>>; + +pub type PredicateObligations<'db> = Vec<PredicateObligation<'db>>; + +impl<'db> PredicateObligation<'db> { + /// Flips the polarity of the inner predicate. + /// + /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. + pub fn flip_polarity(&self, tcx: DbInterner<'db>) -> Option<PredicateObligation<'db>> { + Some(PredicateObligation { + cause: self.cause.clone(), + param_env: self.param_env, + predicate: self.predicate.flip_polarity()?, + recursion_depth: self.recursion_depth, + }) + } +} + +impl<'db, O> Obligation<'db, O> { + pub fn new( + tcx: DbInterner<'db>, + cause: ObligationCause, + param_env: ParamEnv<'db>, + predicate: impl Upcast<DbInterner<'db>, O>, + ) -> Obligation<'db, O> { + Self::with_depth(tcx, cause, 0, param_env, predicate) + } + + /// We often create nested obligations without setting the correct depth. + /// + /// To deal with this evaluate and fulfill explicitly update the depth + /// of nested obligations using this function. + pub fn set_depth_from_parent(&mut self, parent_depth: usize) { + self.recursion_depth = cmp::max(parent_depth + 1, self.recursion_depth); + } + + pub fn with_depth( + tcx: DbInterner<'db>, + cause: ObligationCause, + recursion_depth: usize, + param_env: ParamEnv<'db>, + predicate: impl Upcast<DbInterner<'db>, O>, + ) -> Obligation<'db, O> { + let predicate = predicate.upcast(tcx); + Obligation { cause, param_env, recursion_depth, predicate } + } + + pub fn misc( + tcx: DbInterner<'db>, + body_id: SolverDefId, + param_env: ParamEnv<'db>, + trait_ref: impl Upcast<DbInterner<'db>, O>, + ) -> Obligation<'db, O> { + Obligation::new(tcx, ObligationCause::new(body_id), param_env, trait_ref) + } + + pub fn with<P>( + &self, + tcx: DbInterner<'db>, + value: impl Upcast<DbInterner<'db>, P>, + ) -> Obligation<'db, P> { + Obligation::with_depth(tcx, self.cause.clone(), self.recursion_depth, self.param_env, value) + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/type_variable.rs b/crates/hir-ty/src/next_solver/infer/type_variable.rs new file mode 100644 index 0000000..5217308 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/type_variable.rs
@@ -0,0 +1,268 @@ +//! Storage for type variables for the infer context the next-trait-solver. + +use std::cmp; +use std::marker::PhantomData; +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::next_solver::Ty; +use crate::next_solver::infer::InferCtxtUndoLogs; + +impl<'db> Rollback<sv::UndoLog<ut::Delegate<TyVidEqKey<'db>>>> for TypeVariableStorage<'db> { + fn reverse(&mut self, undo: sv::UndoLog<ut::Delegate<TyVidEqKey<'db>>>) { + self.eq_relations.reverse(undo) + } +} + +#[derive(Clone, Default)] +pub(crate) struct TypeVariableStorage<'db> { + /// The origins of each type variable. + values: IndexVec<TyVid, TypeVariableData>, + /// Two variables are unified in `eq_relations` when we have a + /// constraint `?X == ?Y`. This table also stores, for each key, + /// the known value. + eq_relations: ut::UnificationTableStorage<TyVidEqKey<'db>>, +} + +pub(crate) struct TypeVariableTable<'a, 'db> { + storage: &'a mut TypeVariableStorage<'db>, + + undo_log: &'a mut InferCtxtUndoLogs<'db>, +} + +#[derive(Copy, Clone, Debug)] +pub struct TypeVariableOrigin { + /// `DefId` of the type parameter this was instantiated for, if any. + /// + /// This should only be used for diagnostics. + pub param_def_id: Option<SolverDefId>, +} + +#[derive(Clone)] +pub(crate) struct TypeVariableData { + origin: TypeVariableOrigin, +} + +#[derive(Clone, Debug)] +pub(crate) enum TypeVariableValue<'db> { + Known { value: Ty<'db> }, + Unknown { universe: UniverseIndex }, +} + +impl<'db> TypeVariableValue<'db> { + /// If this value is known, returns the type it is known to be. + /// Otherwise, `None`. + pub(crate) fn known(&self) -> Option<Ty<'db>> { + match self { + TypeVariableValue::Unknown { .. } => None, + TypeVariableValue::Known { value } => Some(*value), + } + } + + pub(crate) fn is_unknown(&self) -> bool { + match *self { + TypeVariableValue::Unknown { .. } => true, + TypeVariableValue::Known { .. } => false, + } + } +} + +impl<'db> TypeVariableStorage<'db> { + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'db>, + ) -> TypeVariableTable<'a, 'db> { + TypeVariableTable { storage: self, undo_log } + } + + #[inline] + pub(crate) fn eq_relations_ref(&self) -> &ut::UnificationTableStorage<TyVidEqKey<'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()); + } +} + +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 + } + + /// Records that `a == b`, depending on `dir`. + /// + /// Precondition: neither `a` nor `b` are known. + pub(crate) fn equate(&mut self, a: TyVid, b: TyVid) { + debug_assert!(self.probe(a).is_unknown()); + debug_assert!(self.probe(b).is_unknown()); + self.eq_relations().union(a, b); + } + + /// Instantiates `vid` with the type `ty`. + /// + /// Precondition: `vid` must not have been previously instantiated. + pub(crate) fn instantiate(&mut self, vid: TyVid, ty: Ty<'db>) { + let vid = self.root_var(vid); + debug_assert!(!ty.is_ty_var(), "instantiating ty var with var: {vid:?} {ty:?}"); + debug_assert!(self.probe(vid).is_unknown()); + debug_assert!( + self.eq_relations().probe_value(vid).is_unknown(), + "instantiating type variable `{vid:?}` twice: new-value = {ty:?}, old-value={:?}", + self.eq_relations().probe_value(vid) + ); + 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 { + let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); + 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); + + index + } + + /// Returns the number of type variables created thus far. + pub(crate) fn num_vars(&self) -> usize { + self.storage.values.len() + } + + /// Returns the "root" variable of `vid` in the `eq_relations` + /// equivalence table. All type variables that have been equated + /// will yield the same root variable (per the union-find + /// algorithm), so `root_var(a) == root_var(b)` implies that `a == + /// b` (transitively). + pub(crate) fn root_var(&mut self, vid: TyVid) -> TyVid { + self.eq_relations().find(vid).vid + } + + /// Retrieves the type to which `vid` has been instantiated, if + /// any. + pub(crate) fn probe(&mut self, vid: TyVid) -> TypeVariableValue<'db> { + self.inlined_probe(vid) + } + + /// An always-inlined variant of `probe`, for very hot call sites. + #[inline(always)] + pub(crate) fn inlined_probe(&mut self, vid: TyVid) -> TypeVariableValue<'db> { + self.eq_relations().inlined_probe_value(vid) + } + + #[inline] + fn eq_relations(&mut self) -> super::UnificationTable<'_, 'db, TyVidEqKey<'db>> { + self.storage.eq_relations.with_log(self.undo_log) + } + + /// Returns indices of all variables that are not yet + /// instantiated. + pub(crate) fn unresolved_variables(&mut self) -> Vec<TyVid> { + (0..self.num_vars()) + .filter_map(|i| { + let vid = TyVid::from_usize(i); + match self.probe(vid) { + TypeVariableValue::Unknown { .. } => Some(vid), + TypeVariableValue::Known { .. } => None, + } + }) + .collect() + } +} + +/////////////////////////////////////////////////////////////////////////// + +/// These structs (a newtyped TyVid) are used as the unification key +/// for the `eq_relations`; they carry a `TypeVariableValue` along +/// with them. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) struct TyVidEqKey<'db> { + vid: TyVid, + + // in the table, we map each ty-vid to one of these: + phantom: PhantomData<TypeVariableValue<'db>>, +} + +impl<'db> From<TyVid> for TyVidEqKey<'db> { + #[inline] // make this function eligible for inlining - it is quite hot. + fn from(vid: TyVid) -> Self { + TyVidEqKey { vid, phantom: PhantomData } + } +} + +impl<'db> ut::UnifyKey for TyVidEqKey<'db> { + type Value = TypeVariableValue<'db>; + #[inline(always)] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> Self { + TyVidEqKey::from(TyVid::from_u32(i)) + } + fn tag() -> &'static str { + "TyVidEqKey" + } +} + +impl<'db> ut::UnifyValue for TypeVariableValue<'db> { + type Error = ut::NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result<Self, ut::NoError> { + match (value1, value2) { + // We never equate two type variables, both of which + // have known types. Instead, we recursively equate + // those types. + (&TypeVariableValue::Known { .. }, &TypeVariableValue::Known { .. }) => { + panic!("equating two type variables, both of which have known types") + } + + // If one side is known, prefer that one. + (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => { + Ok(value1.clone()) + } + (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => { + Ok(value2.clone()) + } + + // If both sides are *unknown*, it hardly matters, does it? + ( + &TypeVariableValue::Unknown { universe: universe1 }, + &TypeVariableValue::Unknown { 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(TypeVariableValue::Unknown { universe }) + } + } + } +}
diff --git a/crates/hir-ty/src/next_solver/infer/unify_key.rs b/crates/hir-ty/src/next_solver/infer/unify_key.rs new file mode 100644 index 0000000..b9afb45 --- /dev/null +++ b/crates/hir-ty/src/next_solver/infer/unify_key.rs
@@ -0,0 +1,176 @@ +//! Unification keyes for the infer context the next-trait-solver. + +use std::cmp; +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, SolverDefId, Ty}; + +#[derive(Clone, Debug)] +pub enum RegionVariableValue<'db> { + Known { value: Region<'db> }, + Unknown { universe: UniverseIndex }, +} + +#[derive(PartialEq, Copy, Clone, Debug)] +pub struct RegionVidKey<'db> { + pub vid: RegionVid, + pub phantom: PhantomData<RegionVariableValue<'db>>, +} + +impl<'db> From<RegionVid> for RegionVidKey<'db> { + fn from(vid: RegionVid) -> Self { + RegionVidKey { vid, phantom: PhantomData } + } +} + +impl<'db> UnifyKey for RegionVidKey<'db> { + type Value = RegionVariableValue<'db>; + #[inline] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> Self { + RegionVidKey::from(RegionVid::from_u32(i)) + } + fn tag() -> &'static str { + "RegionVidKey" + } +} + +pub struct RegionUnificationError; +impl<'db> UnifyValue for RegionVariableValue<'db> { + type Error = RegionUnificationError; + + fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> { + match (value1, value2) { + (RegionVariableValue::Known { .. }, RegionVariableValue::Known { .. }) => { + Err(RegionUnificationError) + } + + (RegionVariableValue::Known { value }, RegionVariableValue::Unknown { universe }) + | (RegionVariableValue::Unknown { universe }, RegionVariableValue::Known { value }) => { + let universe_of_value = match (*value).kind() { + RegionKind::ReStatic + | RegionKind::ReErased + | RegionKind::ReLateParam(..) + | RegionKind::ReEarlyParam(..) + | RegionKind::ReError(_) => UniverseIndex::ROOT, + RegionKind::RePlaceholder(placeholder) => placeholder.universe, + RegionKind::ReVar(..) | RegionKind::ReBound(..) => { + panic!("not a universal region") + } + }; + + if universe.can_name(universe_of_value) { + Ok(RegionVariableValue::Known { value: *value }) + } else { + Err(RegionUnificationError) + } + } + + ( + RegionVariableValue::Unknown { universe: a }, + RegionVariableValue::Unknown { universe: b }, + ) => { + // 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) }) + } + } + } +} + +// Generic consts. + +#[derive(Copy, Clone, Debug)] +pub struct ConstVariableOrigin { + /// `DefId` of the const parameter this was instantiated for, if any. + /// + /// This should only be used for diagnostics. + pub param_def_id: Option<SolverDefId>, +} + +#[derive(Clone, Debug)] +pub enum ConstVariableValue<'db> { + Known { value: Const<'db> }, + Unknown { origin: ConstVariableOrigin, universe: UniverseIndex }, +} + +impl<'db> ConstVariableValue<'db> { + /// If this value is known, returns the const it is known to be. + /// Otherwise, `None`. + pub fn known(&self) -> Option<Const<'db>> { + match self { + ConstVariableValue::Unknown { .. } => None, + ConstVariableValue::Known { value } => Some(*value), + } + } +} + +#[derive(PartialEq, Copy, Clone, Debug)] +pub struct ConstVidKey<'db> { + pub vid: ConstVid, + pub phantom: PhantomData<Const<'db>>, +} + +impl<'db> From<ConstVid> for ConstVidKey<'db> { + fn from(vid: ConstVid) -> Self { + ConstVidKey { vid, phantom: PhantomData } + } +} + +impl<'db> UnifyKey for ConstVidKey<'db> { + type Value = ConstVariableValue<'db>; + #[inline] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> Self { + ConstVidKey::from(ConstVid::from_u32(i)) + } + fn tag() -> &'static str { + "ConstVidKey" + } +} + +impl<'db> UnifyValue for ConstVariableValue<'db> { + type Error = NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> { + match (value1, value2) { + (ConstVariableValue::Known { .. }, ConstVariableValue::Known { .. }) => { + panic!("equating two const variables, both of which have known values") + } + + // If one side is known, prefer that one. + (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => { + Ok(value1.clone()) + } + (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => { + Ok(value2.clone()) + } + + // If both sides are *unknown*, it hardly matters, does it? + ( + ConstVariableValue::Unknown { origin, universe: universe1 }, + ConstVariableValue::Unknown { origin: _, 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 }) + } + } + } +}
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs new file mode 100644 index 0000000..4fe0b54 --- /dev/null +++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -0,0 +1,2185 @@ +//! Things related to the Interner in the next-trait-solver. +#![allow(unused)] + +use base_db::Crate; +use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances}; +use hir_def::lang_item::LangItem; +use hir_def::signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags}; +use hir_def::{AdtId, BlockId, TypeAliasId, VariantId}; +use hir_def::{AttrDefId, Lookup}; +use hir_def::{CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId}; +use intern::sym::non_exhaustive; +use intern::{Interned, impl_internable, sym}; +use la_arena::Idx; +use rustc_abi::{Align, ReprFlags, ReprOptions}; +use rustc_hash::FxHashSet; +use rustc_index::bit_set::DenseBitSet; +use rustc_type_ir::elaborate::elaborate; +use rustc_type_ir::error::TypeError; +use rustc_type_ir::inherent::{ + AdtDef as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike as _, Span as _, +}; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SizedTraitKind; +use rustc_type_ir::{ + AliasTerm, AliasTermKind, AliasTy, AliasTyKind, EarlyBinder, FlagComputation, Flags, + ImplPolarity, InferTy, ProjectionPredicate, TraitPredicate, TraitRef, Upcast, +}; +use salsa::plumbing::AsId; +use smallvec::{SmallVec, smallvec}; +use std::fmt; +use std::ops::ControlFlow; +use syntax::ast::SelfParamKind; +use triomphe::Arc; + +use rustc_ast_ir::visit::VisitorResult; +use rustc_index::IndexVec; +use rustc_type_ir::TypeVisitableExt; +use rustc_type_ir::{ + BoundVar, CollectAndApply, DebruijnIndex, GenericArgKind, RegionKind, TermKind, UniverseIndex, + Variance, WithCachedTypeInfo, elaborate, + inherent::{self, Const as _, Region as _, Ty as _}, + ir_print, relate, +}; + +use crate::lower_nextsolver::{self, TyLoweringContext}; +use crate::method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}; +use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}; +use crate::next_solver::{ + CanonicalVarKind, FxIndexMap, InternedWrapperNoDebug, RegionAssumptions, SolverDefIds, +}; +use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase}; + +use super::generics::generics; +use super::util::sizedness_constraint_for_ty; +use super::{ + Binder, BoundExistentialPredicate, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause, + Clauses, Const, ConstKind, ErrorGuaranteed, ExprConst, ExternalConstraints, + ExternalConstraintsData, GenericArg, GenericArgs, InternedClausesWrapper, ParamConst, ParamEnv, + ParamTy, PlaceholderConst, PlaceholderTy, PredefinedOpaques, PredefinedOpaquesData, Predicate, + PredicateKind, Term, Ty, TyKind, Tys, ValueConst, + abi::Safety, + fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate}, + generics::Generics, + mapping::ChalkToNextSolver, + region::{ + BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, PlaceholderRegion, Region, + }, +}; +use super::{ClauseKind, SolverDefId, Valtree}; + +#[macro_export] +#[doc(hidden)] +macro_rules! _interned_vec_nolifetime_salsa { + ($name:ident, $ty:ty) => { + interned_vec_nolifetime_salsa!($name, $ty, nofold); + + impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for $name { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.try_fold_with(folder)).collect::<Result<_, _>>()?; + Ok($name::new_(folder.cx().db(), inner)) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Self { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.fold_with(folder)).collect(); + $name::new_(folder.cx().db(), inner) + } + } + + impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for $name { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + use rustc_ast_ir::visit::VisitorResult; + use rustc_type_ir::inherent::SliceLike as _; + rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter()); + V::Result::output() + } + } + }; + ($name:ident, $ty:ty, nofold) => { + #[salsa::interned(no_lifetime, constructor = new_, debug)] + pub struct $name { + #[returns(ref)] + inner_: smallvec::SmallVec<[$ty; 2]>, + } + + impl $name { + pub fn new_from_iter<'db>( + interner: DbInterner<'db>, + data: impl IntoIterator<Item = $ty>, + ) -> Self { + $name::new_(interner.db(), data.into_iter().collect::<smallvec::SmallVec<[_; 2]>>()) + } + + pub fn inner(&self) -> &smallvec::SmallVec<[$ty; 2]> { + // SAFETY: ¯\_(ツ)_/¯ + salsa::with_attached_database(|db| { + let inner = self.inner_(db); + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + } + + impl rustc_type_ir::inherent::SliceLike for $name { + type Item = $ty; + + type IntoIter = <smallvec::SmallVec<[$ty; 2]> as IntoIterator>::IntoIter; + + fn iter(self) -> Self::IntoIter { + self.inner().clone().into_iter() + } + + fn as_slice(&self) -> &[Self::Item] { + self.inner().as_slice() + } + } + + impl IntoIterator for $name { + type Item = $ty; + type IntoIter = <Self as rustc_type_ir::inherent::SliceLike>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + rustc_type_ir::inherent::SliceLike::iter(self) + } + } + + impl Default for $name { + fn default() -> Self { + $name::new_from_iter(DbInterner::conjure(), []) + } + } + }; +} + +pub use crate::_interned_vec_nolifetime_salsa as interned_vec_nolifetime_salsa; + +#[macro_export] +#[doc(hidden)] +macro_rules! _interned_vec_db { + ($name:ident, $ty:ident) => { + interned_vec_db!($name, $ty, nofold); + + impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for $name<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.try_fold_with(folder)).collect::<Result<_, _>>()?; + Ok($name::new_(folder.cx().db(), inner)) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Self { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.fold_with(folder)).collect(); + $name::new_(folder.cx().db(), inner) + } + } + + impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for $name<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + use rustc_ast_ir::visit::VisitorResult; + use rustc_type_ir::inherent::SliceLike as _; + rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter()); + V::Result::output() + } + } + }; + ($name:ident, $ty:ident, nofold) => { + #[salsa::interned(constructor = new_)] + pub struct $name<'db> { + #[returns(ref)] + inner_: smallvec::SmallVec<[$ty<'db>; 2]>, + } + + impl<'db> std::fmt::Debug for $name<'db> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_slice().fmt(fmt) + } + } + + impl<'db> $name<'db> { + pub fn new_from_iter( + interner: DbInterner<'db>, + data: impl IntoIterator<Item = $ty<'db>>, + ) -> Self { + $name::new_(interner.db(), data.into_iter().collect::<smallvec::SmallVec<[_; 2]>>()) + } + + pub fn inner(&self) -> &smallvec::SmallVec<[$ty<'db>; 2]> { + // SAFETY: ¯\_(ツ)_/¯ + salsa::with_attached_database(|db| { + let inner = self.inner_(db); + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + } + + impl<'db> rustc_type_ir::inherent::SliceLike for $name<'db> { + type Item = $ty<'db>; + + type IntoIter = <smallvec::SmallVec<[$ty<'db>; 2]> as IntoIterator>::IntoIter; + + fn iter(self) -> Self::IntoIter { + self.inner().clone().into_iter() + } + + fn as_slice(&self) -> &[Self::Item] { + self.inner().as_slice() + } + } + + impl<'db> IntoIterator for $name<'db> { + type Item = $ty<'db>; + type IntoIter = <Self as rustc_type_ir::inherent::SliceLike>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + rustc_type_ir::inherent::SliceLike::iter(self) + } + } + + impl<'db> Default for $name<'db> { + fn default() -> Self { + $name::new_from_iter(DbInterner::conjure(), []) + } + } + }; +} + +pub use crate::_interned_vec_db as interned_vec_db; + +#[derive(Debug, Copy, Clone)] +pub struct DbInterner<'db> { + pub(crate) db: &'db dyn HirDatabase, + pub(crate) krate: Option<Crate>, + pub(crate) block: Option<BlockId>, +} + +// FIXME: very wrong, see https://github.com/rust-lang/rust/pull/144808 +unsafe impl Send for DbInterner<'_> {} +unsafe impl Sync for DbInterner<'_> {} + +impl<'db> DbInterner<'db> { + // FIXME(next-solver): remove this method + pub fn conjure() -> DbInterner<'db> { + salsa::with_attached_database(|db| DbInterner { + db: unsafe { + std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>(db.as_view()) + }, + krate: None, + block: None, + }) + .expect("db is expected to be attached") + } + + pub fn new_with( + db: &'db dyn HirDatabase, + krate: Option<Crate>, + block: Option<BlockId>, + ) -> DbInterner<'db> { + DbInterner { db, krate, block } + } + + pub fn db(&self) -> &'db dyn HirDatabase { + self.db + } +} + +// This is intentionally left as `()` +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct Span(()); + +impl<'db> inherent::Span<DbInterner<'db>> for Span { + fn dummy() -> Self { + Span(()) + } +} + +interned_vec_nolifetime_salsa!(BoundVarKinds, BoundVarKind, nofold); + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum BoundVarKind { + Ty(BoundTyKind), + Region(BoundRegionKind), + Const, +} + +impl BoundVarKind { + pub fn expect_region(self) -> BoundRegionKind { + match self { + BoundVarKind::Region(lt) => lt, + _ => panic!("expected a region, but found another kind"), + } + } + + pub fn expect_ty(self) -> BoundTyKind { + match self { + BoundVarKind::Ty(ty) => ty, + _ => panic!("expected a type, but found another kind"), + } + } + + pub fn expect_const(self) { + match self { + BoundVarKind::Const => (), + _ => panic!("expected a const, but found another kind"), + } + } +} + +interned_vec_db!(CanonicalVars, CanonicalVarKind, nofold); + +pub struct DepNodeIndex; + +#[derive(Debug)] +pub struct Tracked<T: fmt::Debug + Clone>(T); + +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Placeholder<T> { + pub universe: UniverseIndex, + pub bound: T, +} + +impl<T: std::fmt::Debug> std::fmt::Debug for Placeholder<T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + if self.universe == UniverseIndex::ROOT { + write!(f, "!{:?}", self.bound) + } else { + write!(f, "!{}_{:?}", self.universe.index(), self.bound) + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct AllocId; + +interned_vec_nolifetime_salsa!(VariancesOf, Variance, nofold); + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct VariantIdx(usize); + +// FIXME: could/should store actual data? +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum VariantDef { + Struct(StructId), + Union(UnionId), + Enum(EnumVariantId), +} + +impl VariantDef { + pub fn id(&self) -> VariantId { + match self { + VariantDef::Struct(struct_id) => VariantId::StructId(*struct_id), + VariantDef::Union(union_id) => VariantId::UnionId(*union_id), + VariantDef::Enum(enum_variant_id) => VariantId::EnumVariantId(*enum_variant_id), + } + } + + pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Idx<FieldData>, FieldData)> { + let id: VariantId = match self { + VariantDef::Struct(it) => (*it).into(), + VariantDef::Union(it) => (*it).into(), + VariantDef::Enum(it) => (*it).into(), + }; + id.fields(db).fields().iter().map(|(id, data)| (id, data.clone())).collect() + } +} + +/* +/// Definition of a variant -- a struct's fields or an enum variant. +#[derive(Debug, HashStable, TyEncodable, TyDecodable)] +pub struct VariantDef { + /// `DefId` that identifies the variant itself. + /// If this variant belongs to a struct or union, then this is a copy of its `DefId`. + pub def_id: DefId, + /// `DefId` that identifies the variant's constructor. + /// If this variant is a struct variant, then this is `None`. + pub ctor: Option<(CtorKind, DefId)>, + /// Variant or struct name, maybe empty for anonymous adt (struct or union). + pub name: Symbol, + /// Discriminant of this variant. + pub discr: VariantDiscr, + /// Fields of this variant. + pub fields: IndexVec<FieldIdx, FieldDef>, + /// The error guarantees from parser, if any. + tainted: Option<ErrorGuaranteed>, + /// Flags of the variant (e.g. is field list non-exhaustive)? + flags: VariantFlags, +} +*/ + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct AdtFlags { + is_enum: bool, + is_union: bool, + is_struct: bool, + is_phantom_data: bool, + is_fundamental: bool, + is_box: bool, + is_manually_drop: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AdtDefInner { + pub id: AdtId, + variants: Vec<(VariantIdx, VariantDef)>, + flags: AdtFlags, + repr: ReprOptions, +} + +// We're gonna cheat a little bit and implement `Hash` on only the `DefId` and +// accept there might be collisions for def ids from different crates (or across +// different tests, oh my). +impl std::hash::Hash for AdtDefInner { + #[inline] + fn hash<H: std::hash::Hasher>(&self, s: &mut H) { + self.id.hash(s) + } +} + +#[salsa::interned(no_lifetime, constructor = new_)] +pub struct AdtDef { + #[returns(ref)] + data_: AdtDefInner, +} + +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 = db.struct_signature(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 mut repr = ReprOptions::default(); + repr.align = data.repr.and_then(|r| r.align); + repr.pack = data.repr.and_then(|r| r.pack); + repr.int = data.repr.and_then(|r| r.int); + + let mut repr_flags = ReprFlags::empty(); + if flags.is_box { + repr_flags.insert(ReprFlags::IS_LINEAR); + } + if data.repr.is_some_and(|r| r.c()) { + repr_flags.insert(ReprFlags::IS_C); + } + if data.repr.is_some_and(|r| r.simd()) { + repr_flags.insert(ReprFlags::IS_SIMD); + } + repr.flags = repr_flags; + + (flags, variants, repr) + } + AdtId::UnionId(union_id) => { + let data = db.union_signature(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 mut repr = ReprOptions::default(); + repr.align = data.repr.and_then(|r| r.align); + repr.pack = data.repr.and_then(|r| r.pack); + repr.int = data.repr.and_then(|r| r.int); + + let mut repr_flags = ReprFlags::empty(); + if flags.is_box { + repr_flags.insert(ReprFlags::IS_LINEAR); + } + if data.repr.is_some_and(|r| r.c()) { + repr_flags.insert(ReprFlags::IS_C); + } + if data.repr.is_some_and(|r| r.simd()) { + repr_flags.insert(ReprFlags::IS_SIMD); + } + repr.flags = repr_flags; + + (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 = db.enum_signature(enum_id); + + let mut repr = ReprOptions::default(); + repr.align = data.repr.and_then(|r| r.align); + repr.pack = data.repr.and_then(|r| r.pack); + repr.int = data.repr.and_then(|r| r.int); + + let mut repr_flags = ReprFlags::empty(); + if flags.is_box { + repr_flags.insert(ReprFlags::IS_LINEAR); + } + if data.repr.is_some_and(|r| r.c()) { + repr_flags.insert(ReprFlags::IS_C); + } + if data.repr.is_some_and(|r| r.simd()) { + repr_flags.insert(ReprFlags::IS_SIMD); + } + repr.flags = repr_flags; + + (flags, variants, repr) + } + }; + + AdtDef::new_(db, AdtDefInner { id: def_id, variants, flags, repr }) + } + + pub fn inner(&self) -> &AdtDefInner { + salsa::with_attached_database(|db| { + let inner = self.data_(db); + // SAFETY: ¯\_(ツ)_/¯ + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + pub fn is_enum(&self) -> bool { + self.inner().flags.is_enum + } + + #[inline] + pub fn repr(self) -> ReprOptions { + self.inner().repr + } + + /// 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() + } +} + +impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef { + fn def_id(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::DefId { + SolverDefId::AdtId(self.inner().id) + } + + fn is_struct(self) -> bool { + self.inner().flags.is_struct + } + + fn is_phantom_data(self) -> bool { + self.inner().flags.is_phantom_data + } + + fn is_fundamental(self) -> bool { + self.inner().flags.is_fundamental + } + + fn struct_tail_ty( + self, + interner: DbInterner<'db>, + ) -> Option<EarlyBinder<DbInterner<'db>, Ty<'db>>> { + let db = interner.db(); + let hir_def::AdtId::StructId(struct_id) = self.inner().id else { + return None; + }; + let id: VariantId = struct_id.into(); + let field_types = interner.db().field_types_ns(id); + + field_types.iter().last().map(|f| *f.1) + } + + fn all_field_tys( + self, + interner: DbInterner<'db>, + ) -> EarlyBinder<DbInterner<'db>, impl IntoIterator<Item = Ty<'db>>> { + let db = interner.db(); + // FIXME: this is disabled just to match the behavior with chalk right now + let field_tys = |id: VariantId| { + let variant_data = id.fields(db); + let fields = if variant_data.fields().is_empty() { + vec![] + } else { + let field_types = db.field_types_ns(id); + variant_data + .fields() + .iter() + .map(|(idx, _)| { + let ty = field_types[idx]; + ty.skip_binder() + }) + .collect() + }; + }; + let field_tys = |id: VariantId| vec![]; + let tys: Vec<_> = match self.inner().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 + .enum_variants(db) + .variants + .iter() + .flat_map(|&(variant_id, _, _)| field_tys(variant_id.into())) + .collect(), + }; + + EarlyBinder::bind(tys) + } + + fn sizedness_constraint( + self, + interner: DbInterner<'db>, + sizedness: SizedTraitKind, + ) -> Option<EarlyBinder<DbInterner<'db>, Ty<'db>>> { + if self.is_struct() { + let tail_ty = self.all_field_tys(interner).skip_binder().into_iter().last()?; + + let constraint_ty = sizedness_constraint_for_ty(interner, sizedness, tail_ty)?; + + Some(EarlyBinder::bind(constraint_ty)) + } else { + None + } + } + + fn destructor( + self, + interner: DbInterner<'db>, + ) -> Option<rustc_type_ir::solve::AdtDestructorKind> { + // FIXME(next-solver) + None + } + + fn is_manually_drop(self) -> bool { + self.inner().flags.is_manually_drop + } +} + +impl fmt::Debug for AdtDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + salsa::with_attached_database(|db| match self.inner().id { + AdtId::StructId(struct_id) => { + let data = db.as_view::<dyn HirDatabase>().struct_signature(struct_id); + f.write_str(data.name.as_str()) + } + AdtId::UnionId(union_id) => { + let data = db.as_view::<dyn HirDatabase>().union_signature(union_id); + f.write_str(data.name.as_str()) + } + AdtId::EnumId(enum_id) => { + let data = db.as_view::<dyn HirDatabase>().enum_signature(enum_id); + f.write_str(data.name.as_str()) + } + }) + .unwrap_or_else(|| f.write_str(&format!("AdtDef({:?})", self.inner().id))) + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Features; + +impl<'db> inherent::Features<DbInterner<'db>> for Features { + fn generic_const_exprs(self) -> bool { + false + } + + fn coroutine_clone(self) -> bool { + false + } + + fn associated_const_equality(self) -> bool { + false + } + + fn feature_bound_holds_in_crate(self, symbol: ()) -> bool { + false + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct UnsizingParams(pub(crate) DenseBitSet<u32>); + +impl std::ops::Deref for UnsizingParams { + type Target = DenseBitSet<u32>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub type PatternKind<'db> = rustc_type_ir::PatternKind<DbInterner<'db>>; + +#[salsa::interned(constructor = new_, debug)] +pub struct Pattern<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug<PatternKind<'db>>, +} + +impl<'db> std::fmt::Debug for InternedWrapperNoDebug<PatternKind<'db>> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl<'db> Pattern<'db> { + pub fn new(interner: DbInterner<'db>, kind: PatternKind<'db>) -> Self { + Pattern::new_(interner.db(), InternedWrapperNoDebug(kind)) + } + + pub fn inner(&self) -> &PatternKind<'db> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Ty<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> Flags for Pattern<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + match self.inner() { + PatternKind::Range { start, end } => { + FlagComputation::for_const_kind(&start.kind()).flags + | FlagComputation::for_const_kind(&end.kind()).flags + } + PatternKind::Or(pats) => { + let mut flags = pats.as_slice()[0].flags(); + for pat in pats.as_slice()[1..].iter() { + flags |= pat.flags(); + } + flags + } + } + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + match self.inner() { + PatternKind::Range { start, end } => { + start.outer_exclusive_binder().max(end.outer_exclusive_binder()) + } + PatternKind::Or(pats) => { + let mut idx = pats.as_slice()[0].outer_exclusive_binder(); + for pat in pats.as_slice()[1..].iter() { + idx = idx.max(pat.outer_exclusive_binder()); + } + idx + } + } + } +} + +impl<'db> rustc_type_ir::inherent::IntoKind for Pattern<'db> { + type Kind = rustc_type_ir::PatternKind<DbInterner<'db>>; + fn kind(self) -> Self::Kind { + *self.inner() + } +} + +impl<'db> rustc_type_ir::relate::Relate<DbInterner<'db>> for Pattern<'db> { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + let tcx = relation.cx(); + match (a.kind(), b.kind()) { + ( + PatternKind::Range { start: start_a, end: end_a }, + PatternKind::Range { start: start_b, end: end_b }, + ) => { + let start = relation.relate(start_a, start_b)?; + let end = relation.relate(end_a, end_b)?; + Ok(Pattern::new(tcx, PatternKind::Range { start, end })) + } + (PatternKind::Or(a), PatternKind::Or(b)) => { + if a.len() != b.len() { + return Err(TypeError::Mismatch); + } + let pats = CollectAndApply::collect_and_apply( + std::iter::zip(a.iter(), b.iter()).map(|(a, b)| relation.relate(a, b)), + |g| PatList::new_from_iter(tcx, g.iter().cloned()), + )?; + Ok(Pattern::new(tcx, PatternKind::Or(pats))) + } + (PatternKind::Range { .. } | PatternKind::Or(_), _) => Err(TypeError::Mismatch), + } + } +} + +interned_vec_db!(PatList, Pattern); + +impl<'db> rustc_type_ir::Interner for DbInterner<'db> { + type DefId = SolverDefId; + type LocalDefId = SolverDefId; + type LocalDefIds = SolverDefIds; + type Span = Span; + + type GenericArgs = GenericArgs<'db>; + type GenericArgsSlice = GenericArgs<'db>; + type GenericArg = GenericArg<'db>; + + type Term = Term<'db>; + + type BoundVarKinds = BoundVarKinds; + type BoundVarKind = BoundVarKind; + + type PredefinedOpaques = PredefinedOpaques<'db>; + + fn mk_predefined_opaques_in_body( + self, + data: rustc_type_ir::solve::PredefinedOpaquesData<Self>, + ) -> Self::PredefinedOpaques { + PredefinedOpaques::new(self, data) + } + + type CanonicalVarKinds = CanonicalVars<'db>; + + fn mk_canonical_var_kinds( + self, + kinds: &[rustc_type_ir::CanonicalVarKind<Self>], + ) -> Self::CanonicalVarKinds { + CanonicalVars::new_from_iter(self, kinds.iter().cloned()) + } + + type ExternalConstraints = ExternalConstraints<'db>; + + fn mk_external_constraints( + self, + data: rustc_type_ir::solve::ExternalConstraintsData<Self>, + ) -> Self::ExternalConstraints { + ExternalConstraints::new(self, data) + } + + type DepNodeIndex = DepNodeIndex; + + type Tracked<T: fmt::Debug + Clone> = Tracked<T>; + + type Ty = Ty<'db>; + type Tys = Tys<'db>; + type FnInputTys = Tys<'db>; + type ParamTy = ParamTy; + type BoundTy = BoundTy; + type PlaceholderTy = PlaceholderTy; + type Symbol = (); + + type ErrorGuaranteed = ErrorGuaranteed; + type BoundExistentialPredicates = BoundExistentialPredicates<'db>; + type AllocId = AllocId; + type Pat = Pattern<'db>; + type PatList = PatList<'db>; + type Safety = Safety; + type Abi = FnAbi; + + type Const = Const<'db>; + type PlaceholderConst = PlaceholderConst; + type ParamConst = ParamConst; + type BoundConst = rustc_type_ir::BoundVar; + type ValueConst = ValueConst<'db>; + type ValTree = Valtree<'db>; + type ExprConst = ExprConst; + + type Region = Region<'db>; + type EarlyParamRegion = EarlyParamRegion; + type LateParamRegion = LateParamRegion; + type BoundRegion = BoundRegion; + type PlaceholderRegion = PlaceholderRegion; + + type RegionAssumptions = RegionAssumptions<'db>; + + type ParamEnv = ParamEnv<'db>; + type Predicate = Predicate<'db>; + type Clause = Clause<'db>; + type Clauses = Clauses<'db>; + + type GenericsOf = Generics; + + type VariancesOf = VariancesOf; + + type AdtDef = AdtDef; + + type Features = Features; + + fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs { + GenericArgs::new_from_iter(self, args.iter().cloned()) + } + + fn mk_args_from_iter<I, T>(self, args: I) -> T::Output + where + I: Iterator<Item = T>, + T: rustc_type_ir::CollectAndApply<Self::GenericArg, Self::GenericArgs>, + { + CollectAndApply::collect_and_apply(args, |g| { + GenericArgs::new_from_iter(self, g.iter().cloned()) + }) + } + + type UnsizingParams = UnsizingParams; + + fn mk_tracked<T: fmt::Debug + Clone>( + self, + data: T, + dep_node: Self::DepNodeIndex, + ) -> Self::Tracked<T> { + Tracked(data) + } + + fn get_tracked<T: fmt::Debug + Clone>(self, tracked: &Self::Tracked<T>) -> T { + tracked.0.clone() + } + + fn with_cached_task<T>(self, task: impl FnOnce() -> T) -> (T, Self::DepNodeIndex) { + (task(), DepNodeIndex) + } + + fn with_global_cache<R>( + self, + f: impl FnOnce(&mut rustc_type_ir::search_graph::GlobalCache<Self>) -> R, + ) -> R { + salsa::with_attached_database(|db| { + tls_cache::with_cache( + unsafe { + std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>( + db.as_view::<dyn HirDatabase>(), + ) + }, + f, + ) + }) + .unwrap() + } + + fn canonical_param_env_cache_get_or_insert<R>( + self, + param_env: Self::ParamEnv, + f: impl FnOnce() -> rustc_type_ir::CanonicalParamEnvCacheEntry<Self>, + from_entry: impl FnOnce(&rustc_type_ir::CanonicalParamEnvCacheEntry<Self>) -> R, + ) -> R { + from_entry(&f()) + } + + fn evaluation_is_concurrent(&self) -> bool { + false + } + + fn expand_abstract_consts<T: rustc_type_ir::TypeFoldable<Self>>(self, _: T) -> T { + unreachable!("only used by the old trait solver in rustc"); + } + + fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf { + generics(self.db(), def_id) + } + + fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf { + match def_id { + SolverDefId::FunctionId(def_id) => VariancesOf::new_from_iter( + self, + self.db() + .variances_of(hir_def::GenericDefId::FunctionId(def_id)) + .as_deref() + .unwrap_or_default() + .iter() + .map(|v| v.to_nextsolver(self)), + ), + SolverDefId::AdtId(def_id) => VariancesOf::new_from_iter( + self, + self.db() + .variances_of(hir_def::GenericDefId::AdtId(def_id)) + .as_deref() + .unwrap_or_default() + .iter() + .map(|v| v.to_nextsolver(self)), + ), + SolverDefId::InternedOpaqueTyId(_def_id) => { + // FIXME(next-solver): track variances + // + // We compute them based on the only `Ty` level info in rustc, + // move `variances_of_opaque` into `rustc_next_trait_solver` for reuse. + VariancesOf::new_from_iter( + self, + (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant), + ) + } + _ => VariancesOf::new_from_iter(self, []), + } + } + + fn type_of(self, def_id: Self::DefId) -> EarlyBinder<Self, Self::Ty> { + let def_id = match def_id { + SolverDefId::TypeAliasId(id) => { + use hir_def::Lookup; + match id.lookup(self.db()).container { + ItemContainerId::ImplId(it) => it, + _ => panic!("assoc ty value should be in impl"), + }; + crate::TyDefId::TypeAliasId(id) + } + SolverDefId::AdtId(id) => crate::TyDefId::AdtId(id), + // FIXME(next-solver): This uses the types of `query mir_borrowck` in rustc. + // + // We currently always use the type from HIR typeck which ignores regions. This + // should be fine. + SolverDefId::InternedOpaqueTyId(_) => { + return self.type_of_opaque_hir_typeck(def_id); + } + _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"), + }; + self.db().ty_ns(def_id) + } + + fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef { + let def_id = match adt_def_id { + SolverDefId::AdtId(adt_id) => adt_id, + _ => panic!("Invalid DefId passed to adt_def"), + }; + AdtDef::new(def_id, self) + } + + fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy<Self>) -> AliasTyKind { + match alias.def_id { + SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque, + SolverDefId::TypeAliasId(_) => AliasTyKind::Projection, + _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), + } + } + + fn alias_term_kind( + self, + alias: rustc_type_ir::AliasTerm<Self>, + ) -> rustc_type_ir::AliasTermKind { + match alias.def_id { + SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy, + SolverDefId::TypeAliasId(_) => AliasTermKind::ProjectionTy, + SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst, + _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), + } + } + + fn trait_ref_and_own_args_for_alias( + self, + def_id: Self::DefId, + args: Self::GenericArgs, + ) -> (rustc_type_ir::TraitRef<Self>, Self::GenericArgsSlice) { + let trait_def_id = self.parent(def_id); + let trait_generics = self.generics_of(trait_def_id); + let trait_args = GenericArgs::new_from_iter( + self, + args.as_slice()[0..trait_generics.own_params.len()].iter().cloned(), + ); + let alias_args = + GenericArgs::new_from_iter(self, args.iter().skip(trait_generics.own_params.len())); + (TraitRef::new_from_args(self, trait_def_id, trait_args), alias_args) + } + + fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool { + // FIXME + true + } + + fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) {} + + fn debug_assert_existential_args_compatible( + self, + def_id: Self::DefId, + args: Self::GenericArgs, + ) { + } + + fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output + where + I: Iterator<Item = T>, + T: rustc_type_ir::CollectAndApply<Self::Ty, Self::Tys>, + { + CollectAndApply::collect_and_apply(args, |g| Tys::new_from_iter(self, g.iter().cloned())) + } + + fn parent(self, def_id: Self::DefId) -> Self::DefId { + use hir_def::Lookup; + + let container = match def_id { + SolverDefId::FunctionId(it) => it.lookup(self.db()).container, + SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container, + SolverDefId::ForeignId(it) => it.lookup(self.db()).container, + SolverDefId::ConstId(it) => it.lookup(self.db()).container, + SolverDefId::InternedClosureId(it) => { + return self + .db() + .lookup_intern_closure(it) + .0 + .as_generic_def_id(self.db()) + .unwrap() + .into(); + } + SolverDefId::InternedCoroutineId(it) => { + return self + .db() + .lookup_intern_coroutine(it) + .0 + .as_generic_def_id(self.db()) + .unwrap() + .into(); + } + SolverDefId::StaticId(_) + | SolverDefId::AdtId(_) + | SolverDefId::TraitId(_) + | SolverDefId::ImplId(_) + | SolverDefId::Ctor(..) + | SolverDefId::InternedOpaqueTyId(..) => panic!(), + }; + + match container { + ItemContainerId::ImplId(it) => it.into(), + ItemContainerId::TraitId(it) => it.into(), + ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => panic!(), + } + } + + fn recursion_limit(self) -> usize { + 50 + } + + fn features(self) -> Self::Features { + Features + } + + fn fn_sig( + self, + def_id: Self::DefId, + ) -> EarlyBinder<Self, rustc_type_ir::Binder<Self, rustc_type_ir::FnSig<Self>>> { + let id = match def_id { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(ctor) => match ctor { + super::Ctor::Struct(struct_id) => CallableDefId::StructId(struct_id), + super::Ctor::Enum(enum_variant_id) => CallableDefId::EnumVariantId(enum_variant_id), + }, + def => unreachable!("{:?}", def), + }; + self.db().callable_item_signature_ns(id) + } + + fn coroutine_movability(self, def_id: Self::DefId) -> rustc_ast_ir::Movability { + unimplemented!() + } + + fn coroutine_for_closure(self, def_id: Self::DefId) -> Self::DefId { + unimplemented!() + } + + fn generics_require_sized_self(self, def_id: Self::DefId) -> bool { + let sized_trait = + LangItem::Sized.resolve_trait(self.db(), self.krate.expect("Must have self.krate")); + let Some(sized_id) = sized_trait else { + return false; /* No Sized trait, can't require it! */ + }; + let sized_def_id = sized_id.into(); + + // 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, .. }) + ) + } + ClauseKind::RegionOutlives(_) + | ClauseKind::TypeOutlives(_) + | ClauseKind::Projection(_) + | ClauseKind::ConstArgHasType(_, _) + | ClauseKind::WellFormed(_) + | ClauseKind::ConstEvaluatable(_) + | ClauseKind::HostEffect(..) + | ClauseKind::UnstableFeature(_) => false, + }) + } + + #[tracing::instrument(skip(self), ret)] + fn item_bounds( + self, + def_id: Self::DefId, + ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> { + explicit_item_bounds(self, def_id).map_bound(|bounds| { + Clauses::new_from_iter(self, elaborate(self, bounds).collect::<Vec<_>>()) + }) + } + + #[tracing::instrument(skip(self), ret)] + fn item_self_bounds( + self, + def_id: Self::DefId, + ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> { + explicit_item_bounds(self, def_id).map_bound(|bounds| { + Clauses::new_from_iter( + self, + elaborate(self, bounds).filter_only_self().collect::<Vec<_>>(), + ) + }) + } + + fn item_non_self_bounds( + self, + def_id: Self::DefId, + ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> { + let all_bounds: FxHashSet<_> = self.item_bounds(def_id).skip_binder().into_iter().collect(); + let own_bounds: FxHashSet<_> = + self.item_self_bounds(def_id).skip_binder().into_iter().collect(); + if all_bounds.len() == own_bounds.len() { + EarlyBinder::bind(Clauses::new_from_iter(self, [])) + } else { + EarlyBinder::bind(Clauses::new_from_iter( + self, + all_bounds.difference(&own_bounds).cloned(), + )) + } + } + + #[tracing::instrument(level = "debug", skip(self), ret)] + fn predicates_of( + self, + def_id: Self::DefId, + ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> { + let predicates = self.db().generic_predicates_ns(def_id.try_into().unwrap()); + let predicates: Vec<_> = predicates.iter().cloned().collect(); + EarlyBinder::bind(predicates.into_iter()) + } + + #[tracing::instrument(level = "debug", skip(self), ret)] + fn own_predicates_of( + self, + def_id: Self::DefId, + ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> { + let predicates = self.db().generic_predicates_without_parent_ns(def_id.try_into().unwrap()); + let predicates: Vec<_> = predicates.iter().cloned().collect(); + EarlyBinder::bind(predicates.into_iter()) + } + + #[tracing::instrument(skip(self), ret)] + fn explicit_super_predicates_of( + self, + def_id: Self::DefId, + ) -> EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>> { + let predicates: Vec<(Clause<'db>, Span)> = self + .db() + .generic_predicates_ns(def_id.try_into().unwrap()) + .iter() + .cloned() + .map(|p| (p, Span::dummy())) + .collect(); + EarlyBinder::bind(predicates) + } + + #[tracing::instrument(skip(self), ret)] + fn explicit_implied_predicates_of( + self, + def_id: Self::DefId, + ) -> EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>> { + let predicates: Vec<(Clause<'db>, Span)> = self + .db() + .generic_predicates_ns(def_id.try_into().unwrap()) + .iter() + .cloned() + .map(|p| (p, Span::dummy())) + .collect(); + EarlyBinder::bind(predicates) + } + + fn impl_super_outlives( + self, + impl_def_id: Self::DefId, + ) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => unreachable!(), + }; + let trait_ref = self.db().impl_trait_ns(impl_id).expect("expected an impl of trait"); + trait_ref.map_bound(|trait_ref| { + let clause: Clause<'_> = trait_ref.upcast(self); + Clauses::new_from_iter( + self, + rustc_type_ir::elaborate::elaborate(self, [clause]).filter(|clause| { + matches!( + clause.kind().skip_binder(), + ClauseKind::TypeOutlives(_) | ClauseKind::RegionOutlives(_) + ) + }), + ) + }) + } + + fn const_conditions( + self, + def_id: Self::DefId, + ) -> EarlyBinder< + Self, + impl IntoIterator<Item = rustc_type_ir::Binder<Self, rustc_type_ir::TraitRef<Self>>>, + > { + EarlyBinder::bind([unimplemented!()]) + } + + fn has_target_features(self, def_id: Self::DefId) -> bool { + false + } + + fn require_lang_item( + self, + lang_item: rustc_type_ir::lang_items::TraitSolverLangItem, + ) -> Self::DefId { + let lang_item = match lang_item { + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFn => LangItem::AsyncFn, + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindHelper => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindUpvars => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnMut => LangItem::AsyncFnMut, + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnce => LangItem::AsyncFnOnce, + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnceOutput => { + LangItem::AsyncFnOnceOutput + } + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncIterator => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::CallOnceFuture => { + LangItem::CallOnceFuture + } + rustc_type_ir::lang_items::TraitSolverLangItem::CallRefFuture => { + LangItem::CallRefFuture + } + rustc_type_ir::lang_items::TraitSolverLangItem::Clone => LangItem::Clone, + rustc_type_ir::lang_items::TraitSolverLangItem::Copy => LangItem::Copy, + rustc_type_ir::lang_items::TraitSolverLangItem::Coroutine => LangItem::Coroutine, + rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineReturn => { + LangItem::CoroutineReturn + } + rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineYield => { + LangItem::CoroutineYield + } + rustc_type_ir::lang_items::TraitSolverLangItem::Destruct => LangItem::Destruct, + rustc_type_ir::lang_items::TraitSolverLangItem::DiscriminantKind => { + LangItem::DiscriminantKind + } + rustc_type_ir::lang_items::TraitSolverLangItem::Drop => LangItem::Drop, + rustc_type_ir::lang_items::TraitSolverLangItem::DynMetadata => LangItem::DynMetadata, + rustc_type_ir::lang_items::TraitSolverLangItem::Fn => LangItem::Fn, + rustc_type_ir::lang_items::TraitSolverLangItem::FnMut => LangItem::FnMut, + rustc_type_ir::lang_items::TraitSolverLangItem::FnOnce => LangItem::FnOnce, + rustc_type_ir::lang_items::TraitSolverLangItem::FnPtrTrait => LangItem::FnPtrTrait, + rustc_type_ir::lang_items::TraitSolverLangItem::FusedIterator => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::Future => LangItem::Future, + rustc_type_ir::lang_items::TraitSolverLangItem::FutureOutput => LangItem::FutureOutput, + rustc_type_ir::lang_items::TraitSolverLangItem::Iterator => LangItem::Iterator, + rustc_type_ir::lang_items::TraitSolverLangItem::Metadata => LangItem::Metadata, + rustc_type_ir::lang_items::TraitSolverLangItem::Option => LangItem::Option, + rustc_type_ir::lang_items::TraitSolverLangItem::PointeeTrait => LangItem::PointeeTrait, + rustc_type_ir::lang_items::TraitSolverLangItem::Poll => LangItem::Poll, + rustc_type_ir::lang_items::TraitSolverLangItem::Sized => LangItem::Sized, + rustc_type_ir::lang_items::TraitSolverLangItem::MetaSized => LangItem::MetaSized, + rustc_type_ir::lang_items::TraitSolverLangItem::PointeeSized => LangItem::PointeeSized, + rustc_type_ir::lang_items::TraitSolverLangItem::TransmuteTrait => { + LangItem::TransmuteTrait + } + rustc_type_ir::lang_items::TraitSolverLangItem::Tuple => LangItem::Tuple, + rustc_type_ir::lang_items::TraitSolverLangItem::Unpin => LangItem::Unpin, + rustc_type_ir::lang_items::TraitSolverLangItem::Unsize => LangItem::Unsize, + rustc_type_ir::lang_items::TraitSolverLangItem::BikeshedGuaranteedNoDrop => { + unimplemented!() + } + }; + let target = hir_def::lang_item::lang_item( + self.db(), + self.krate.expect("Must have self.krate"), + lang_item, + ) + .unwrap_or_else(|| panic!("Lang item {lang_item:?} required but not found.")); + match target { + hir_def::lang_item::LangItemTarget::EnumId(enum_id) => enum_id.into(), + hir_def::lang_item::LangItemTarget::Function(function_id) => function_id.into(), + hir_def::lang_item::LangItemTarget::ImplDef(impl_id) => impl_id.into(), + hir_def::lang_item::LangItemTarget::Static(static_id) => static_id.into(), + hir_def::lang_item::LangItemTarget::Struct(struct_id) => struct_id.into(), + hir_def::lang_item::LangItemTarget::Union(union_id) => union_id.into(), + hir_def::lang_item::LangItemTarget::TypeAlias(type_alias_id) => type_alias_id.into(), + hir_def::lang_item::LangItemTarget::Trait(trait_id) => trait_id.into(), + hir_def::lang_item::LangItemTarget::EnumVariant(enum_variant_id) => unimplemented!(), + } + } + + #[allow(clippy::match_like_matches_macro)] + fn is_lang_item( + self, + def_id: Self::DefId, + lang_item: rustc_type_ir::lang_items::TraitSolverLangItem, + ) -> bool { + use rustc_type_ir::lang_items::TraitSolverLangItem::*; + + // FIXME: derive PartialEq on TraitSolverLangItem + self.as_lang_item(def_id).map_or(false, |l| match (l, lang_item) { + (AsyncFn, AsyncFn) => true, + (AsyncFnKindHelper, AsyncFnKindHelper) => true, + (AsyncFnKindUpvars, AsyncFnKindUpvars) => true, + (AsyncFnMut, AsyncFnMut) => true, + (AsyncFnOnce, AsyncFnOnce) => true, + (AsyncFnOnceOutput, AsyncFnOnceOutput) => true, + (AsyncIterator, AsyncIterator) => true, + (CallOnceFuture, CallOnceFuture) => true, + (CallRefFuture, CallRefFuture) => true, + (Clone, Clone) => true, + (Copy, Copy) => true, + (Coroutine, Coroutine) => true, + (CoroutineReturn, CoroutineReturn) => true, + (CoroutineYield, CoroutineYield) => true, + (Destruct, Destruct) => true, + (DiscriminantKind, DiscriminantKind) => true, + (Drop, Drop) => true, + (DynMetadata, DynMetadata) => true, + (Fn, Fn) => true, + (FnMut, FnMut) => true, + (FnOnce, FnOnce) => true, + (FnPtrTrait, FnPtrTrait) => true, + (FusedIterator, FusedIterator) => true, + (Future, Future) => true, + (FutureOutput, FutureOutput) => true, + (Iterator, Iterator) => true, + (Metadata, Metadata) => true, + (Option, Option) => true, + (PointeeTrait, PointeeTrait) => true, + (Poll, Poll) => true, + (Sized, Sized) => true, + (TransmuteTrait, TransmuteTrait) => true, + (Tuple, Tuple) => true, + (Unpin, Unpin) => true, + (Unsize, Unsize) => true, + _ => false, + }) + } + + fn as_lang_item( + self, + def_id: Self::DefId, + ) -> Option<rustc_type_ir::lang_items::TraitSolverLangItem> { + use rustc_type_ir::lang_items::TraitSolverLangItem; + + let def_id: AttrDefId = match def_id { + SolverDefId::TraitId(id) => id.into(), + SolverDefId::TypeAliasId(id) => id.into(), + _ => panic!("Unexpected SolverDefId in as_lang_item"), + }; + let lang_item = self.db().lang_attr(def_id)?; + Some(match lang_item { + LangItem::Sized => TraitSolverLangItem::Sized, + LangItem::MetaSized => TraitSolverLangItem::MetaSized, + LangItem::PointeeSized => TraitSolverLangItem::PointeeSized, + LangItem::Unsize => TraitSolverLangItem::Unsize, + LangItem::StructuralPeq => return None, + LangItem::StructuralTeq => return None, + LangItem::Copy => TraitSolverLangItem::Copy, + LangItem::Clone => TraitSolverLangItem::Clone, + LangItem::Sync => return None, + LangItem::DiscriminantKind => TraitSolverLangItem::DiscriminantKind, + LangItem::Discriminant => return None, + LangItem::PointeeTrait => TraitSolverLangItem::PointeeTrait, + LangItem::Metadata => TraitSolverLangItem::Metadata, + LangItem::DynMetadata => TraitSolverLangItem::DynMetadata, + LangItem::Freeze => return None, + LangItem::FnPtrTrait => TraitSolverLangItem::FnPtrTrait, + LangItem::FnPtrAddr => return None, + LangItem::Drop => TraitSolverLangItem::Drop, + LangItem::Destruct => TraitSolverLangItem::Destruct, + LangItem::CoerceUnsized => return None, + LangItem::DispatchFromDyn => return None, + LangItem::TransmuteOpts => return None, + LangItem::TransmuteTrait => TraitSolverLangItem::TransmuteTrait, + LangItem::Add => return None, + LangItem::Sub => return None, + LangItem::Mul => return None, + LangItem::Div => return None, + LangItem::Rem => return None, + LangItem::Neg => return None, + LangItem::Not => return None, + LangItem::BitXor => return None, + LangItem::BitAnd => return None, + LangItem::BitOr => return None, + LangItem::Shl => return None, + LangItem::Shr => return None, + LangItem::AddAssign => return None, + LangItem::SubAssign => return None, + LangItem::MulAssign => return None, + LangItem::DivAssign => return None, + LangItem::RemAssign => return None, + LangItem::BitXorAssign => return None, + LangItem::BitAndAssign => return None, + LangItem::BitOrAssign => return None, + LangItem::ShlAssign => return None, + LangItem::ShrAssign => return None, + LangItem::Index => return None, + LangItem::IndexMut => return None, + LangItem::UnsafeCell => return None, + LangItem::VaList => return None, + LangItem::Deref => return None, + LangItem::DerefMut => return None, + LangItem::DerefTarget => return None, + LangItem::Receiver => return None, + LangItem::Fn => TraitSolverLangItem::Fn, + LangItem::FnMut => TraitSolverLangItem::FnMut, + LangItem::FnOnce => TraitSolverLangItem::FnOnce, + LangItem::FnOnceOutput => return None, + LangItem::Future => TraitSolverLangItem::Future, + LangItem::CoroutineState => return None, + LangItem::Coroutine => TraitSolverLangItem::Coroutine, + LangItem::CoroutineReturn => TraitSolverLangItem::CoroutineReturn, + LangItem::CoroutineYield => TraitSolverLangItem::CoroutineYield, + LangItem::Unpin => TraitSolverLangItem::Unpin, + LangItem::Pin => return None, + LangItem::PartialEq => return None, + LangItem::PartialOrd => return None, + LangItem::CVoid => return None, + LangItem::Panic => return None, + LangItem::PanicNounwind => return None, + LangItem::PanicFmt => return None, + LangItem::PanicDisplay => return None, + LangItem::ConstPanicFmt => return None, + LangItem::PanicBoundsCheck => return None, + LangItem::PanicMisalignedPointerDereference => return None, + LangItem::PanicInfo => return None, + LangItem::PanicLocation => return None, + LangItem::PanicImpl => return None, + LangItem::PanicCannotUnwind => return None, + LangItem::BeginPanic => return None, + LangItem::FormatAlignment => return None, + LangItem::FormatArgument => return None, + LangItem::FormatArguments => return None, + LangItem::FormatCount => return None, + LangItem::FormatPlaceholder => return None, + LangItem::FormatUnsafeArg => return None, + LangItem::ExchangeMalloc => return None, + LangItem::BoxFree => return None, + LangItem::DropInPlace => return None, + LangItem::AllocLayout => return None, + LangItem::Start => return None, + LangItem::EhPersonality => return None, + LangItem::EhCatchTypeinfo => return None, + LangItem::OwnedBox => return None, + LangItem::PhantomData => return None, + LangItem::ManuallyDrop => return None, + LangItem::MaybeUninit => return None, + LangItem::AlignOffset => return None, + LangItem::Termination => return None, + LangItem::Try => return None, + LangItem::Tuple => TraitSolverLangItem::Tuple, + LangItem::SliceLen => return None, + LangItem::TryTraitFromResidual => return None, + LangItem::TryTraitFromOutput => return None, + LangItem::TryTraitBranch => return None, + LangItem::TryTraitFromYeet => return None, + LangItem::PointerLike => return None, + LangItem::ConstParamTy => return None, + LangItem::Poll => TraitSolverLangItem::Poll, + LangItem::PollReady => return None, + LangItem::PollPending => return None, + LangItem::ResumeTy => return None, + LangItem::GetContext => return None, + LangItem::Context => return None, + LangItem::FuturePoll => return None, + LangItem::FutureOutput => TraitSolverLangItem::FutureOutput, + LangItem::Option => TraitSolverLangItem::Option, + LangItem::OptionSome => return None, + LangItem::OptionNone => return None, + LangItem::ResultOk => return None, + LangItem::ResultErr => return None, + LangItem::ControlFlowContinue => return None, + LangItem::ControlFlowBreak => return None, + LangItem::IntoFutureIntoFuture => return None, + LangItem::IntoIterIntoIter => return None, + LangItem::IteratorNext => return None, + LangItem::Iterator => TraitSolverLangItem::Iterator, + LangItem::PinNewUnchecked => return None, + LangItem::RangeFrom => return None, + LangItem::RangeFull => return None, + LangItem::RangeInclusiveStruct => return None, + LangItem::RangeInclusiveNew => return None, + LangItem::Range => return None, + LangItem::RangeToInclusive => return None, + LangItem::RangeTo => return None, + LangItem::String => return None, + LangItem::CStr => return None, + LangItem::AsyncFn => TraitSolverLangItem::AsyncFn, + LangItem::AsyncFnMut => TraitSolverLangItem::AsyncFnMut, + LangItem::AsyncFnOnce => TraitSolverLangItem::AsyncFnOnce, + LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput, + LangItem::CallRefFuture => TraitSolverLangItem::CallRefFuture, + LangItem::CallOnceFuture => TraitSolverLangItem::CallOnceFuture, + LangItem::Ordering => return None, + LangItem::PanicNullPointerDereference => return None, + LangItem::ReceiverTarget => return None, + LangItem::UnsafePinned => return None, + LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput, + }) + } + + fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId> { + let trait_ = match def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + trait_.trait_items(self.db()).associated_types().map(|id| id.into()) + } + + fn for_each_relevant_impl( + self, + trait_def_id: Self::DefId, + self_ty: Self::Ty, + mut f: impl FnMut(Self::DefId), + ) { + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("for_each_relevant_impl called for non-trait"), + }; + + let self_ty_fp = TyFingerprint::for_trait_impl_ns(&self_ty); + let fps: &[TyFingerprint] = match self_ty.kind() { + TyKind::Infer(InferTy::IntVar(..)) => &ALL_INT_FPS, + TyKind::Infer(InferTy::FloatVar(..)) => &ALL_FLOAT_FPS, + _ => self_ty_fp.as_slice(), + }; + + if fps.is_empty() { + for_trait_impls( + self.db(), + self.krate.expect("Must have self.krate"), + self.block, + trait_, + self_ty_fp, + |impls| { + for i in impls.for_trait(trait_) { + use rustc_type_ir::TypeVisitable; + let contains_errors = self.db().impl_trait_ns(i).map_or(false, |b| { + b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break() + }); + if contains_errors { + continue; + } + + f(SolverDefId::ImplId(i)); + } + ControlFlow::Continue(()) + }, + ); + } else { + for_trait_impls( + self.db(), + self.krate.expect("Must have self.krate"), + self.block, + trait_, + self_ty_fp, + |impls| { + for fp in fps { + for i in impls.for_trait_and_self_ty(trait_, *fp) { + use rustc_type_ir::TypeVisitable; + let contains_errors = self.db().impl_trait_ns(i).map_or(false, |b| { + b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break() + }); + if contains_errors { + continue; + } + + f(SolverDefId::ImplId(i)); + } + } + ControlFlow::Continue(()) + }, + ); + } + } + + fn has_item_definition(self, def_id: Self::DefId) -> bool { + // FIXME(next-solver): should check if the associated item has a value. + true + } + + fn impl_is_default(self, impl_def_id: Self::DefId) -> bool { + // FIXME + false + } + + #[tracing::instrument(skip(self), ret)] + fn impl_trait_ref( + self, + impl_def_id: Self::DefId, + ) -> EarlyBinder<Self, rustc_type_ir::TraitRef<Self>> { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => panic!("Unexpected SolverDefId in impl_trait_ref"), + }; + + let db = self.db(); + + db.impl_trait_ns(impl_id) + // ImplIds for impls where the trait ref can't be resolved should never reach trait solving + .expect("invalid impl passed to trait solver") + } + + fn impl_polarity(self, impl_def_id: Self::DefId) -> rustc_type_ir::ImplPolarity { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => unreachable!(), + }; + let impl_data = self.db().impl_signature(impl_id); + if impl_data.flags.contains(ImplFlags::NEGATIVE) { + ImplPolarity::Negative + } else { + ImplPolarity::Positive + } + } + + fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool { + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Unexpected SolverDefId in trait_is_auto"), + }; + let trait_data = self.db().trait_signature(trait_); + trait_data.flags.contains(TraitFlags::AUTO) + } + + fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool { + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Unexpected SolverDefId in trait_is_alias"), + }; + let trait_data = self.db().trait_signature(trait_); + trait_data.flags.contains(TraitFlags::ALIAS) + } + + fn trait_is_dyn_compatible(self, trait_def_id: Self::DefId) -> bool { + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + crate::dyn_compatibility::dyn_compatibility(self.db(), trait_).is_none() + } + + fn trait_is_fundamental(self, def_id: Self::DefId) -> bool { + let trait_ = match def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Unexpected SolverDefId in trait_is_fundamental"), + }; + let trait_data = self.db().trait_signature(trait_); + trait_data.flags.contains(TraitFlags::FUNDAMENTAL) + } + + fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool { + // FIXME(next-solver): should check the `TraitFlags` for + // the `#[rustc_do_not_implement_via_object]` flag + true + } + + fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed { + panic!("Bug encountered in next-trait-solver.") + } + + fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + true + } + + fn coroutine_is_async(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + true + } + + fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams { + let id = match adt_def_id { + SolverDefId::AdtId(id) => id, + _ => unreachable!(), + }; + let def = AdtDef::new(id, self); + let num_params = self.generics_of(adt_def_id).count(); + + let maybe_unsizing_param_idx = |arg: GenericArg<'db>| match arg.kind() { + GenericArgKind::Type(ty) => match ty.kind() { + rustc_type_ir::TyKind::Param(p) => Some(p.index), + _ => None, + }, + GenericArgKind::Lifetime(_) => None, + GenericArgKind::Const(ct) => match ct.kind() { + rustc_type_ir::ConstKind::Param(p) => Some(p.index), + _ => None, + }, + }; + + // The last field of the structure has to exist and contain type/const parameters. + let variant = def.non_enum_variant(); + let fields = variant.fields(self.db()); + let Some((tail_field, prefix_fields)) = fields.split_last() else { + return UnsizingParams(DenseBitSet::new_empty(num_params)); + }; + + let field_types = self.db().field_types_ns(variant.id()); + let mut unsizing_params = DenseBitSet::new_empty(num_params); + let ty = field_types[tail_field.0]; + for arg in ty.instantiate_identity().walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.insert(i); + } + } + + // Ensure none of the other fields mention the parameters used + // in unsizing. + for field in prefix_fields { + for arg in field_types[field.0].instantiate_identity().walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.remove(i); + } + } + } + + UnsizingParams(unsizing_params) + } + + fn anonymize_bound_vars<T: rustc_type_ir::TypeFoldable<Self>>( + self, + value: rustc_type_ir::Binder<Self, T>, + ) -> rustc_type_ir::Binder<Self, T> { + struct Anonymize<'a, 'db> { + interner: DbInterner<'db>, + map: &'a mut FxIndexMap<BoundVar, BoundVarKind>, + } + impl<'db> BoundVarReplacerDelegate<'db> for Anonymize<'_, 'db> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + let entry = self.map.entry(br.var); + let index = entry.index(); + let var = BoundVar::from_usize(index); + let kind = (*entry.or_insert_with(|| BoundVarKind::Region(BoundRegionKind::Anon))) + .expect_region(); + let br = BoundRegion { var, kind }; + Region::new_bound(self.interner, DebruijnIndex::ZERO, br) + } + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + let entry = self.map.entry(bt.var); + let index = entry.index(); + let var = BoundVar::from_usize(index); + let kind = + (*entry.or_insert_with(|| BoundVarKind::Ty(BoundTyKind::Anon))).expect_ty(); + Ty::new_bound(self.interner, DebruijnIndex::ZERO, BoundTy { var, kind }) + } + fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + let entry = self.map.entry(bv); + let index = entry.index(); + let var = BoundVar::from_usize(index); + let () = (*entry.or_insert_with(|| BoundVarKind::Const)).expect_const(); + Const::new_bound(self.interner, DebruijnIndex::ZERO, var) + } + } + + let mut map = Default::default(); + let delegate = Anonymize { interner: self, map: &mut map }; + let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate); + let bound_vars = CollectAndApply::collect_and_apply(map.into_values(), |xs| { + BoundVarKinds::new_from_iter(self, xs.iter().cloned()) + }); + Binder::bind_with_vars(inner, bound_vars) + } + + fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds { + // FIXME(next-solver) + SolverDefIds::new_from_iter(self, []) + } + + fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn explicit_implied_const_bounds( + self, + def_id: Self::DefId, + ) -> EarlyBinder< + Self, + impl IntoIterator<Item = rustc_type_ir::Binder<Self, rustc_type_ir::TraitRef<Self>>>, + > { + // FIXME(next-solver) + EarlyBinder::bind([]) + } + + fn fn_is_const(self, def_id: Self::DefId) -> bool { + let id = match def_id { + SolverDefId::FunctionId(id) => id, + _ => unreachable!(), + }; + self.db().function_signature(id).flags.contains(FnFlags::CONST) + } + + fn impl_is_const(self, def_id: Self::DefId) -> bool { + false + } + + fn opt_alias_variances( + self, + kind: impl Into<rustc_type_ir::AliasTermKind>, + def_id: Self::DefId, + ) -> Option<Self::VariancesOf> { + None + } + + fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) -> EarlyBinder<Self, Self::Ty> { + match def_id { + SolverDefId::InternedOpaqueTyId(opaque) => { + let impl_trait_id = self.db().lookup_intern_impl_trait_id(opaque); + match impl_trait_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let infer = self.db().infer(func.into()); + EarlyBinder::bind(infer.type_of_rpit[idx].to_nextsolver(self)) + } + crate::ImplTraitId::TypeAliasImplTrait(..) + | crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { + // FIXME(next-solver) + EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed)) + } + } + } + _ => panic!("Unexpected SolverDefId in type_of_opaque_hir_typeck"), + } + } + + fn coroutine_hidden_types( + self, + def_id: Self::DefId, + ) -> EarlyBinder<Self, rustc_type_ir::Binder<Self, rustc_type_ir::CoroutineWitnessTypes<Self>>> + { + // FIXME(next-solver) + unimplemented!() + } + + fn is_default_trait(self, def_id: Self::DefId) -> bool { + self.as_lang_item(def_id).map_or(false, |l| matches!(l, TraitSolverLangItem::Sized)) + } + + fn trait_is_coinductive(self, trait_def_id: Self::DefId) -> bool { + let id = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + self.db().trait_signature(id).flags.contains(TraitFlags::COINDUCTIVE) + } + + fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { + let id = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + self.db().trait_signature(id).flags.contains(TraitFlags::UNSAFE) + } + + fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool { + false + } + + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { + false + } + + fn next_trait_solver_globally(self) -> bool { + true + } + + fn opaque_types_and_coroutines_defined_by( + self, + defining_anchor: Self::LocalDefId, + ) -> Self::LocalDefIds { + // FIXME(next-solver) + unimplemented!() + } +} + +impl<'db> DbInterner<'db> { + pub fn shift_bound_var_indices<T>(self, bound_vars: usize, value: T) -> T + where + T: rustc_type_ir::TypeFoldable<Self>, + { + let shift_bv = |bv: BoundVar| BoundVar::from_usize(bv.as_usize() + bound_vars); + self.replace_escaping_bound_vars_uncached( + value, + FnMutDelegate { + regions: &mut |r: BoundRegion| { + Region::new_bound( + self, + DebruijnIndex::ZERO, + BoundRegion { var: shift_bv(r.var), kind: r.kind }, + ) + }, + types: &mut |t: BoundTy| { + Ty::new_bound( + self, + DebruijnIndex::ZERO, + BoundTy { var: shift_bv(t.var), kind: t.kind }, + ) + }, + consts: &mut |c| Const::new_bound(self, DebruijnIndex::ZERO, shift_bv(c)), + }, + ) + } + + pub fn replace_escaping_bound_vars_uncached<T: rustc_type_ir::TypeFoldable<DbInterner<'db>>>( + self, + value: T, + delegate: impl BoundVarReplacerDelegate<'db>, + ) -> T { + if !value.has_escaping_bound_vars() { + value + } else { + let mut replacer = BoundVarReplacer::new(self, delegate); + value.fold_with(&mut replacer) + } + } + + pub fn replace_bound_vars_uncached<T: rustc_type_ir::TypeFoldable<DbInterner<'db>>>( + self, + value: Binder<'db, T>, + delegate: impl BoundVarReplacerDelegate<'db>, + ) -> T { + self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) + } +} + +macro_rules! TrivialTypeTraversalImpls { + ($($ty:ty,)+) => { + $( + impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for $ty { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + _: &mut F, + ) -> ::std::result::Result<Self, F::Error> { + Ok(self) + } + + #[inline] + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>( + self, + _: &mut F, + ) -> Self { + self + } + } + + impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for $ty { + #[inline] + fn visit_with<F: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + _: &mut F) + -> F::Result + { + <F::Result as rustc_ast_ir::visit::VisitorResult>::output() + } + } + )+ + }; +} + +TrivialTypeTraversalImpls! { + SolverDefId, + Pattern<'db>, + Safety, + FnAbi, + Span, + ParamConst, + ParamTy, + BoundRegion, + BoundVar, + Placeholder<BoundRegion>, + Placeholder<BoundTy>, + Placeholder<BoundVar>, +} + +pub(crate) use tls_cache::with_new_cache; +mod tls_cache { + use crate::db::HirDatabase; + + use super::DbInterner; + use rustc_type_ir::search_graph::GlobalCache; + use std::cell::RefCell; + + scoped_tls::scoped_thread_local!(static GLOBAL_CACHE: RefCell<rustc_type_ir::search_graph::GlobalCache<DbInterner<'static>>>); + + pub(crate) fn with_new_cache<T>(f: impl FnOnce() -> T) -> T { + GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), f) + } + + pub(super) fn with_cache<'db, T>( + _db: &'db dyn HirDatabase, + f: impl FnOnce(&mut GlobalCache<DbInterner<'db>>) -> T, + ) -> T { + // SAFETY: No idea + let call = move |slot: &RefCell<_>| { + f(unsafe { + std::mem::transmute::< + &mut GlobalCache<DbInterner<'static>>, + &mut GlobalCache<DbInterner<'db>>, + >(&mut *slot.borrow_mut()) + }) + }; + if GLOBAL_CACHE.is_set() { + GLOBAL_CACHE.with(call) + } else { + GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), || GLOBAL_CACHE.with(call)) + } + } +}
diff --git a/crates/hir-ty/src/next_solver/ir_print.rs b/crates/hir-ty/src/next_solver/ir_print.rs new file mode 100644 index 0000000..4d32b27 --- /dev/null +++ b/crates/hir-ty/src/next_solver/ir_print.rs
@@ -0,0 +1,267 @@ +//! Things related to IR printing in the next-trait-solver. + +use std::any::type_name_of_val; + +use rustc_type_ir::inherent::SliceLike; +use rustc_type_ir::{self as ty, ir_print::IrPrint}; + +use crate::db::HirDatabase; + +use super::SolverDefId; +use super::interner::DbInterner; + +impl<'db> IrPrint<ty::AliasTy<Self>> for DbInterner<'db> { + fn print(t: &ty::AliasTy<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::AliasTy<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + salsa::with_attached_database(|db| match t.def_id { + SolverDefId::TypeAliasId(id) => fmt.write_str(&format!( + "AliasTy({:?}[{:?}])", + db.as_view::<dyn HirDatabase>().type_alias_signature(id).name.as_str(), + t.args + )), + SolverDefId::InternedOpaqueTyId(id) => { + fmt.write_str(&format!("AliasTy({:?}[{:?}])", id, t.args)) + } + _ => panic!("Expected TypeAlias or OpaqueTy."), + }) + .unwrap_or_else(|| fmt.write_str(&format!("AliasTy({:?}[{:?}])", t.def_id, t.args))) + } +} + +impl<'db> IrPrint<ty::AliasTerm<Self>> for DbInterner<'db> { + fn print(t: &ty::AliasTerm<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::AliasTerm<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + salsa::with_attached_database(|db| match t.def_id { + SolverDefId::TypeAliasId(id) => fmt.write_str(&format!( + "AliasTerm({:?}[{:?}])", + db.as_view::<dyn HirDatabase>().type_alias_signature(id).name.as_str(), + t.args + )), + SolverDefId::InternedOpaqueTyId(id) => { + fmt.write_str(&format!("AliasTerm({:?}[{:?}])", id, t.args)) + } + _ => panic!("Expected TypeAlias or OpaqueTy."), + }) + .unwrap_or_else(|| fmt.write_str(&format!("AliasTerm({:?}[{:?}])", t.def_id, t.args))) + } +} +impl<'db> IrPrint<ty::TraitRef<Self>> for DbInterner<'db> { + fn print(t: &ty::TraitRef<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::TraitRef<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let trait_ = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Expected trait."), + }; + let self_ty = &t.args.as_slice()[0]; + let trait_args = &t.args.as_slice()[1..]; + if trait_args.is_empty() { + fmt.write_str(&format!( + "{:?}: {}", + self_ty, + db.as_view::<dyn HirDatabase>().trait_signature(trait_).name.as_str() + )) + } else { + fmt.write_str(&format!( + "{:?}: {}<{:?}>", + self_ty, + db.as_view::<dyn HirDatabase>().trait_signature(trait_).name.as_str(), + trait_args + )) + } + }) + .unwrap_or_else(|| fmt.write_str(&format!("TraitRef({:?}[{:?}])", t.def_id, t.args))) + } +} +impl<'db> IrPrint<ty::TraitPredicate<Self>> for DbInterner<'db> { + fn print(t: &ty::TraitPredicate<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::TraitPredicate<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint<rustc_type_ir::HostEffectPredicate<Self>> for DbInterner<'db> { + fn print( + t: &rustc_type_ir::HostEffectPredicate<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &rustc_type_ir::HostEffectPredicate<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint<ty::ExistentialTraitRef<Self>> for DbInterner<'db> { + fn print( + t: &ty::ExistentialTraitRef<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::ExistentialTraitRef<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let trait_ = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Expected trait."), + }; + fmt.write_str(&format!( + "ExistentialTraitRef({:?}[{:?}])", + db.as_view::<dyn HirDatabase>().trait_signature(trait_).name.as_str(), + t.args + )) + }) + .unwrap_or_else(|| { + fmt.write_str(&format!("ExistentialTraitRef({:?}[{:?}])", t.def_id, t.args)) + }) + } +} +impl<'db> IrPrint<ty::ExistentialProjection<Self>> for DbInterner<'db> { + fn print( + t: &ty::ExistentialProjection<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::ExistentialProjection<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let id = match t.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => panic!("Expected trait."), + }; + fmt.write_str(&format!( + "ExistentialProjection(({:?}[{:?}]) -> {:?})", + db.as_view::<dyn HirDatabase>().type_alias_signature(id).name.as_str(), + t.args, + t.term + )) + }) + .unwrap_or_else(|| { + fmt.write_str(&format!( + "ExistentialProjection(({:?}[{:?}]) -> {:?})", + t.def_id, t.args, t.term + )) + }) + } +} +impl<'db> IrPrint<ty::ProjectionPredicate<Self>> for DbInterner<'db> { + fn print( + t: &ty::ProjectionPredicate<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::ProjectionPredicate<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let id = match t.projection_term.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => panic!("Expected trait."), + }; + fmt.write_str(&format!( + "ProjectionPredicate(({:?}[{:?}]) -> {:?})", + db.as_view::<dyn HirDatabase>().type_alias_signature(id).name.as_str(), + t.projection_term.args, + t.term + )) + }) + .unwrap_or_else(|| { + fmt.write_str(&format!( + "ProjectionPredicate(({:?}[{:?}]) -> {:?})", + t.projection_term.def_id, t.projection_term.args, t.term + )) + }) + } +} +impl<'db> IrPrint<ty::NormalizesTo<Self>> for DbInterner<'db> { + fn print(t: &ty::NormalizesTo<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::NormalizesTo<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint<ty::SubtypePredicate<Self>> for DbInterner<'db> { + fn print( + t: &ty::SubtypePredicate<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::SubtypePredicate<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint<ty::CoercePredicate<Self>> for DbInterner<'db> { + fn print(t: &ty::CoercePredicate<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::CoercePredicate<Self>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint<ty::FnSig<Self>> for DbInterner<'db> { + fn print(t: &ty::FnSig<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::FnSig<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} + +impl<'db> IrPrint<rustc_type_ir::PatternKind<DbInterner<'db>>> for DbInterner<'db> { + fn print( + t: &rustc_type_ir::PatternKind<DbInterner<'db>>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &rustc_type_ir::PatternKind<DbInterner<'db>>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +}
diff --git a/crates/hir-ty/src/next_solver/mapping.rs b/crates/hir-ty/src/next_solver/mapping.rs new file mode 100644 index 0000000..3a3206b --- /dev/null +++ b/crates/hir-ty/src/next_solver/mapping.rs
@@ -0,0 +1,1368 @@ +//! Things useful for mapping to/from Chalk and next-trait-solver types. + +use base_db::Crate; +use chalk_ir::{ + CanonicalVarKind, CanonicalVarKinds, ForeignDefId, InferenceVar, Substitution, TyVariableKind, + WellFormed, fold::Shift, interner::HasInterner, +}; +use hir_def::{ + CallableDefId, ConstParamId, FunctionId, GeneralConstId, LifetimeParamId, TypeAliasId, + TypeOrConstParamId, TypeParamId, signatures::TraitFlags, +}; +use intern::sym; +use rustc_type_ir::{ + AliasTerm, BoundVar, DebruijnIndex, ExistentialProjection, ExistentialTraitRef, Interner as _, + OutlivesPredicate, ProjectionPredicate, TypeFoldable, TypeSuperFoldable, TypeVisitable, + TypeVisitableExt, UniverseIndex, elaborate, + inherent::{BoundVarLike, Clause as _, IntoKind, PlaceholderLike, SliceLike, Ty as _}, + shift_vars, + solve::Goal, +}; +use salsa::plumbing::AsId; + +use crate::{ + ConcreteConst, ConstScalar, ImplTraitId, Interner, + db::{ + HirDatabase, InternedClosureId, InternedCoroutineId, InternedOpaqueTyId, + InternedTypeOrConstParamId, + }, + from_assoc_type_id, from_chalk_trait_id, + mapping::ToChalk, + next_solver::{ + Binder, ClauseKind, ConstBytes, TraitPredicate, UnevaluatedConst, + interner::{AdtDef, BoundVarKind, BoundVarKinds, DbInterner}, + }, + to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, +}; + +use super::{ + BoundExistentialPredicate, BoundExistentialPredicates, BoundRegion, BoundRegionKind, BoundTy, + BoundTyKind, Canonical, CanonicalVars, Clause, Clauses, Const, Ctor, EarlyParamRegion, + ErrorGuaranteed, ExistentialPredicate, GenericArg, GenericArgs, ParamConst, ParamEnv, ParamTy, + Placeholder, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Predicate, PredicateKind, + Region, SolverDefId, SubtypePredicate, Term, TraitRef, Ty, Tys, ValueConst, VariancesOf, +}; + +pub fn to_placeholder_idx<T: Clone + std::fmt::Debug>( + db: &dyn HirDatabase, + id: TypeOrConstParamId, + map: impl Fn(BoundVar) -> T, +) -> Placeholder<T> { + let interned_id = InternedTypeOrConstParamId::new(db, id); + Placeholder { + universe: UniverseIndex::ZERO, + bound: map(BoundVar::from_usize(interned_id.as_id().index() as usize)), + } +} + +pub fn convert_binder_to_early_binder<'db, T: rustc_type_ir::TypeFoldable<DbInterner<'db>>>( + interner: DbInterner<'db>, + binder: rustc_type_ir::Binder<DbInterner<'db>, T>, +) -> rustc_type_ir::EarlyBinder<DbInterner<'db>, T> { + let mut folder = BinderToEarlyBinder { interner, debruijn: rustc_type_ir::DebruijnIndex::ZERO }; + rustc_type_ir::EarlyBinder::bind(binder.skip_binder().fold_with(&mut folder)) +} + +struct BinderToEarlyBinder<'db> { + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, +} + +impl<'db> rustc_type_ir::TypeFolder<DbInterner<'db>> for BinderToEarlyBinder<'db> { + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_binder<T>( + &mut self, + t: rustc_type_ir::Binder<DbInterner<'db>, T>, + ) -> rustc_type_ir::Binder<DbInterner<'db>, T> + where + T: TypeFoldable<DbInterner<'db>>, + { + self.debruijn.shift_in(1); + let result = t.super_fold_with(self); + self.debruijn.shift_out(1); + result + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + match t.kind() { + rustc_type_ir::TyKind::Bound(debruijn, bound_ty) if self.debruijn == debruijn => { + let var: rustc_type_ir::BoundVar = bound_ty.var(); + Ty::new(self.cx(), rustc_type_ir::TyKind::Param(ParamTy { index: var.as_u32() })) + } + _ => t.super_fold_with(self), + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + match r.kind() { + rustc_type_ir::ReBound(debruijn, bound_region) if self.debruijn == debruijn => { + let var: rustc_type_ir::BoundVar = bound_region.var(); + Region::new( + self.cx(), + rustc_type_ir::RegionKind::ReEarlyParam(EarlyParamRegion { + index: var.as_u32(), + }), + ) + } + _ => r, + } + } + + fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { + match c.kind() { + rustc_type_ir::ConstKind::Bound(debruijn, var) if self.debruijn == debruijn => { + Const::new( + self.cx(), + rustc_type_ir::ConstKind::Param(ParamConst { index: var.as_u32() }), + ) + } + _ => c.super_fold_with(self), + } + } +} + +pub trait ChalkToNextSolver<'db, Out> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out; +} + +impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Ty<'db> { + Ty::new( + interner, + match self.kind(Interner) { + chalk_ir::TyKind::Adt(adt_id, substitution) => { + let def = AdtDef::new(adt_id.0, interner); + let args = substitution.to_nextsolver(interner); + rustc_type_ir::TyKind::Adt(def, args) + } + chalk_ir::TyKind::AssociatedType(assoc_type_id, substitution) => { + let def_id = SolverDefId::TypeAliasId(from_assoc_type_id(*assoc_type_id)); + let args: GenericArgs<'db> = substitution.to_nextsolver(interner); + let alias_ty = rustc_type_ir::AliasTy::new(interner, def_id, args.iter()); + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Projection, alias_ty) + } + chalk_ir::TyKind::Scalar(scalar) => match scalar { + chalk_ir::Scalar::Bool => rustc_type_ir::TyKind::Bool, + chalk_ir::Scalar::Char => rustc_type_ir::TyKind::Char, + chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I8) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I16) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I32) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I64) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I128) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U16) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U64) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U128) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F16) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F32) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F64) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F128) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) + } + }, + chalk_ir::TyKind::Tuple(_, substitution) => { + let args = substitution.to_nextsolver(interner); + rustc_type_ir::TyKind::Tuple(args) + } + chalk_ir::TyKind::Array(ty, len) => rustc_type_ir::TyKind::Array( + ty.to_nextsolver(interner), + len.to_nextsolver(interner), + ), + chalk_ir::TyKind::Slice(ty) => { + rustc_type_ir::TyKind::Slice(ty.to_nextsolver(interner)) + } + chalk_ir::TyKind::Raw(mutability, ty) => rustc_type_ir::RawPtr( + ty.to_nextsolver(interner), + mutability.to_nextsolver(interner), + ), + chalk_ir::TyKind::Ref(mutability, lifetime, ty) => rustc_type_ir::TyKind::Ref( + lifetime.to_nextsolver(interner), + ty.to_nextsolver(interner), + mutability.to_nextsolver(interner), + ), + chalk_ir::TyKind::OpaqueType(def_id, substitution) => { + let id: InternedOpaqueTyId = (*def_id).into(); + let args: GenericArgs<'db> = substitution.to_nextsolver(interner); + let alias_ty = rustc_type_ir::AliasTy::new(interner, id.into(), args); + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Opaque, alias_ty) + } + chalk_ir::TyKind::FnDef(fn_def_id, substitution) => { + let def_id = CallableDefId::from_chalk(interner.db(), *fn_def_id); + let id: SolverDefId = match def_id { + CallableDefId::FunctionId(id) => id.into(), + CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)), + CallableDefId::EnumVariantId(id) => SolverDefId::Ctor(Ctor::Enum(id)), + }; + rustc_type_ir::TyKind::FnDef(id, substitution.to_nextsolver(interner)) + } + chalk_ir::TyKind::Str => rustc_type_ir::TyKind::Str, + chalk_ir::TyKind::Never => rustc_type_ir::TyKind::Never, + chalk_ir::TyKind::Closure(closure_id, substitution) => { + let id: InternedClosureId = (*closure_id).into(); + rustc_type_ir::TyKind::Closure(id.into(), substitution.to_nextsolver(interner)) + } + chalk_ir::TyKind::Coroutine(coroutine_id, substitution) => { + let id: InternedCoroutineId = (*coroutine_id).into(); + rustc_type_ir::TyKind::Coroutine( + id.into(), + substitution.to_nextsolver(interner), + ) + } + chalk_ir::TyKind::CoroutineWitness(coroutine_id, substitution) => { + let id: InternedCoroutineId = (*coroutine_id).into(); + rustc_type_ir::TyKind::CoroutineWitness( + id.into(), + substitution.to_nextsolver(interner), + ) + } + chalk_ir::TyKind::Foreign(foreign_def_id) => rustc_type_ir::TyKind::Foreign( + SolverDefId::ForeignId(crate::from_foreign_def_id(*foreign_def_id)), + ), + chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed), + chalk_ir::TyKind::Placeholder(placeholder_index) => { + rustc_type_ir::TyKind::Placeholder(PlaceholderTy::new_anon( + placeholder_index.ui.to_nextsolver(interner), + rustc_type_ir::BoundVar::from_usize(placeholder_index.idx), + )) + } + chalk_ir::TyKind::Dyn(dyn_ty) => { + // exists<type> { for<...> ^1.0: ... } + let bounds = BoundExistentialPredicates::new_from_iter( + interner, + dyn_ty.bounds.skip_binders().iter(Interner).filter_map(|pred| { + // for<...> ^1.0: ... + let (val, binders) = pred.clone().into_value_and_skipped_binders(); + let bound_vars = binders.to_nextsolver(interner); + let clause = match val { + chalk_ir::WhereClause::Implemented(trait_ref) => { + let trait_id = from_chalk_trait_id(trait_ref.trait_id); + if interner + .db() + .trait_signature(trait_id) + .flags + .contains(TraitFlags::AUTO) + { + ExistentialPredicate::AutoTrait(SolverDefId::TraitId( + trait_id, + )) + } else { + let def_id = SolverDefId::TraitId(trait_id); + let args = GenericArgs::new_from_iter( + interner, + trait_ref + .substitution + .iter(Interner) + .skip(1) + .map(|a| a.clone().shifted_out(Interner).unwrap()) + .map(|a| a.to_nextsolver(interner)), + ); + let trait_ref = ExistentialTraitRef::new_from_args( + interner, def_id, args, + ); + ExistentialPredicate::Trait(trait_ref) + } + } + chalk_ir::WhereClause::AliasEq(alias_eq) => { + let (def_id, args) = match &alias_eq.alias { + chalk_ir::AliasTy::Projection(projection) => { + let id = + from_assoc_type_id(projection.associated_ty_id); + let def_id = SolverDefId::TypeAliasId(id); + let generics = interner.generics_of(def_id); + let parent_len = generics.parent_count; + let substs = projection.substitution.iter(Interner).skip(1); + + let args = GenericArgs::new_from_iter( + interner, + substs + .map(|a| { + a.clone().shifted_out(Interner).unwrap() + }) + .map(|a| a.to_nextsolver(interner)), + ); + (def_id, args) + } + chalk_ir::AliasTy::Opaque(opaque_ty) => { + panic!("Invalid ExistentialPredicate (opaques can't be named)."); + } + }; + let term = alias_eq + .ty + .clone() + .shifted_out(Interner) + .unwrap() + .to_nextsolver(interner) + .into(); + let projection = ExistentialProjection::new_from_args( + interner, def_id, args, term, + ); + ExistentialPredicate::Projection(projection) + } + chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { + return None; + } + chalk_ir::WhereClause::TypeOutlives(type_outlives) => return None, + }; + + Some(Binder::bind_with_vars(clause, bound_vars)) + }), + ); + let region = dyn_ty.lifetime.to_nextsolver(interner); + let kind = rustc_type_ir::DynKind::Dyn; + rustc_type_ir::TyKind::Dynamic(bounds, region, kind) + } + chalk_ir::TyKind::Alias(alias_ty) => match alias_ty { + chalk_ir::AliasTy::Projection(projection_ty) => { + let def_id = SolverDefId::TypeAliasId(from_assoc_type_id( + projection_ty.associated_ty_id, + )); + let alias_ty = rustc_type_ir::AliasTy::new_from_args( + interner, + def_id, + projection_ty.substitution.to_nextsolver(interner), + ); + rustc_type_ir::TyKind::Alias( + rustc_type_ir::AliasTyKind::Projection, + alias_ty, + ) + } + chalk_ir::AliasTy::Opaque(opaque_ty) => { + let id: InternedOpaqueTyId = opaque_ty.opaque_ty_id.into(); + let def_id = SolverDefId::InternedOpaqueTyId(id); + let alias_ty = rustc_type_ir::AliasTy::new_from_args( + interner, + def_id, + opaque_ty.substitution.to_nextsolver(interner), + ); + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Opaque, alias_ty) + } + }, + chalk_ir::TyKind::Function(fn_pointer) => { + let sig_tys = fn_pointer.clone().into_binders(Interner).to_nextsolver(interner); + let header = rustc_type_ir::FnHeader { + abi: fn_pointer.sig.abi, + c_variadic: fn_pointer.sig.variadic, + safety: match fn_pointer.sig.safety { + chalk_ir::Safety::Safe => super::abi::Safety::Safe, + chalk_ir::Safety::Unsafe => super::abi::Safety::Unsafe, + }, + }; + + rustc_type_ir::TyKind::FnPtr(sig_tys, header) + } + chalk_ir::TyKind::BoundVar(bound_var) => rustc_type_ir::TyKind::Bound( + bound_var.debruijn.to_nextsolver(interner), + BoundTy { + var: rustc_type_ir::BoundVar::from_usize(bound_var.index), + kind: BoundTyKind::Anon, + }, + ), + chalk_ir::TyKind::InferenceVar(inference_var, ty_variable_kind) => { + rustc_type_ir::TyKind::Infer( + (*inference_var, *ty_variable_kind).to_nextsolver(interner), + ) + } + }, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Region<'db> { + Region::new( + interner, + match self.data(Interner) { + chalk_ir::LifetimeData::BoundVar(bound_var) => rustc_type_ir::RegionKind::ReBound( + bound_var.debruijn.to_nextsolver(interner), + BoundRegion { + var: rustc_type_ir::BoundVar::from_u32(bound_var.index as u32), + kind: BoundRegionKind::Anon, + }, + ), + chalk_ir::LifetimeData::InferenceVar(inference_var) => { + rustc_type_ir::RegionKind::ReVar(rustc_type_ir::RegionVid::from_u32( + inference_var.index(), + )) + } + chalk_ir::LifetimeData::Placeholder(placeholder_index) => { + rustc_type_ir::RegionKind::RePlaceholder(PlaceholderRegion::new_anon( + rustc_type_ir::UniverseIndex::from_u32(placeholder_index.ui.counter as u32), + rustc_type_ir::BoundVar::from_u32(placeholder_index.idx as u32), + )) + } + chalk_ir::LifetimeData::Static => rustc_type_ir::RegionKind::ReStatic, + chalk_ir::LifetimeData::Erased => rustc_type_ir::RegionKind::ReErased, + chalk_ir::LifetimeData::Phantom(_, _) => { + unreachable!() + } + chalk_ir::LifetimeData::Error => { + rustc_type_ir::RegionKind::ReError(ErrorGuaranteed) + } + }, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Const<'db> { + let data = self.data(Interner); + Const::new( + interner, + match &data.value { + chalk_ir::ConstValue::BoundVar(bound_var) => rustc_type_ir::ConstKind::Bound( + bound_var.debruijn.to_nextsolver(interner), + rustc_type_ir::BoundVar::from_usize(bound_var.index), + ), + chalk_ir::ConstValue::InferenceVar(inference_var) => { + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var( + rustc_type_ir::ConstVid::from_u32(inference_var.index()), + )) + } + chalk_ir::ConstValue::Placeholder(placeholder_index) => { + rustc_type_ir::ConstKind::Placeholder(PlaceholderConst::new( + placeholder_index.ui.to_nextsolver(interner), + rustc_type_ir::BoundVar::from_usize(placeholder_index.idx), + )) + } + chalk_ir::ConstValue::Concrete(concrete_const) => match &concrete_const.interned { + ConstScalar::Bytes(bytes, memory) => { + rustc_type_ir::ConstKind::Value(ValueConst::new( + data.ty.to_nextsolver(interner), + ConstBytes(bytes.clone(), memory.clone()), + )) + } + ConstScalar::UnevaluatedConst(c, subst) => { + let def = match *c { + GeneralConstId::ConstId(id) => SolverDefId::ConstId(id), + GeneralConstId::StaticId(id) => SolverDefId::StaticId(id), + }; + let args = subst.to_nextsolver(interner); + rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new(def, args)) + } + ConstScalar::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + }, + }, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys<DbInterner<'db>>> + for chalk_ir::FnSubst<Interner> +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::FnSigTys<DbInterner<'db>> { + rustc_type_ir::FnSigTys { + inputs_and_output: Tys::new_from_iter( + interner, + self.0.iter(Interner).map(|g| g.assert_ty_ref(Interner).to_nextsolver(interner)), + ), + } + } +} + +impl< + 'db, + U: TypeVisitable<DbInterner<'db>>, + T: Clone + ChalkToNextSolver<'db, U> + HasInterner<Interner = Interner>, +> ChalkToNextSolver<'db, rustc_type_ir::Binder<DbInterner<'db>, U>> for chalk_ir::Binders<T> +{ + fn to_nextsolver( + &self, + interner: DbInterner<'db>, + ) -> rustc_type_ir::Binder<DbInterner<'db>, U> { + let (val, binders) = self.clone().into_value_and_skipped_binders(); + rustc_type_ir::Binder::bind_with_vars( + val.to_nextsolver(interner), + binders.to_nextsolver(interner), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKinds { + BoundVarKinds::new_from_iter( + interner, + self.iter(Interner).map(|v| v.to_nextsolver(interner)), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKind { + match self { + chalk_ir::VariableKind::Ty(_ty_variable_kind) => BoundVarKind::Ty(BoundTyKind::Anon), + chalk_ir::VariableKind::Lifetime => BoundVarKind::Region(BoundRegionKind::Anon), + chalk_ir::VariableKind::Const(_ty) => BoundVarKind::Const, + } + } +} + +impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArg<'db> { + match self.data(Interner) { + chalk_ir::GenericArgData::Ty(ty) => ty.to_nextsolver(interner).into(), + chalk_ir::GenericArgData::Lifetime(lifetime) => lifetime.to_nextsolver(interner).into(), + chalk_ir::GenericArgData::Const(const_) => const_.to_nextsolver(interner).into(), + } + } +} +impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArgs<'db> { + GenericArgs::new_from_iter( + interner, + self.iter(Interner).map(|arg| -> GenericArg<'db> { arg.to_nextsolver(interner) }), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Tys<'db> { + Tys::new_from_iter( + interner, + self.iter(Interner).map(|arg| -> Ty<'db> { + match arg.data(Interner) { + chalk_ir::GenericArgData::Ty(ty) => ty.to_nextsolver(interner), + chalk_ir::GenericArgData::Lifetime(_) => unreachable!(), + chalk_ir::GenericArgData::Const(_) => unreachable!(), + } + }), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { + rustc_type_ir::DebruijnIndex::from_u32(self.depth()) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::UniverseIndex> for chalk_ir::UniverseIndex { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex { + rustc_type_ir::UniverseIndex::from_u32(self.counter as u32) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> + for (chalk_ir::InferenceVar, chalk_ir::TyVariableKind) +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::InferTy { + match self.1 { + chalk_ir::TyVariableKind::General => { + rustc_type_ir::InferTy::TyVar(rustc_type_ir::TyVid::from_u32(self.0.index())) + } + chalk_ir::TyVariableKind::Integer => { + rustc_type_ir::InferTy::IntVar(rustc_type_ir::IntVid::from_u32(self.0.index())) + } + chalk_ir::TyVariableKind::Float => { + rustc_type_ir::InferTy::FloatVar(rustc_type_ir::FloatVid::from_u32(self.0.index())) + } + } + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutability { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_ast_ir::Mutability { + match self { + chalk_ir::Mutability::Mut => rustc_ast_ir::Mutability::Mut, + chalk_ir::Mutability::Not => rustc_ast_ir::Mutability::Not, + } + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::Variance { + match self { + crate::Variance::Covariant => rustc_type_ir::Variance::Covariant, + crate::Variance::Invariant => rustc_type_ir::Variance::Invariant, + crate::Variance::Contravariant => rustc_type_ir::Variance::Contravariant, + crate::Variance::Bivariant => rustc_type_ir::Variance::Bivariant, + } + } +} + +impl<'db> ChalkToNextSolver<'db, Canonical<'db, Goal<DbInterner<'db>, Predicate<'db>>>> + for chalk_ir::Canonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>> +{ + fn to_nextsolver( + &self, + interner: DbInterner<'db>, + ) -> Canonical<'db, Goal<DbInterner<'db>, Predicate<'db>>> { + let param_env = self.value.environment.to_nextsolver(interner); + let variables = CanonicalVars::new_from_iter( + interner, + self.binders.iter(Interner).map(|k| match &k.kind { + chalk_ir::VariableKind::Ty(ty_variable_kind) => match ty_variable_kind { + TyVariableKind::General => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ROOT), + ), + TyVariableKind::Integer => { + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) + } + TyVariableKind::Float => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::Float, + ), + }, + chalk_ir::VariableKind::Lifetime => { + rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ROOT) + } + chalk_ir::VariableKind::Const(ty) => { + rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ROOT) + } + }), + ); + Canonical { + max_universe: UniverseIndex::ROOT, + value: Goal::new(interner, param_env, self.value.goal.to_nextsolver(interner)), + variables, + } + } +} + +impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Predicate<'db> { + match self.data(Interner) { + chalk_ir::GoalData::Quantified(quantifier_kind, binders) => { + if !binders.binders.is_empty(Interner) { + panic!("Should not be constructed."); + } + let (val, _) = binders.clone().into_value_and_skipped_binders(); + val.shifted_out(Interner).unwrap().to_nextsolver(interner) + } + chalk_ir::GoalData::Implies(program_clauses, goal) => { + panic!("Should not be constructed.") + } + chalk_ir::GoalData::All(goals) => panic!("Should not be constructed."), + chalk_ir::GoalData::Not(goal) => panic!("Should not be constructed."), + chalk_ir::GoalData::EqGoal(eq_goal) => panic!("Should not be constructed."), + chalk_ir::GoalData::SubtypeGoal(subtype_goal) => { + let subtype_predicate = SubtypePredicate { + a: subtype_goal.a.to_nextsolver(interner), + b: subtype_goal.b.to_nextsolver(interner), + a_is_expected: true, + }; + let pred_kind = PredicateKind::Subtype(subtype_predicate); + let pred_kind = Binder::bind_with_vars( + shift_vars(interner, pred_kind, 1), + BoundVarKinds::new_from_iter(interner, []), + ); + Predicate::new(interner, pred_kind) + } + chalk_ir::GoalData::DomainGoal(domain_goal) => { + let pred_kind = domain_goal.to_nextsolver(interner); + let pred_kind = Binder::bind_with_vars( + shift_vars(interner, pred_kind, 1), + BoundVarKinds::new_from_iter(interner, []), + ); + Predicate::new(interner, pred_kind) + } + chalk_ir::GoalData::CannotProve => panic!("Should not be constructed."), + } + } +} + +impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> ParamEnv<'db> { + let clauses = Clauses::new_from_iter( + interner, + self.clauses.iter(Interner).map(|c| c.to_nextsolver(interner)), + ); + let clauses = + Clauses::new_from_iter(interner, elaborate::elaborate(interner, clauses.iter())); + ParamEnv { clauses } + } +} + +impl<'db> ChalkToNextSolver<'db, Clause<'db>> for chalk_ir::ProgramClause<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Clause<'db> { + Clause(Predicate::new(interner, self.data(Interner).0.to_nextsolver(interner))) + } +} + +impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> + for chalk_ir::ProgramClauseImplication<Interner> +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { + assert!(self.conditions.is_empty(Interner)); + assert!(self.constraints.is_empty(Interner)); + self.consequence.to_nextsolver(interner) + } +} + +impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { + match self { + chalk_ir::DomainGoal::Holds(where_clause) => match where_clause { + chalk_ir::WhereClause::Implemented(trait_ref) => { + let predicate = TraitPredicate { + trait_ref: trait_ref.to_nextsolver(interner), + polarity: rustc_type_ir::PredicatePolarity::Positive, + }; + PredicateKind::Clause(ClauseKind::Trait(predicate)) + } + chalk_ir::WhereClause::AliasEq(alias_eq) => match &alias_eq.alias { + chalk_ir::AliasTy::Projection(p) => { + let def_id = + SolverDefId::TypeAliasId(from_assoc_type_id(p.associated_ty_id)); + let args = p.substitution.to_nextsolver(interner); + let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); + let term: Term<'db> = term.into(); + let predicate = ProjectionPredicate { + projection_term: AliasTerm::new_from_args(interner, def_id, args), + term, + }; + PredicateKind::Clause(ClauseKind::Projection(predicate)) + } + chalk_ir::AliasTy::Opaque(opaque) => { + let id: InternedOpaqueTyId = opaque.opaque_ty_id.into(); + let def_id = SolverDefId::InternedOpaqueTyId(id); + let args = opaque.substitution.to_nextsolver(interner); + let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); + let term: Term<'db> = term.into(); + let opaque_ty = Ty::new( + interner, + rustc_type_ir::TyKind::Alias( + rustc_type_ir::AliasTyKind::Opaque, + rustc_type_ir::AliasTy::new_from_args(interner, def_id, args), + ), + ) + .into(); + PredicateKind::AliasRelate( + opaque_ty, + term, + rustc_type_ir::AliasRelationDirection::Equate, + ) + } + }, + chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { + let predicate = OutlivesPredicate( + lifetime_outlives.a.to_nextsolver(interner), + lifetime_outlives.b.to_nextsolver(interner), + ); + PredicateKind::Clause(ClauseKind::RegionOutlives(predicate)) + } + chalk_ir::WhereClause::TypeOutlives(type_outlives) => { + let predicate = OutlivesPredicate( + type_outlives.ty.to_nextsolver(interner), + type_outlives.lifetime.to_nextsolver(interner), + ); + PredicateKind::Clause(ClauseKind::TypeOutlives(predicate)) + } + }, + chalk_ir::DomainGoal::Normalize(normalize) => { + let proj_ty = match &normalize.alias { + chalk_ir::AliasTy::Projection(proj) => proj, + _ => unimplemented!(), + }; + let args: GenericArgs<'db> = proj_ty.substitution.to_nextsolver(interner); + let alias = rustc_type_ir::AliasTerm::new( + interner, + from_assoc_type_id(proj_ty.associated_ty_id).into(), + args, + ); + let term = normalize.ty.to_nextsolver(interner).into(); + let normalizes_to = rustc_type_ir::NormalizesTo { alias, term }; + PredicateKind::NormalizesTo(normalizes_to) + } + chalk_ir::DomainGoal::WellFormed(well_formed) => { + let term = match well_formed { + WellFormed::Trait(_) => panic!("Should not be constructed."), + WellFormed::Ty(ty) => Term::Ty(ty.to_nextsolver(interner)), + }; + PredicateKind::Clause(rustc_type_ir::ClauseKind::WellFormed(term)) + } + chalk_ir::DomainGoal::FromEnv(from_env) => match from_env { + chalk_ir::FromEnv::Trait(trait_ref) => { + let predicate = TraitPredicate { + trait_ref: trait_ref.to_nextsolver(interner), + polarity: rustc_type_ir::PredicatePolarity::Positive, + }; + PredicateKind::Clause(ClauseKind::Trait(predicate)) + } + chalk_ir::FromEnv::Ty(ty) => PredicateKind::Clause(ClauseKind::WellFormed( + Term::Ty(ty.to_nextsolver(interner)), + )), + }, + chalk_ir::DomainGoal::IsLocal(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::IsUpstream(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::IsFullyVisible(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::LocalImplAllowed(trait_ref) => { + panic!("Should not be constructed.") + } + chalk_ir::DomainGoal::Compatible => panic!("Should not be constructed."), + chalk_ir::DomainGoal::DownstreamType(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::Reveal => panic!("Should not be constructed."), + chalk_ir::DomainGoal::ObjectSafe(trait_id) => panic!("Should not be constructed."), + } + } +} + +impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> TraitRef<'db> { + let args = self.substitution.to_nextsolver(interner); + TraitRef::new_from_args( + interner, + SolverDefId::TraitId(from_chalk_trait_id(self.trait_id)), + args, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause<Interner> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { + match self { + chalk_ir::WhereClause::Implemented(trait_ref) => { + let predicate = TraitPredicate { + trait_ref: trait_ref.to_nextsolver(interner), + polarity: rustc_type_ir::PredicatePolarity::Positive, + }; + PredicateKind::Clause(ClauseKind::Trait(predicate)) + } + chalk_ir::WhereClause::AliasEq(alias_eq) => { + let projection = match &alias_eq.alias { + chalk_ir::AliasTy::Projection(p) => p, + _ => unimplemented!(), + }; + let def_id = + SolverDefId::TypeAliasId(from_assoc_type_id(projection.associated_ty_id)); + let args = projection.substitution.to_nextsolver(interner); + let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); + let term: Term<'db> = term.into(); + let predicate = ProjectionPredicate { + projection_term: AliasTerm::new_from_args(interner, def_id, args), + term, + }; + PredicateKind::Clause(ClauseKind::Projection(predicate)) + } + chalk_ir::WhereClause::TypeOutlives(type_outlives) => { + let ty = type_outlives.ty.to_nextsolver(interner); + let r = type_outlives.lifetime.to_nextsolver(interner); + PredicateKind::Clause(ClauseKind::TypeOutlives(OutlivesPredicate(ty, r))) + } + chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { + let a = lifetime_outlives.a.to_nextsolver(interner); + let b = lifetime_outlives.b.to_nextsolver(interner); + PredicateKind::Clause(ClauseKind::RegionOutlives(OutlivesPredicate(a, b))) + } + } + } +} + +pub fn convert_canonical_args_for_result<'db>( + interner: DbInterner<'db>, + args: Canonical<'db, Vec<GenericArg<'db>>>, +) -> chalk_ir::Canonical<chalk_ir::ConstrainedSubst<Interner>> { + let Canonical { value, variables, max_universe } = args; + let binders = CanonicalVarKinds::from_iter( + Interner, + variables.iter().map(|v| match v { + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::General(_)) => { + CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::General), + chalk_ir::UniverseIndex::ROOT, + ) + } + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) => { + CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Integer), + chalk_ir::UniverseIndex::ROOT, + ) + } + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Float) => { + CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Float), + chalk_ir::UniverseIndex::ROOT, + ) + } + rustc_type_ir::CanonicalVarKind::Region(universe_index) => CanonicalVarKind::new( + chalk_ir::VariableKind::Lifetime, + chalk_ir::UniverseIndex::ROOT, + ), + rustc_type_ir::CanonicalVarKind::Const(universe_index) => CanonicalVarKind::new( + chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)), + chalk_ir::UniverseIndex::ROOT, + ), + rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => unimplemented!(), + rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => unimplemented!(), + rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => unimplemented!(), + }), + ); + chalk_ir::Canonical { + binders, + value: chalk_ir::ConstrainedSubst { + constraints: chalk_ir::Constraints::empty(Interner), + subst: convert_args_for_result(interner, &value), + }, + } +} + +pub fn convert_args_for_result<'db>( + interner: DbInterner<'db>, + args: &[GenericArg<'db>], +) -> crate::Substitution { + let mut substs = Vec::with_capacity(args.len()); + for arg in args { + match (*arg).kind() { + rustc_type_ir::GenericArgKind::Type(ty) => { + let ty = convert_ty_for_result(interner, ty); + substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner)); + } + rustc_type_ir::GenericArgKind::Lifetime(region) => { + let lifetime = convert_region_for_result(region); + substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner)); + } + rustc_type_ir::GenericArgKind::Const(const_) => { + substs.push( + chalk_ir::GenericArgData::Const(convert_const_for_result(interner, const_)) + .intern(Interner), + ); + } + } + } + Substitution::from_iter(Interner, substs) +} + +pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) -> crate::Ty { + use crate::{Scalar, TyKind}; + use chalk_ir::{FloatTy, IntTy, UintTy}; + match ty.kind() { + rustc_type_ir::TyKind::Bool => TyKind::Scalar(Scalar::Bool), + rustc_type_ir::TyKind::Char => TyKind::Scalar(Scalar::Char), + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) => { + TyKind::Scalar(Scalar::Int(IntTy::I8)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) => { + TyKind::Scalar(Scalar::Int(IntTy::I16)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) => { + TyKind::Scalar(Scalar::Int(IntTy::I32)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) => { + TyKind::Scalar(Scalar::Int(IntTy::I64)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) => { + TyKind::Scalar(Scalar::Int(IntTy::I128)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) => { + TyKind::Scalar(Scalar::Int(IntTy::Isize)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) => { + TyKind::Scalar(Scalar::Uint(UintTy::U8)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) => { + TyKind::Scalar(Scalar::Uint(UintTy::U16)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) => { + TyKind::Scalar(Scalar::Uint(UintTy::U32)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) => { + TyKind::Scalar(Scalar::Uint(UintTy::U64)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) => { + TyKind::Scalar(Scalar::Uint(UintTy::U128)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) => { + TyKind::Scalar(Scalar::Uint(UintTy::Usize)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) => { + TyKind::Scalar(Scalar::Float(FloatTy::F16)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) => { + TyKind::Scalar(Scalar::Float(FloatTy::F32)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) => { + TyKind::Scalar(Scalar::Float(FloatTy::F64)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) => { + TyKind::Scalar(Scalar::Float(FloatTy::F128)) + } + rustc_type_ir::TyKind::Str => TyKind::Str, + rustc_type_ir::TyKind::Error(_) => TyKind::Error, + rustc_type_ir::TyKind::Never => TyKind::Never, + + rustc_type_ir::TyKind::Adt(def, args) => { + let adt_id = def.inner().id; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::Adt(chalk_ir::AdtId(adt_id), subst) + } + + rustc_type_ir::TyKind::Infer(infer_ty) => { + let (var, kind) = match infer_ty { + rustc_type_ir::InferTy::TyVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::General) + } + rustc_type_ir::InferTy::IntVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::Integer) + } + rustc_type_ir::InferTy::FloatVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::Float) + } + rustc_type_ir::InferTy::FreshFloatTy(..) + | rustc_type_ir::InferTy::FreshIntTy(..) + | rustc_type_ir::InferTy::FreshTy(..) => { + panic!("Freshening shouldn't happen.") + } + }; + TyKind::InferenceVar(var, kind) + } + + rustc_type_ir::TyKind::Ref(r, ty, mutability) => { + let mutability = match mutability { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; + let r = convert_region_for_result(r); + let ty = convert_ty_for_result(interner, ty); + TyKind::Ref(mutability, r, ty) + } + + rustc_type_ir::TyKind::Tuple(tys) => { + let size = tys.len(); + let subst = Substitution::from_iter( + Interner, + tys.iter().map(|ty| { + chalk_ir::GenericArgData::Ty(convert_ty_for_result(interner, ty)) + .intern(Interner) + }), + ); + TyKind::Tuple(size, subst) + } + + rustc_type_ir::TyKind::Array(ty, const_) => { + let ty = convert_ty_for_result(interner, ty); + let const_ = convert_const_for_result(interner, const_); + TyKind::Array(ty, const_) + } + + rustc_type_ir::TyKind::Alias(alias_ty_kind, alias_ty) => match alias_ty_kind { + rustc_type_ir::AliasTyKind::Projection => { + let assoc_ty_id = match alias_ty.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + let associated_ty_id = to_assoc_type_id(assoc_ty_id); + let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); + TyKind::AssociatedType(associated_ty_id, substitution) + } + rustc_type_ir::AliasTyKind::Opaque => { + let opaque_ty_id = match alias_ty.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); + TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id: opaque_ty_id.into(), + substitution, + })) + } + rustc_type_ir::AliasTyKind::Inherent => unimplemented!(), + rustc_type_ir::AliasTyKind::Free => unimplemented!(), + }, + + rustc_type_ir::TyKind::Placeholder(placeholder) => { + let ui = chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }; + let placeholder_index = + chalk_ir::PlaceholderIndex { idx: placeholder.bound.var.as_usize(), ui }; + TyKind::Placeholder(placeholder_index) + } + + rustc_type_ir::TyKind::Bound(debruijn_index, ty) => TyKind::BoundVar(chalk_ir::BoundVar { + debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + index: ty.var.as_usize(), + }), + + rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => { + let num_binders = bound_sig.bound_vars().len(); + let sig = chalk_ir::FnSig { + abi: fn_header.abi, + safety: match fn_header.safety { + crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, + crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, + }, + variadic: fn_header.c_variadic, + }; + let args = GenericArgs::new_from_iter( + interner, + bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), + ); + let substitution = convert_args_for_result(interner, args.as_slice()); + let substitution = chalk_ir::FnSubst(substitution); + let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; + TyKind::Function(fnptr) + } + + rustc_type_ir::TyKind::Dynamic(preds, region, dyn_kind) => { + assert!(matches!(dyn_kind, rustc_type_ir::DynKind::Dyn)); + let self_ty = Ty::new_bound( + interner, + DebruijnIndex::from_u32(1), + BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_u32(0) }, + ); + let bounds = chalk_ir::QuantifiedWhereClauses::from_iter( + Interner, + preds.iter().map(|p| { + let binders = chalk_ir::VariableKinds::from_iter( + Interner, + p.bound_vars().iter().map(|b| match b { + BoundVarKind::Ty(kind) => { + chalk_ir::VariableKind::Ty(TyVariableKind::General) + } + BoundVarKind::Region(kind) => chalk_ir::VariableKind::Lifetime, + BoundVarKind::Const => { + chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)) + } + }), + ); + let where_clause = match p.skip_binder() { + rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { + let trait_ref = TraitRef::new( + interner, + trait_ref.def_id, + [self_ty.into()].into_iter().chain(trait_ref.args.iter()), + ); + let trait_id = match trait_ref.def_id { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = + convert_args_for_result(interner, trait_ref.args.as_slice()); + let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; + chalk_ir::WhereClause::Implemented(trait_ref) + } + rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => { + let trait_id = match trait_ { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = chalk_ir::Substitution::empty(Interner); + let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; + chalk_ir::WhereClause::Implemented(trait_ref) + } + rustc_type_ir::ExistentialPredicate::Projection(existential_projection) => { + let projection = ProjectionPredicate { + projection_term: AliasTerm::new( + interner, + existential_projection.def_id, + [self_ty.into()] + .iter() + .chain(existential_projection.args.iter()), + ), + term: existential_projection.term, + }; + let associated_ty_id = match projection.projection_term.def_id { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => unreachable!(), + }; + let substitution = convert_args_for_result( + interner, + projection.projection_term.args.as_slice(), + ); + let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }); + let ty = match projection.term { + Term::Ty(ty) => ty, + _ => unreachable!(), + }; + let ty = convert_ty_for_result(interner, ty); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + chalk_ir::WhereClause::AliasEq(alias_eq) + } + }; + chalk_ir::Binders::new(binders, where_clause) + }), + ); + let binders = chalk_ir::VariableKinds::from1( + Interner, + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), + ); + let bounds = chalk_ir::Binders::new(binders, bounds); + let dyn_ty = chalk_ir::DynTy { bounds, lifetime: convert_region_for_result(region) }; + TyKind::Dyn(dyn_ty) + } + + rustc_type_ir::TyKind::Slice(ty) => { + let ty = convert_ty_for_result(interner, ty); + TyKind::Slice(ty) + } + + rustc_type_ir::TyKind::Foreign(foreign) => { + let def_id = match foreign { + SolverDefId::ForeignId(id) => id, + _ => unreachable!(), + }; + TyKind::Foreign(to_foreign_def_id(def_id)) + } + rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(), + rustc_type_ir::TyKind::RawPtr(ty, mutability) => { + let mutability = match mutability { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; + let ty = convert_ty_for_result(interner, ty); + TyKind::Raw(mutability, ty) + } + rustc_type_ir::TyKind::FnDef(def_id, args) => { + let id = match def_id { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(Ctor::Struct(id)) => CallableDefId::StructId(id), + SolverDefId::Ctor(Ctor::Enum(id)) => CallableDefId::EnumVariantId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::FnDef(id.to_chalk(interner.db()), subst) + } + + rustc_type_ir::TyKind::Closure(def_id, args) => { + let id = match def_id { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::Closure(id.into(), subst) + } + rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(), + rustc_type_ir::TyKind::Coroutine(def_id, args) => { + let id = match def_id { + SolverDefId::InternedCoroutineId(id) => id, + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::Coroutine(id.into(), subst) + } + rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { + let id = match def_id { + SolverDefId::InternedCoroutineId(id) => id, + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::CoroutineWitness(id.into(), subst) + } + + rustc_type_ir::TyKind::Param(_) => unimplemented!(), + rustc_type_ir::TyKind::UnsafeBinder(_) => unimplemented!(), + } + .intern(Interner) +} + +fn convert_const_for_result<'db>(interner: DbInterner<'db>, const_: Const<'db>) -> crate::Const { + let value: chalk_ir::ConstValue<Interner> = match const_.kind() { + rustc_type_ir::ConstKind::Param(_) => unimplemented!(), + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { + chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) + } + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => { + panic!("Vars should not be freshened.") + } + rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { + chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + var.index(), + )) + } + rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { + chalk_ir::ConstValue::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: placeholder_const.universe.as_usize() }, + idx: placeholder_const.bound.as_usize(), + }) + } + rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => { + let id = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::UnevaluatedConst(id, subst), + }) + } + rustc_type_ir::ConstKind::Value(value_const) => { + let bytes = value_const.value.inner(); + let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::Bytes(bytes.0.clone(), bytes.1.clone()), + }); + return chalk_ir::ConstData { + ty: convert_ty_for_result(interner, value_const.ty), + value, + } + .intern(Interner); + } + rustc_type_ir::ConstKind::Error(_) => { + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::Unknown, + }) + } + rustc_type_ir::ConstKind::Expr(_) => unimplemented!(), + }; + chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) +} + +fn convert_region_for_result<'db>(region: Region<'db>) -> crate::Lifetime { + match region.kind() { + rustc_type_ir::RegionKind::ReEarlyParam(early) => unimplemented!(), + rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + bound.var.as_usize(), + )), + ), + rustc_type_ir::RegionKind::ReLateParam(_) => unimplemented!(), + rustc_type_ir::RegionKind::ReStatic => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Static) + } + rustc_type_ir::RegionKind::ReVar(vid) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())), + ), + rustc_type_ir::RegionKind::RePlaceholder(placeholder) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { + idx: placeholder.bound.var.as_usize(), + ui: chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }, + }), + ), + rustc_type_ir::RegionKind::ReErased => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Erased) + } + rustc_type_ir::RegionKind::ReError(_) => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Error) + } + } +}
diff --git a/crates/hir-ty/src/next_solver/opaques.rs b/crates/hir-ty/src/next_solver/opaques.rs new file mode 100644 index 0000000..43589ab --- /dev/null +++ b/crates/hir-ty/src/next_solver/opaques.rs
@@ -0,0 +1,167 @@ +//! Things related to opaques in the next-trait-solver. + +use intern::Interned; +use rustc_ast_ir::try_visit; + +use crate::next_solver::SolverDefId; + +use super::{CanonicalVarKind, DbInterner, interned_vec_nolifetime_salsa}; + +pub type OpaqueTypeKey<'db> = rustc_type_ir::OpaqueTypeKey<DbInterner<'db>>; +pub type PredefinedOpaquesData<'db> = rustc_type_ir::solve::PredefinedOpaquesData<DbInterner<'db>>; +pub type ExternalConstraintsData<'db> = + rustc_type_ir::solve::ExternalConstraintsData<DbInterner<'db>>; + +#[salsa::interned(constructor = new_, debug)] +pub struct PredefinedOpaques<'db> { + #[returns(ref)] + kind_: rustc_type_ir::solve::PredefinedOpaquesData<DbInterner<'db>>, +} + +impl<'db> PredefinedOpaques<'db> { + pub fn new(interner: DbInterner<'db>, data: PredefinedOpaquesData<'db>) -> Self { + PredefinedOpaques::new_(interner.db(), data) + } + + pub fn inner(&self) -> &PredefinedOpaquesData<'db> { + salsa::with_attached_database(|db| { + let inner = self.kind_(db); + // SAFETY: ¯\_(ツ)_/¯ + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for PredefinedOpaques<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.opaque_types.visit_with(visitor) + } +} + +impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for PredefinedOpaques<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(PredefinedOpaques::new( + folder.cx(), + PredefinedOpaquesData { + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.try_fold_with(folder)) + .collect::<Result<_, F::Error>>()?, + }, + )) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + PredefinedOpaques::new( + folder.cx(), + PredefinedOpaquesData { + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.fold_with(folder)) + .collect(), + }, + ) + } +} + +impl<'db> std::ops::Deref for PredefinedOpaques<'db> { + type Target = PredefinedOpaquesData<'db>; + + fn deref(&self) -> &Self::Target { + self.inner() + } +} + +interned_vec_nolifetime_salsa!(SolverDefIds, SolverDefId); + +#[salsa::interned(constructor = new_, debug)] +pub struct ExternalConstraints<'db> { + #[returns(ref)] + kind_: rustc_type_ir::solve::ExternalConstraintsData<DbInterner<'db>>, +} + +impl<'db> ExternalConstraints<'db> { + pub fn new(interner: DbInterner<'db>, data: ExternalConstraintsData<'db>) -> Self { + ExternalConstraints::new_(interner.db(), data) + } + + pub fn inner(&self) -> &ExternalConstraintsData<'db> { + salsa::with_attached_database(|db| { + let inner = self.kind_(db); + // SAFETY: ¯\_(ツ)_/¯ + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> std::ops::Deref for ExternalConstraints<'db> { + type Target = ExternalConstraintsData<'db>; + + fn deref(&self) -> &Self::Target { + self.inner() + } +} + +impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for ExternalConstraints<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + try_visit!(self.region_constraints.visit_with(visitor)); + try_visit!(self.opaque_types.visit_with(visitor)); + self.normalization_nested_goals.visit_with(visitor) + } +} + +impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for ExternalConstraints<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(ExternalConstraints::new( + folder.cx(), + ExternalConstraintsData { + region_constraints: self.region_constraints.clone().try_fold_with(folder)?, + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.try_fold_with(folder)) + .collect::<Result<_, F::Error>>()?, + normalization_nested_goals: self + .normalization_nested_goals + .clone() + .try_fold_with(folder)?, + }, + )) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + ExternalConstraints::new( + folder.cx(), + ExternalConstraintsData { + region_constraints: self.region_constraints.clone().fold_with(folder), + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.fold_with(folder)) + .collect(), + normalization_nested_goals: self + .normalization_nested_goals + .clone() + .fold_with(folder), + }, + ) + } +}
diff --git a/crates/hir-ty/src/next_solver/predicate.rs b/crates/hir-ty/src/next_solver/predicate.rs new file mode 100644 index 0000000..50261bb --- /dev/null +++ b/crates/hir-ty/src/next_solver/predicate.rs
@@ -0,0 +1,894 @@ +//! Things related to predicates. + +use std::cmp::Ordering; + +use intern::Interned; +use rustc_ast_ir::try_visit; +use rustc_type_ir::{ + self as ty, CollectAndApply, DebruijnIndex, EarlyBinder, FlagComputation, Flags, + PredicatePolarity, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitable, Upcast, UpcastFrom, VisitorResult, WithCachedTypeInfo, + elaborate::Elaboratable, + error::{ExpectedFound, TypeError}, + inherent::{IntoKind, SliceLike}, + relate::Relate, +}; +use smallvec::{SmallVec, smallvec}; + +use super::{Binder, BoundVarKinds, DbInterner, Region, Ty, interned_vec_db}; + +pub type BoundExistentialPredicate<'db> = Binder<'db, ExistentialPredicate<'db>>; + +pub type TraitRef<'db> = ty::TraitRef<DbInterner<'db>>; +pub type AliasTerm<'db> = ty::AliasTerm<DbInterner<'db>>; +pub type ProjectionPredicate<'db> = ty::ProjectionPredicate<DbInterner<'db>>; +pub type ExistentialPredicate<'db> = ty::ExistentialPredicate<DbInterner<'db>>; +pub type ExistentialTraitRef<'db> = ty::ExistentialTraitRef<DbInterner<'db>>; +pub type ExistentialProjection<'db> = ty::ExistentialProjection<DbInterner<'db>>; +pub type TraitPredicate<'db> = ty::TraitPredicate<DbInterner<'db>>; +pub type ClauseKind<'db> = ty::ClauseKind<DbInterner<'db>>; +pub type PredicateKind<'db> = ty::PredicateKind<DbInterner<'db>>; +pub type NormalizesTo<'db> = ty::NormalizesTo<DbInterner<'db>>; +pub type CoercePredicate<'db> = ty::CoercePredicate<DbInterner<'db>>; +pub type SubtypePredicate<'db> = ty::SubtypePredicate<DbInterner<'db>>; +pub type OutlivesPredicate<'db, T> = ty::OutlivesPredicate<DbInterner<'db>, T>; +pub type RegionOutlivesPredicate<'db> = OutlivesPredicate<'db, Region<'db>>; +pub type TypeOutlivesPredicate<'db> = OutlivesPredicate<'db, Ty<'db>>; +pub type PolyTraitPredicate<'db> = Binder<'db, TraitPredicate<'db>>; +pub type PolyRegionOutlivesPredicate<'db> = Binder<'db, RegionOutlivesPredicate<'db>>; +pub type PolyTypeOutlivesPredicate<'db> = Binder<'db, TypeOutlivesPredicate<'db>>; +pub type PolySubtypePredicate<'db> = Binder<'db, SubtypePredicate<'db>>; +pub type PolyCoercePredicate<'db> = Binder<'db, CoercePredicate<'db>>; +pub type PolyProjectionPredicate<'db> = Binder<'db, ProjectionPredicate<'db>>; +pub type PolyTraitRef<'db> = Binder<'db, TraitRef<'db>>; +pub type PolyExistentialTraitRef<'db> = Binder<'db, ExistentialTraitRef<'db>>; +pub type PolyExistentialProjection<'db> = Binder<'db, ExistentialProjection<'db>>; + +/// Compares via an ordering that will not change if modules are reordered or other changes are +/// made to the tree. In particular, this ordering is preserved across incremental compilations. +fn stable_cmp_existential_predicate<'db>( + a: &ExistentialPredicate<'db>, + b: &ExistentialPredicate<'db>, +) -> Ordering { + // FIXME: this is actual unstable - see impl in predicate.rs in `rustc_middle` + match (a, b) { + (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => Ordering::Equal, + (ExistentialPredicate::Projection(a), ExistentialPredicate::Projection(b)) => { + // Should sort by def path hash + Ordering::Equal + } + (ExistentialPredicate::AutoTrait(a), ExistentialPredicate::AutoTrait(b)) => { + // Should sort by def path hash + Ordering::Equal + } + (ExistentialPredicate::Trait(_), _) => Ordering::Less, + (ExistentialPredicate::Projection(_), ExistentialPredicate::Trait(_)) => Ordering::Greater, + (ExistentialPredicate::Projection(_), _) => Ordering::Less, + (ExistentialPredicate::AutoTrait(_), _) => Ordering::Greater, + } +} +interned_vec_db!(BoundExistentialPredicates, BoundExistentialPredicate); + +impl<'db> rustc_type_ir::inherent::BoundExistentialPredicates<DbInterner<'db>> + for BoundExistentialPredicates<'db> +{ + fn principal_def_id(self) -> Option<<DbInterner<'db> as rustc_type_ir::Interner>::DefId> { + self.principal().map(|trait_ref| trait_ref.skip_binder().def_id) + } + + fn principal( + self, + ) -> Option< + rustc_type_ir::Binder<DbInterner<'db>, rustc_type_ir::ExistentialTraitRef<DbInterner<'db>>>, + > { + self.inner()[0] + .map_bound(|this| match this { + ExistentialPredicate::Trait(tr) => Some(tr), + _ => None, + }) + .transpose() + } + + fn auto_traits( + self, + ) -> impl IntoIterator<Item = <DbInterner<'db> as rustc_type_ir::Interner>::DefId> { + self.iter().filter_map(|predicate| match predicate.skip_binder() { + ExistentialPredicate::AutoTrait(did) => Some(did), + _ => None, + }) + } + + fn projection_bounds( + self, + ) -> impl IntoIterator< + Item = rustc_type_ir::Binder< + DbInterner<'db>, + rustc_type_ir::ExistentialProjection<DbInterner<'db>>, + >, + > { + self.iter().filter_map(|predicate| { + predicate + .map_bound(|pred| match pred { + ExistentialPredicate::Projection(projection) => Some(projection), + _ => None, + }) + .transpose() + }) + } +} + +impl<'db> rustc_type_ir::relate::Relate<DbInterner<'db>> for BoundExistentialPredicates<'db> { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + let interner = relation.cx(); + + // We need to perform this deduplication as we sometimes generate duplicate projections in `a`. + let mut a_v: Vec<_> = a.into_iter().collect(); + let mut b_v: Vec<_> = b.into_iter().collect(); + // `skip_binder` here is okay because `stable_cmp` doesn't look at binders + a_v.sort_by(|a, b| { + stable_cmp_existential_predicate(a.as_ref().skip_binder(), b.as_ref().skip_binder()) + }); + a_v.dedup(); + b_v.sort_by(|a, b| { + stable_cmp_existential_predicate(a.as_ref().skip_binder(), b.as_ref().skip_binder()) + }); + b_v.dedup(); + if a_v.len() != b_v.len() { + return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))); + } + + let v = std::iter::zip(a_v, b_v).map( + |(ep_a, ep_b): ( + Binder<'_, ty::ExistentialPredicate<_>>, + Binder<'_, ty::ExistentialPredicate<_>>, + )| { + match (ep_a.skip_binder(), ep_b.skip_binder()) { + (ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => { + Ok(ep_a.rebind(ty::ExistentialPredicate::Trait( + relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), + ))) + } + ( + ty::ExistentialPredicate::Projection(a), + ty::ExistentialPredicate::Projection(b), + ) => Ok(ep_a.rebind(ty::ExistentialPredicate::Projection( + relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), + ))), + ( + ty::ExistentialPredicate::AutoTrait(a), + ty::ExistentialPredicate::AutoTrait(b), + ) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))), + _ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))), + } + }, + ); + + CollectAndApply::collect_and_apply(v, |g| { + BoundExistentialPredicates::new_from_iter(interner, g.iter().cloned()) + }) + } +} + +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] +pub struct InternedWrapperNoDebug<T>(pub(crate) T); + +#[salsa::interned(constructor = new_)] +pub struct Predicate<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug<WithCachedTypeInfo<Binder<'db, PredicateKind<'db>>>>, +} + +impl<'db> std::fmt::Debug for Predicate<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().internee.fmt(f) + } +} + +impl<'db> std::fmt::Debug + for InternedWrapperNoDebug<WithCachedTypeInfo<Binder<'db, PredicateKind<'db>>>> +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Binder<")?; + match self.0.internee.skip_binder() { + rustc_type_ir::PredicateKind::Clause(clause_kind) => { + write!(f, "{clause_kind:?}") + } + rustc_type_ir::PredicateKind::DynCompatible(trait_def_id) => { + write!(f, "the trait `{trait_def_id:?}` is dyn-compatible") + } + rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => { + write!(f, "{subtype_predicate:?}") + } + rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => { + write!(f, "{coerce_predicate:?}") + } + rustc_type_ir::PredicateKind::ConstEquate(c1, c2) => { + write!(f, "the constant `{c1:?}` equals `{c2:?}`") + } + rustc_type_ir::PredicateKind::Ambiguous => write!(f, "ambiguous"), + rustc_type_ir::PredicateKind::NormalizesTo(data) => write!(f, "{data:?}"), + rustc_type_ir::PredicateKind::AliasRelate(t1, t2, dir) => { + write!(f, "{t1:?} {dir:?} {t2:?}") + } + }?; + write!(f, ", [{:?}]>", self.0.internee.bound_vars())?; + Ok(()) + } +} + +impl<'db> Predicate<'db> { + pub fn new(interner: DbInterner<'db>, kind: Binder<'db, PredicateKind<'db>>) -> Self { + let flags = FlagComputation::for_predicate(kind); + let cached = WithCachedTypeInfo { + internee: kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + Predicate::new_(interner.db(), InternedWrapperNoDebug(cached)) + } + + pub fn inner(&self) -> &WithCachedTypeInfo<Binder<'db, PredicateKind<'db>>> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Predicate<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + /// Flips the polarity of a Predicate. + /// + /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. + pub fn flip_polarity(self) -> Option<Predicate<'db>> { + let kind = self + .kind() + .map_bound(|kind| match kind { + PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity, + })) => Some(PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: polarity.flip(), + }))), + + _ => None, + }) + .transpose()?; + + Some(Predicate::new(DbInterner::conjure(), kind)) + } + + pub fn as_trait_clause(self) -> Option<PolyTraitPredicate<'db>> { + let predicate = self.kind(); + match predicate.skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)), + PredicateKind::Clause(ClauseKind::Projection(..)) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) + | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) + | PredicateKind::NormalizesTo(..) + | PredicateKind::AliasRelate(..) + | PredicateKind::Subtype(..) + | PredicateKind::Coerce(..) + | PredicateKind::Clause(ClauseKind::RegionOutlives(..)) + | PredicateKind::Clause(ClauseKind::WellFormed(..)) + | PredicateKind::DynCompatible(..) + | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) + | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) + | PredicateKind::ConstEquate(..) + | PredicateKind::Ambiguous => None, + } + } +} + +// FIXME: should make a "header" in interned_vec + +#[derive(Debug, Clone)] +pub struct InternedClausesWrapper<'db>(SmallVec<[Clause<'db>; 2]>, TypeFlags, DebruijnIndex); + +impl<'db> PartialEq for InternedClausesWrapper<'db> { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +impl<'db> Eq for InternedClausesWrapper<'db> {} + +impl<'db> std::hash::Hash for InternedClausesWrapper<'db> { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.0.hash(state) + } +} + +type InternedClauses<'db> = Interned<InternedClausesWrapper<'db>>; + +#[salsa::interned(constructor = new_)] +pub struct Clauses<'db> { + #[returns(ref)] + inner_: InternedClausesWrapper<'db>, +} + +impl<'db> Clauses<'db> { + pub fn new_from_iter( + interner: DbInterner<'db>, + data: impl IntoIterator<Item = Clause<'db>>, + ) -> Self { + let clauses: SmallVec<_> = data.into_iter().collect(); + let flags = FlagComputation::<DbInterner<'db>>::for_clauses(&clauses); + let wrapper = InternedClausesWrapper(clauses, flags.flags, flags.outer_exclusive_binder); + Clauses::new_(interner.db(), wrapper) + } + + pub fn inner(&self) -> &InternedClausesWrapper<'db> { + salsa::with_attached_database(|db| { + let inner = self.inner_(db); + // SAFETY: The caller already has access to a `Clauses<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> std::fmt::Debug for Clauses<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().0.fmt(f) + } +} + +impl<'db> rustc_type_ir::inherent::Clauses<DbInterner<'db>> for Clauses<'db> {} + +impl<'db> rustc_type_ir::inherent::SliceLike for Clauses<'db> { + type Item = Clause<'db>; + + type IntoIter = <smallvec::SmallVec<[Clause<'db>; 2]> as IntoIterator>::IntoIter; + + fn iter(self) -> Self::IntoIter { + self.inner().0.clone().into_iter() + } + + fn as_slice(&self) -> &[Self::Item] { + self.inner().0.as_slice() + } +} + +impl<'db> IntoIterator for Clauses<'db> { + type Item = Clause<'db>; + type IntoIter = <Self as rustc_type_ir::inherent::SliceLike>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + rustc_type_ir::inherent::SliceLike::iter(self) + } +} + +impl<'db> Default for Clauses<'db> { + fn default() -> Self { + Clauses::new_from_iter(DbInterner::conjure(), []) + } +} + +impl<'db> rustc_type_ir::TypeSuperFoldable<DbInterner<'db>> for Clauses<'db> { + fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + let mut clauses: SmallVec<[_; 2]> = SmallVec::with_capacity(self.inner().0.len()); + for c in self { + clauses.push(c.try_fold_with(folder)?); + } + Ok(Clauses::new_from_iter(folder.cx(), clauses)) + } + + fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Self { + let mut clauses: SmallVec<[_; 2]> = SmallVec::with_capacity(self.inner().0.len()); + for c in self { + clauses.push(c.fold_with(folder)); + } + Clauses::new_from_iter(folder.cx(), clauses) + } +} + +impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for Clauses<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.try_fold_with(folder)).collect::<Result<_, _>>()?; + Ok(Clauses::new_from_iter(folder.cx(), inner)) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = self.iter().map(|v| v.fold_with(folder)).collect(); + Clauses::new_from_iter(folder.cx(), inner) + } +} + +impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for Clauses<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + use rustc_ast_ir::visit::VisitorResult; + use rustc_type_ir::inherent::SliceLike as _; + rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter()); + V::Result::output() + } +} + +impl<'db> rustc_type_ir::Flags for Clauses<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().1 + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().2 + } +} + +impl<'db> rustc_type_ir::TypeSuperVisitable<DbInterner<'db>> for Clauses<'db> { + fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.as_slice().visit_with(visitor) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] // TODO implement Debug by hand +pub struct Clause<'db>(pub(crate) Predicate<'db>); + +// We could cram the reveal into the clauses like rustc does, probably +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ParamEnv<'db> { + pub(crate) clauses: Clauses<'db>, +} + +impl<'db> ParamEnv<'db> { + pub fn empty() -> Self { + ParamEnv { clauses: Clauses::new_from_iter(DbInterner::conjure(), []) } + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for ParamEnv<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + try_visit!(self.clauses.visit_with(visitor)); + V::Result::output() + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for ParamEnv<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(ParamEnv { clauses: self.clauses.try_fold_with(folder)? }) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + ParamEnv { clauses: self.clauses.fold_with(folder) } + } +} + +impl<'db> rustc_type_ir::inherent::ParamEnv<DbInterner<'db>> for ParamEnv<'db> { + fn caller_bounds(self) -> impl rustc_type_ir::inherent::SliceLike<Item = Clause<'db>> { + self.clauses + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ParamEnvAnd<'db, T> { + pub param_env: ParamEnv<'db>, + pub value: T, +} + +impl<'db, T> ParamEnvAnd<'db, T> { + pub fn into_parts(self) -> (ParamEnv<'db>, T) { + (self.param_env, self.value) + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for Predicate<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_predicate(*self) + } +} + +impl<'db> TypeSuperVisitable<DbInterner<'db>> for Predicate<'db> { + fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + (*self).kind().visit_with(visitor) + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for Predicate<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + folder.try_fold_predicate(self) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + folder.fold_predicate(self) + } +} + +impl<'db> TypeSuperFoldable<DbInterner<'db>> for Predicate<'db> { + fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + let new = self.kind().try_fold_with(folder)?; + Ok(Predicate::new(folder.cx(), new)) + } + fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Self { + let new = self.kind().fold_with(folder); + Predicate::new(folder.cx(), new) + } +} + +impl<'db> Elaboratable<DbInterner<'db>> for Predicate<'db> { + fn predicate(&self) -> <DbInterner<'db> as rustc_type_ir::Interner>::Predicate { + *self + } + + fn child(&self, clause: <DbInterner<'db> as rustc_type_ir::Interner>::Clause) -> Self { + clause.as_predicate() + } + + fn child_with_derived_cause( + &self, + clause: <DbInterner<'db> as rustc_type_ir::Interner>::Clause, + _span: <DbInterner<'db> as rustc_type_ir::Interner>::Span, + _parent_trait_pred: rustc_type_ir::Binder< + DbInterner<'db>, + rustc_type_ir::TraitPredicate<DbInterner<'db>>, + >, + _index: usize, + ) -> Self { + clause.as_predicate() + } +} + +impl<'db> Flags for Predicate<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().outer_exclusive_binder + } +} + +impl<'db> IntoKind for Predicate<'db> { + type Kind = Binder<'db, PredicateKind<'db>>; + + fn kind(self) -> Self::Kind { + self.inner().internee + } +} + +impl<'db> UpcastFrom<DbInterner<'db>, ty::PredicateKind<DbInterner<'db>>> for Predicate<'db> { + fn upcast_from(from: ty::PredicateKind<DbInterner<'db>>, interner: DbInterner<'db>) -> Self { + Binder::dummy(from).upcast(interner) + } +} +impl<'db> + UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::PredicateKind<DbInterner<'db>>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::Binder<DbInterner<'db>, ty::PredicateKind<DbInterner<'db>>>, + interner: DbInterner<'db>, + ) -> Self { + Predicate::new(interner, from) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::ClauseKind<DbInterner<'db>>> for Predicate<'db> { + fn upcast_from(from: ty::ClauseKind<DbInterner<'db>>, interner: DbInterner<'db>) -> Self { + Binder::dummy(PredicateKind::Clause(from)).upcast(interner) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::ClauseKind<DbInterner<'db>>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::Binder<DbInterner<'db>, ty::ClauseKind<DbInterner<'db>>>, + interner: DbInterner<'db>, + ) -> Self { + from.map_bound(PredicateKind::Clause).upcast(interner) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, Clause<'db>> for Predicate<'db> { + fn upcast_from(from: Clause<'db>, _interner: DbInterner<'db>) -> Self { + from.0 + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::NormalizesTo<DbInterner<'db>>> for Predicate<'db> { + fn upcast_from(from: ty::NormalizesTo<DbInterner<'db>>, interner: DbInterner<'db>) -> Self { + PredicateKind::NormalizesTo(from).upcast(interner) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>> for Predicate<'db> { + fn upcast_from(from: ty::TraitRef<DbInterner<'db>>, interner: DbInterner<'db>) -> Self { + Binder::dummy(from).upcast(interner) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::Binder<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>>, + interner: DbInterner<'db>, + ) -> Self { + from.map_bound(|trait_ref| TraitPredicate { + trait_ref, + polarity: PredicatePolarity::Positive, + }) + .upcast(interner) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, Binder<'db, ty::TraitPredicate<DbInterner<'db>>>> + for Predicate<'db> +{ + fn upcast_from( + from: Binder<'db, ty::TraitPredicate<DbInterner<'db>>>, + interner: DbInterner<'db>, + ) -> Self { + from.map_bound(|it| PredicateKind::Clause(ClauseKind::Trait(it))).upcast(interner) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, Binder<'db, ProjectionPredicate<'db>>> for Predicate<'db> { + fn upcast_from(from: Binder<'db, ProjectionPredicate<'db>>, interner: DbInterner<'db>) -> Self { + from.map_bound(|it| PredicateKind::Clause(ClauseKind::Projection(it))).upcast(interner) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ProjectionPredicate<'db>> for Predicate<'db> { + fn upcast_from(from: ProjectionPredicate<'db>, interner: DbInterner<'db>) -> Self { + PredicateKind::Clause(ClauseKind::Projection(from)).upcast(interner) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::TraitPredicate<DbInterner<'db>>> for Predicate<'db> { + fn upcast_from(from: ty::TraitPredicate<DbInterner<'db>>, interner: DbInterner<'db>) -> Self { + PredicateKind::Clause(ClauseKind::Trait(from)).upcast(interner) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::OutlivesPredicate<DbInterner<'db>, Ty<'db>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::OutlivesPredicate<DbInterner<'db>, Ty<'db>>, + interner: DbInterner<'db>, + ) -> Self { + PredicateKind::Clause(ClauseKind::TypeOutlives(from)).upcast(interner) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::OutlivesPredicate<DbInterner<'db>, Region<'db>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::OutlivesPredicate<DbInterner<'db>, Region<'db>>, + interner: DbInterner<'db>, + ) -> Self { + PredicateKind::Clause(ClauseKind::RegionOutlives(from)).upcast(interner) + } +} + +impl<'db> rustc_type_ir::inherent::Predicate<DbInterner<'db>> for Predicate<'db> { + fn as_clause(self) -> Option<<DbInterner<'db> as rustc_type_ir::Interner>::Clause> { + match self.kind().skip_binder() { + PredicateKind::Clause(..) => Some(self.expect_clause()), + _ => 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(..) + | PredicateKind::NormalizesTo(..) => 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::Ambiguous => true, + } + } +} + +impl<'db> Predicate<'db> { + /// Assert that the predicate is a clause. + pub fn expect_clause(self) -> Clause<'db> { + match self.kind().skip_binder() { + PredicateKind::Clause(..) => Clause(self), + _ => panic!("{self:?} is not a clause"), + } + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for Clause<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_predicate((*self).as_predicate()) + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for Clause<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(folder.try_fold_predicate(self.as_predicate())?.expect_clause()) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + folder.fold_predicate(self.as_predicate()).expect_clause() + } +} + +impl<'db> IntoKind for Clause<'db> { + type Kind = Binder<'db, ClauseKind<'db>>; + + fn kind(self) -> Self::Kind { + self.0.kind().map_bound(|pk| match pk { + PredicateKind::Clause(kind) => kind, + _ => unreachable!(), + }) + } +} + +impl<'db> Clause<'db> { + pub fn as_predicate(self) -> Predicate<'db> { + self.0 + } +} + +impl<'db> Elaboratable<DbInterner<'db>> for Clause<'db> { + fn predicate(&self) -> <DbInterner<'db> as rustc_type_ir::Interner>::Predicate { + self.0 + } + + fn child(&self, clause: <DbInterner<'db> as rustc_type_ir::Interner>::Clause) -> Self { + clause + } + + fn child_with_derived_cause( + &self, + clause: <DbInterner<'db> as rustc_type_ir::Interner>::Clause, + _span: <DbInterner<'db> as rustc_type_ir::Interner>::Span, + _parent_trait_pred: rustc_type_ir::Binder< + DbInterner<'db>, + rustc_type_ir::TraitPredicate<DbInterner<'db>>, + >, + _index: usize, + ) -> Self { + clause + } +} + +impl<'db> UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::ClauseKind<DbInterner<'db>>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::Binder<DbInterner<'db>, ty::ClauseKind<DbInterner<'db>>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.map_bound(PredicateKind::Clause).upcast(interner)) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>> for Clause<'db> { + fn upcast_from(from: ty::TraitRef<DbInterner<'db>>, interner: DbInterner<'db>) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::Binder<DbInterner<'db>, ty::TraitRef<DbInterner<'db>>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::TraitPredicate<DbInterner<'db>>> for Clause<'db> { + fn upcast_from(from: ty::TraitPredicate<DbInterner<'db>>, interner: DbInterner<'db>) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> + UpcastFrom<DbInterner<'db>, ty::Binder<DbInterner<'db>, ty::TraitPredicate<DbInterner<'db>>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::Binder<DbInterner<'db>, ty::TraitPredicate<DbInterner<'db>>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> UpcastFrom<DbInterner<'db>, ty::ProjectionPredicate<DbInterner<'db>>> for Clause<'db> { + fn upcast_from( + from: ty::ProjectionPredicate<DbInterner<'db>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> + UpcastFrom< + DbInterner<'db>, + ty::Binder<DbInterner<'db>, ty::ProjectionPredicate<DbInterner<'db>>>, + > for Clause<'db> +{ + fn upcast_from( + from: ty::Binder<DbInterner<'db>, ty::ProjectionPredicate<DbInterner<'db>>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} + +impl<'db> rustc_type_ir::inherent::Clause<DbInterner<'db>> for Clause<'db> { + fn as_predicate(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::Predicate { + self.0 + } + + fn instantiate_supertrait( + self, + cx: DbInterner<'db>, + trait_ref: rustc_type_ir::Binder<DbInterner<'db>, rustc_type_ir::TraitRef<DbInterner<'db>>>, + ) -> Self { + tracing::debug!(?self, ?trait_ref); + // See the rustc impl for a long comment + let bound_pred = self.kind(); + let pred_bound_vars = bound_pred.bound_vars(); + let trait_bound_vars = trait_ref.bound_vars(); + // 1) Self: Bar1<'a, '^0.0> -> Self: Bar1<'a, '^0.1> + 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); + // 3) ['x] + ['b] -> ['x, 'b] + let bound_vars = + BoundVarKinds::new_from_iter(cx, trait_bound_vars.iter().chain(pred_bound_vars.iter())); + + let predicate: Predicate<'db> = + ty::Binder::bind_with_vars(PredicateKind::Clause(new), bound_vars).upcast(cx); + predicate.expect_clause() + } +}
diff --git a/crates/hir-ty/src/next_solver/project.rs b/crates/hir-ty/src/next_solver/project.rs new file mode 100644 index 0000000..e578087 --- /dev/null +++ b/crates/hir-ty/src/next_solver/project.rs
@@ -0,0 +1,3 @@ +//! Projection code for next-solver. + +pub(crate) mod solve_normalize;
diff --git a/crates/hir-ty/src/next_solver/project/solve_normalize.rs b/crates/hir-ty/src/next_solver/project/solve_normalize.rs new file mode 100644 index 0000000..a8fb2ae --- /dev/null +++ b/crates/hir-ty/src/next_solver/project/solve_normalize.rs
@@ -0,0 +1,264 @@ +//! Normalization within a next-solver infer context. + +use std::fmt::Debug; + +use rustc_next_trait_solver::placeholder::BoundVarReplacer; +use rustc_type_ir::{ + AliasRelationDirection, FallibleTypeFolder, Flags, Interner, TermKind, TypeFoldable, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, + inherent::{IntoKind, Span as _, Term as _}, +}; + +use crate::next_solver::{ + Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Span, Term, Ty, + TyKind, + fulfill::{FulfillmentCtxt, NextSolverError}, + infer::{ + InferCtxt, + at::At, + traits::{Obligation, ObligationCause}, + }, + util::PlaceholderReplacer, +}; + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +pub fn deeply_normalize<'db, T>(at: At<'_, 'db>, value: T) -> Result<T, Vec<NextSolverError<'db>>> +where + T: TypeFoldable<DbInterner<'db>>, +{ + assert!(!value.has_escaping_bound_vars()); + deeply_normalize_with_skipped_universes(at, value, vec![]) +} + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +/// +/// Additionally takes a list of universes which represents the binders which have been +/// entered before passing `value` to the function. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. +pub fn deeply_normalize_with_skipped_universes<'db, T>( + at: At<'_, 'db>, + value: T, + universes: Vec<Option<UniverseIndex>>, +) -> Result<T, Vec<NextSolverError<'db>>> +where + T: TypeFoldable<DbInterner<'db>>, +{ + let (value, coroutine_goals) = + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + at, value, universes, + )?; + assert_eq!(coroutine_goals, vec![]); + + Ok(value) +} + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +/// +/// Additionally takes a list of universes which represents the binders which have been +/// entered before passing `value` to the function. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. +/// +/// This returns a set of stalled obligations involving coroutines if the typing mode of +/// the underlying infcx has any stalled coroutine def ids. +pub fn deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals<'db, T>( + at: At<'_, 'db>, + value: T, + universes: Vec<Option<UniverseIndex>>, +) -> Result<(T, Vec<Goal<'db, Predicate<'db>>>), Vec<NextSolverError<'db>>> +where + T: TypeFoldable<DbInterner<'db>>, +{ + let fulfill_cx = FulfillmentCtxt::new(at.infcx); + let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes }; + let value = value.try_fold_with(&mut folder)?; + let errors = folder.fulfill_cx.select_all_or_error(at.infcx); + if errors.is_empty() { Ok((value, vec![])) } else { Err(errors) } +} + +struct NormalizationFolder<'me, 'db> { + at: At<'me, 'db>, + fulfill_cx: FulfillmentCtxt<'db>, + depth: usize, + universes: Vec<Option<UniverseIndex>>, +} + +impl<'db> NormalizationFolder<'_, 'db> { + fn normalize_alias_term( + &mut self, + alias_term: Term<'db>, + ) -> Result<Term<'db>, Vec<NextSolverError<'db>>> { + let infcx = self.at.infcx; + let tcx = infcx.interner; + let recursion_limit = tcx.recursion_limit(); + if self.depth > recursion_limit { + return Err(vec![]); + } + + self.depth += 1; + + let infer_term = infcx.next_term_var_of_kind(alias_term); + let obligation = Obligation::new( + tcx, + self.at.cause.clone(), + self.at.param_env, + PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate), + ); + + self.fulfill_cx.register_predicate_obligation(infcx, obligation); + self.select_all_and_stall_coroutine_predicates()?; + + // Alias is guaranteed to be fully structurally resolved, + // so we can super fold here. + let term = infcx.resolve_vars_if_possible(infer_term); + // super-folding the `term` will directly fold the `Ty` or `Const` so + // we have to match on the term and super-fold them manually. + let result = match term.kind() { + TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(), + TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(), + }; + self.depth -= 1; + Ok(result) + } + + fn select_all_and_stall_coroutine_predicates( + &mut self, + ) -> Result<(), Vec<NextSolverError<'db>>> { + let errors = self.fulfill_cx.select_where_possible(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + Ok(()) + } +} + +impl<'db> FallibleTypeFolder<DbInterner<'db>> for NormalizationFolder<'_, 'db> { + type Error = Vec<NextSolverError<'db>>; + + fn cx(&self) -> DbInterner<'db> { + self.at.infcx.interner + } + + fn try_fold_binder<T: TypeFoldable<DbInterner<'db>>>( + &mut self, + t: Binder<'db, T>, + ) -> Result<Binder<'db, T>, Self::Error> { + self.universes.push(None); + let t = t.try_super_fold_with(self)?; + self.universes.pop(); + Ok(t) + } + + #[tracing::instrument(level = "trace", skip(self), ret)] + fn try_fold_ty(&mut self, ty: Ty<'db>) -> Result<Ty<'db>, Self::Error> { + let infcx = self.at.infcx; + debug_assert_eq!(ty, infcx.shallow_resolve(ty)); + if !ty.has_aliases() { + return Ok(ty); + } + + let TyKind::Alias(..) = ty.kind() else { return ty.try_super_fold_with(self) }; + + if ty.has_escaping_bound_vars() { + let (ty, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty); + let result = self.normalize_alias_term(ty.into())?.expect_type(); + Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + result, + )) + } else { + Ok(self.normalize_alias_term(ty.into())?.expect_type()) + } + } + + #[tracing::instrument(level = "trace", skip(self), ret)] + fn try_fold_const(&mut self, ct: Const<'db>) -> Result<Const<'db>, Self::Error> { + let infcx = self.at.infcx; + debug_assert_eq!(ct, infcx.shallow_resolve_const(ct)); + if !ct.has_aliases() { + return Ok(ct); + } + + let ConstKind::Unevaluated(..) = ct.kind() else { return ct.try_super_fold_with(self) }; + + if ct.has_escaping_bound_vars() { + let (ct, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ct); + let result = self.normalize_alias_term(ct.into())?.expect_const(); + Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + result, + )) + } else { + Ok(self.normalize_alias_term(ct.into())?.expect_const()) + } + } +} + +// Deeply normalize a value and return it +pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable<DbInterner<'db>>>( + infcx: &InferCtxt<'db>, + param_env: ParamEnv<'db>, + t: T, +) -> T { + t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder { + at: infcx.at(&ObligationCause::dummy(), param_env), + }) +} + +struct DeeplyNormalizeForDiagnosticsFolder<'a, 'db> { + at: At<'a, 'db>, +} + +impl<'db> TypeFolder<DbInterner<'db>> for DeeplyNormalizeForDiagnosticsFolder<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.at.infcx.interner + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + let infcx = self.at.infcx; + let result: Result<_, Vec<NextSolverError<'db>>> = infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + self.at, + ty, + vec![None; ty.outer_exclusive_binder().as_usize()], + ) + }); + match result { + Ok((ty, _)) => ty, + Err(_) => ty.super_fold_with(self), + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + let infcx = self.at.infcx; + let result: Result<_, Vec<NextSolverError<'db>>> = infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + self.at, + ct, + vec![None; ct.outer_exclusive_binder().as_usize()], + ) + }); + match result { + Ok((ct, _)) => ct, + Err(_) => ct.super_fold_with(self), + } + } +}
diff --git a/crates/hir-ty/src/next_solver/region.rs b/crates/hir-ty/src/next_solver/region.rs new file mode 100644 index 0000000..c59cdac --- /dev/null +++ b/crates/hir-ty/src/next_solver/region.rs
@@ -0,0 +1,332 @@ +//! Things related to regions. + +use intern::{Interned, Symbol}; +use rustc_type_ir::{ + BoundVar, Flags, INNERMOST, RegionVid, TypeFlags, TypeFoldable, TypeVisitable, VisitorResult, + inherent::{IntoKind, PlaceholderLike, SliceLike}, + relate::Relate, +}; + +use crate::next_solver::{GenericArg, OutlivesPredicate}; + +use super::{ + ErrorGuaranteed, SolverDefId, interned_vec_db, + interner::{BoundVarKind, DbInterner, Placeholder}, +}; + +type RegionKind<'db> = rustc_type_ir::RegionKind<DbInterner<'db>>; + +#[salsa::interned(constructor = new_, debug)] +pub struct Region<'db> { + #[returns(ref)] + kind_: RegionKind<'db>, +} + +impl<'db> Region<'db> { + pub fn new(interner: DbInterner<'db>, kind: RegionKind<'db>) -> Self { + Region::new_(interner.db(), kind) + } + + pub fn inner(&self) -> &RegionKind<'db> { + salsa::with_attached_database(|db| { + let inner = self.kind_(db); + // SAFETY: The caller already has access to a `Region<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute::<&RegionKind<'_>, &RegionKind<'db>>(inner) } + }) + .unwrap() + } + + pub fn new_early_param( + interner: DbInterner<'db>, + early_bound_region: EarlyParamRegion, + ) -> Self { + Region::new(interner, RegionKind::ReEarlyParam(early_bound_region)) + } + + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderRegion) -> Self { + Region::new(interner, RegionKind::RePlaceholder(placeholder)) + } + + pub fn new_var(interner: DbInterner<'db>, v: RegionVid) -> Region<'db> { + Region::new(interner, RegionKind::ReVar(v)) + } + + pub fn is_placeholder(&self) -> bool { + matches!(self.inner(), RegionKind::RePlaceholder(..)) + } + + pub fn is_static(&self) -> bool { + matches!(self.inner(), RegionKind::ReStatic) + } + + pub fn error(interner: DbInterner<'db>) -> Self { + Region::new(interner, RegionKind::ReError(ErrorGuaranteed)) + } + + pub fn type_flags(&self) -> TypeFlags { + let mut flags = TypeFlags::empty(); + + match &self.inner() { + RegionKind::ReVar(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + flags |= TypeFlags::HAS_RE_INFER; + } + RegionKind::RePlaceholder(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + flags |= TypeFlags::HAS_RE_PLACEHOLDER; + } + RegionKind::ReEarlyParam(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + flags |= TypeFlags::HAS_RE_PARAM; + } + RegionKind::ReLateParam(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + } + RegionKind::ReStatic => { + flags |= TypeFlags::HAS_FREE_REGIONS; + } + RegionKind::ReBound(..) => { + flags |= TypeFlags::HAS_RE_BOUND; + } + RegionKind::ReErased => { + flags |= TypeFlags::HAS_RE_ERASED; + } + RegionKind::ReError(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_ERROR; + } + } + + flags + } +} + +pub type PlaceholderRegion = Placeholder<BoundRegion>; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct EarlyParamRegion { + pub index: u32, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +/// The parameter representation of late-bound function parameters, "some region +/// at least as big as the scope `fr.scope`". +/// +/// Similar to a placeholder region as we create `LateParam` regions when entering a binder +/// except they are always in the root universe and instead of using a boundvar to distinguish +/// between others we use the `DefId` of the parameter. For this reason the `bound_region` field +/// should basically always be `BoundRegionKind::Named` as otherwise there is no way of telling +/// different parameters apart. +pub struct LateParamRegion { + pub scope: SolverDefId, + pub bound_region: BoundRegionKind, +} + +impl std::fmt::Debug for LateParamRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ReLateParam({:?}, {:?})", self.scope, self.bound_region) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum BoundRegionKind { + /// An anonymous region parameter for a given fn (&T) + Anon, + + /// Named region parameters for functions (a in &'a T) + /// + /// The `DefId` is needed to distinguish free regions in + /// the event of shadowing. + Named(SolverDefId), + + /// Anonymous region for the implicit env pointer parameter + /// to a closure + ClosureEnv, +} + +impl std::fmt::Debug for BoundRegionKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + BoundRegionKind::Anon => write!(f, "BrAnon"), + BoundRegionKind::Named(did) => { + write!(f, "BrNamed({did:?})") + } + BoundRegionKind::ClosureEnv => write!(f, "BrEnv"), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct BoundRegion { + pub var: BoundVar, + pub kind: BoundRegionKind, +} + +impl rustc_type_ir::inherent::ParamLike for EarlyParamRegion { + fn index(self) -> u32 { + self.index + } +} + +impl std::fmt::Debug for EarlyParamRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.index) + // write!(f, "{}/#{}", self.name, self.index) + } +} + +impl<'db> rustc_type_ir::inherent::BoundVarLike<DbInterner<'db>> for BoundRegion { + fn var(self) -> BoundVar { + self.var + } + + fn assert_eq(self, var: BoundVarKind) { + assert_eq!(self.kind, var.expect_region()) + } +} + +impl core::fmt::Debug for BoundRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + BoundRegionKind::Anon => write!(f, "{:?}", self.var), + BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var), + BoundRegionKind::Named(def) => { + write!(f, "{:?}.Named({:?})", self.var, def) + } + } + } +} + +impl BoundRegionKind { + pub fn is_named(&self) -> bool { + matches!(self, BoundRegionKind::Named(_)) + } + + pub fn get_name(&self) -> Option<Symbol> { + None + } + + pub fn get_id(&self) -> Option<SolverDefId> { + match self { + BoundRegionKind::Named(id) => Some(*id), + _ => None, + } + } +} + +impl<'db> IntoKind for Region<'db> { + type Kind = RegionKind<'db>; + + fn kind(self) -> Self::Kind { + *self.inner() + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for Region<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_region(*self) + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for Region<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + folder.try_fold_region(self) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + folder.fold_region(self) + } +} + +impl<'db> Relate<DbInterner<'db>> for Region<'db> { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + relation.regions(a, b) + } +} + +impl<'db> Flags for Region<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.type_flags() + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + match &self.inner() { + RegionKind::ReBound(debruijn, _) => debruijn.shifted_in(1), + _ => INNERMOST, + } + } +} + +impl<'db> rustc_type_ir::inherent::Region<DbInterner<'db>> for Region<'db> { + fn new_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundRegion, + ) -> Self { + Region::new(interner, RegionKind::ReBound(debruijn, var)) + } + + fn new_anon_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: rustc_type_ir::BoundVar, + ) -> Self { + Region::new( + interner, + RegionKind::ReBound(debruijn, BoundRegion { var, kind: BoundRegionKind::Anon }), + ) + } + + fn new_static(interner: DbInterner<'db>) -> Self { + Region::new(interner, RegionKind::ReStatic) + } + + fn new_placeholder( + interner: DbInterner<'db>, + var: <DbInterner<'db> as rustc_type_ir::Interner>::PlaceholderRegion, + ) -> Self { + Region::new(interner, RegionKind::RePlaceholder(var)) + } +} + +impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderRegion { + type Bound = BoundRegion; + + fn universe(self) -> rustc_type_ir::UniverseIndex { + self.universe + } + + fn var(self) -> rustc_type_ir::BoundVar { + self.bound.var + } + + fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { + Placeholder { universe: ui, bound: self.bound } + } + + fn new(ui: rustc_type_ir::UniverseIndex, bound: Self::Bound) -> Self { + Placeholder { universe: ui, bound } + } + + fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: BoundRegion { var, kind: BoundRegionKind::Anon } } + } +} + +type GenericArgOutlivesPredicate<'db> = OutlivesPredicate<'db, GenericArg<'db>>; + +interned_vec_db!(RegionAssumptions, GenericArgOutlivesPredicate);
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs new file mode 100644 index 0000000..45888ac --- /dev/null +++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -0,0 +1,289 @@ +//! Defining `SolverContext` for next-trait-solver. + +use hir_def::{AssocItemId, GeneralConstId, TypeAliasId}; +use rustc_next_trait_solver::delegate::SolverDelegate; +use rustc_type_ir::{ + InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, UniverseIndex, + inherent::{IntoKind, SliceLike, Span as _, Term as _, Ty as _}, + lang_items::TraitSolverLangItem, + solve::{Certainty, NoSolution}, +}; + +use crate::{ + TraitRefExt, + db::HirDatabase, + next_solver::{ + ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, + mapping::{ChalkToNextSolver, convert_args_for_result}, + util::sizedness_fast_path, + }, +}; + +use super::{ + Canonical, CanonicalVarValues, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, + ParamEnv, Predicate, SolverDefId, Span, Ty, UnevaluatedConst, + infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt}, +}; + +pub type Goal<'db, P> = rustc_type_ir::solve::Goal<DbInterner<'db>, P>; + +#[repr(transparent)] +pub(crate) struct SolverContext<'db>(pub(crate) InferCtxt<'db>); + +impl<'a, 'db> From<&'a InferCtxt<'db>> for &'a SolverContext<'db> { + fn from(infcx: &'a InferCtxt<'db>) -> Self { + // SAFETY: `repr(transparent)` + unsafe { std::mem::transmute(infcx) } + } +} + +impl<'db> std::ops::Deref for SolverContext<'db> { + type Target = InferCtxt<'db>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'db> SolverDelegate for SolverContext<'db> { + type Interner = DbInterner<'db>; + type Infcx = InferCtxt<'db>; + + fn cx(&self) -> Self::Interner { + self.0.interner + } + + fn build_with_canonical<V>( + cx: Self::Interner, + canonical: &rustc_type_ir::CanonicalQueryInput<Self::Interner, V>, + ) -> (Self, V, rustc_type_ir::CanonicalVarValues<Self::Interner>) + where + V: rustc_type_ir::TypeFoldable<Self::Interner>, + { + let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(canonical); + (SolverContext(infcx), value, vars) + } + + fn fresh_var_for_kind_with_span( + &self, + arg: <Self::Interner as rustc_type_ir::Interner>::GenericArg, + span: <Self::Interner as rustc_type_ir::Interner>::Span, + ) -> <Self::Interner as rustc_type_ir::Interner>::GenericArg { + unimplemented!() + } + + fn leak_check( + &self, + max_input_universe: rustc_type_ir::UniverseIndex, + ) -> Result<(), NoSolution> { + Ok(()) + } + + fn well_formed_goals( + &self, + param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv, + arg: <Self::Interner as rustc_type_ir::Interner>::Term, + ) -> Option< + Vec< + rustc_type_ir::solve::Goal< + Self::Interner, + <Self::Interner as rustc_type_ir::Interner>::Predicate, + >, + >, + > { + unimplemented!() + } + + fn make_deduplicated_outlives_constraints( + &self, + ) -> Vec< + rustc_type_ir::OutlivesPredicate< + Self::Interner, + <Self::Interner as rustc_type_ir::Interner>::GenericArg, + >, + > { + // FIXME: add if we care about regions + vec![] + } + + fn instantiate_canonical<V>( + &self, + canonical: rustc_type_ir::Canonical<Self::Interner, V>, + values: rustc_type_ir::CanonicalVarValues<Self::Interner>, + ) -> V + where + V: rustc_type_ir::TypeFoldable<Self::Interner>, + { + canonical.instantiate(self.cx(), &values) + } + + fn instantiate_canonical_var_with_infer( + &self, + cv_info: rustc_type_ir::CanonicalVarKind<Self::Interner>, + _span: <Self::Interner as rustc_type_ir::Interner>::Span, + universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex, + ) -> <Self::Interner as rustc_type_ir::Interner>::GenericArg { + self.0.instantiate_canonical_var(cv_info, universe_map) + } + + fn add_item_bounds_for_hidden_type( + &self, + def_id: <Self::Interner as rustc_type_ir::Interner>::DefId, + args: <Self::Interner as rustc_type_ir::Interner>::GenericArgs, + param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv, + hidden_ty: <Self::Interner as rustc_type_ir::Interner>::Ty, + goals: &mut Vec< + rustc_type_ir::solve::Goal< + Self::Interner, + <Self::Interner as rustc_type_ir::Interner>::Predicate, + >, + >, + ) { + unimplemented!() + } + + fn fetch_eligible_assoc_item( + &self, + goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>, + trait_assoc_def_id: <Self::Interner as rustc_type_ir::Interner>::DefId, + impl_def_id: <Self::Interner as rustc_type_ir::Interner>::DefId, + ) -> Result<Option<<Self::Interner as rustc_type_ir::Interner>::DefId>, ErrorGuaranteed> { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => panic!("Unexpected SolverDefId"), + }; + let trait_assoc_id = match trait_assoc_def_id { + SolverDefId::TypeAliasId(id) => id, + _ => panic!("Unexpected SolverDefId"), + }; + let trait_ref = self + .0 + .interner + .db() + .impl_trait(impl_id) + // ImplIds for impls where the trait ref can't be resolved should never reach solver + .expect("invalid impl passed to next-solver") + .into_value_and_skipped_binders() + .0; + let trait_ = trait_ref.hir_trait_id(); + let trait_data = trait_.trait_items(self.0.interner.db()); + let id = + impl_id.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> { + match item { + (_, AssocItemId::TypeAliasId(type_alias)) => { + let name = &self.0.interner.db().type_alias_signature(*type_alias).name; + let found_trait_assoc_id = trait_data.associated_type_by_name(name)?; + (found_trait_assoc_id == trait_assoc_id).then_some(*type_alias) + } + _ => None, + } + }); + Ok(id.map(SolverDefId::TypeAliasId)) + } + + fn is_transmutable( + &self, + dst: <Self::Interner as rustc_type_ir::Interner>::Ty, + src: <Self::Interner as rustc_type_ir::Interner>::Ty, + assume: <Self::Interner as rustc_type_ir::Interner>::Const, + ) -> Result<Certainty, NoSolution> { + unimplemented!() + } + + fn evaluate_const( + &self, + param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv, + uv: rustc_type_ir::UnevaluatedConst<Self::Interner>, + ) -> Option<<Self::Interner as rustc_type_ir::Interner>::Const> { + let c = match uv.def { + SolverDefId::ConstId(c) => GeneralConstId::ConstId(c), + SolverDefId::StaticId(c) => GeneralConstId::StaticId(c), + _ => unreachable!(), + }; + let subst = convert_args_for_result(self.interner, uv.args.as_slice()); + let ec = self.cx().db.const_eval(c, subst, None).ok()?; + Some(ec.to_nextsolver(self.interner)) + } + + fn compute_goal_fast_path( + &self, + goal: rustc_type_ir::solve::Goal< + Self::Interner, + <Self::Interner as rustc_type_ir::Interner>::Predicate, + >, + span: <Self::Interner as rustc_type_ir::Interner>::Span, + ) -> Option<Certainty> { + if let Some(trait_pred) = goal.predicate.as_trait_clause() { + if self.shallow_resolve(trait_pred.self_ty().skip_binder()).is_ty_var() + // We don't do this fast path when opaques are defined since we may + // eventually use opaques to incompletely guide inference via ty var + // self types. + // FIXME: Properly consider opaques here. + && self.inner.borrow_mut().opaque_types().is_empty() + { + return Some(Certainty::AMBIGUOUS); + } + + if trait_pred.polarity() == PredicatePolarity::Positive { + match self.0.cx().as_lang_item(trait_pred.def_id()) { + Some(TraitSolverLangItem::Sized) | Some(TraitSolverLangItem::MetaSized) => { + let predicate = self.resolve_vars_if_possible(goal.predicate); + if sizedness_fast_path(self.cx(), predicate, goal.param_env) { + return Some(Certainty::Yes); + } + } + Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => { + let self_ty = + self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder()); + // Unlike `Sized` traits, which always prefer the built-in impl, + // `Copy`/`Clone` may be shadowed by a param-env candidate which + // could force a lifetime error or guide inference. While that's + // not generally desirable, it is observable, so for now let's + // ignore this fast path for types that have regions or infer. + if !self_ty + .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER) + && self_ty.is_trivially_pure_clone_copy() + { + return Some(Certainty::Yes); + } + } + _ => {} + } + } + } + + 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::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 + } + } + PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, _)) => { + if self.shallow_resolve_const(ct).is_ct_infer() { + Some(Certainty::AMBIGUOUS) + } else { + None + } + } + PredicateKind::Clause(ClauseKind::WellFormed(arg)) => { + if arg.is_trivially_wf(self.interner) { + Some(Certainty::Yes) + } else if arg.is_infer() { + Some(Certainty::AMBIGUOUS) + } else { + None + } + } + _ => None, + } + } +}
diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs new file mode 100644 index 0000000..0c0fe68 --- /dev/null +++ b/crates/hir-ty/src/next_solver/ty.rs
@@ -0,0 +1,943 @@ +//! Things related to tys in the next-trait-solver. + +use intern::{Interned, Symbol, sym}; +use rustc_abi::{Float, Integer, Size}; +use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; +use rustc_type_ir::{ + BoundVar, ClosureKind, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid, + TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, UintTy, + WithCachedTypeInfo, + inherent::{ + AdtDef, BoundVarLike, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, SliceLike, + }, + relate::Relate, + solve::SizedTraitKind, + walk::TypeWalker, +}; +use salsa::plumbing::{AsId, FromId}; +use smallvec::SmallVec; + +use crate::{ + db::HirDatabase, + interner::InternedWrapperNoDebug, + next_solver::util::{CoroutineArgsExt, IntegerTypeExt}, +}; + +use super::{ + BoundVarKind, DbInterner, GenericArgs, Placeholder, SolverDefId, interned_vec_db, + util::{FloatExt, IntegerExt}, +}; + +pub type TyKind<'db> = rustc_type_ir::TyKind<DbInterner<'db>>; +pub type FnHeader<'db> = rustc_type_ir::FnHeader<DbInterner<'db>>; + +#[salsa::interned(constructor = new_)] +pub struct Ty<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug<WithCachedTypeInfo<TyKind<'db>>>, +} + +const _: () = { + const fn is_copy<T: Copy>() {} + is_copy::<Ty<'static>>(); +}; + +impl<'db> Ty<'db> { + pub fn new(interner: DbInterner<'db>, kind: TyKind<'db>) -> Self { + let flags = FlagComputation::for_kind(&kind); + let cached = WithCachedTypeInfo { + internee: kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + Ty::new_(interner.db(), InternedWrapperNoDebug(cached)) + } + + pub fn inner(&self) -> &WithCachedTypeInfo<TyKind<'db>> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Ty<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + pub fn new_param(interner: DbInterner<'db>, index: u32, name: Symbol) -> Self { + Ty::new(interner, TyKind::Param(ParamTy { index })) + } + + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderTy) -> Self { + Ty::new(interner, TyKind::Placeholder(placeholder)) + } + + pub fn new_infer(interner: DbInterner<'db>, infer: InferTy) -> Self { + Ty::new(interner, TyKind::Infer(infer)) + } + + pub fn new_int_var(interner: DbInterner<'db>, v: IntVid) -> Self { + Ty::new_infer(interner, InferTy::IntVar(v)) + } + + pub fn new_float_var(interner: DbInterner<'db>, v: FloatVid) -> Self { + Ty::new_infer(interner, InferTy::FloatVar(v)) + } + + pub fn new_int(interner: DbInterner<'db>, i: IntTy) -> Self { + Ty::new(interner, TyKind::Int(i)) + } + + pub fn new_uint(interner: DbInterner<'db>, ui: UintTy) -> Self { + Ty::new(interner, TyKind::Uint(ui)) + } + + pub fn new_float(interner: DbInterner<'db>, f: FloatTy) -> Self { + Ty::new(interner, TyKind::Float(f)) + } + + pub fn new_fresh(interner: DbInterner<'db>, n: u32) -> Self { + Ty::new_infer(interner, InferTy::FreshTy(n)) + } + + pub fn new_fresh_int(interner: DbInterner<'db>, n: u32) -> Self { + Ty::new_infer(interner, InferTy::FreshIntTy(n)) + } + + pub fn new_fresh_float(interner: DbInterner<'db>, n: u32) -> Self { + Ty::new_infer(interner, InferTy::FreshFloatTy(n)) + } + + /// Returns the `Size` for primitive types (bool, uint, int, char, float). + pub fn primitive_size(self, interner: DbInterner<'db>) -> Size { + match self.kind() { + TyKind::Bool => Size::from_bytes(1), + TyKind::Char => Size::from_bytes(4), + TyKind::Int(ity) => Integer::from_int_ty(&interner, ity).size(), + TyKind::Uint(uty) => Integer::from_uint_ty(&interner, uty).size(), + TyKind::Float(fty) => Float::from_float_ty(fty).size(), + _ => panic!("non primitive type"), + } + } + + pub fn int_size_and_signed(self, interner: DbInterner<'db>) -> (Size, bool) { + match self.kind() { + TyKind::Int(ity) => (Integer::from_int_ty(&interner, ity).size(), true), + TyKind::Uint(uty) => (Integer::from_uint_ty(&interner, uty).size(), false), + _ => panic!("non integer discriminant"), + } + } + + pub fn walk(self) -> TypeWalker<DbInterner<'db>> { + TypeWalker::new(self.into()) + } + + /// Fast path helper for testing if a type is `Sized` or `MetaSized`. + /// + /// Returning true means the type is known to implement the sizedness trait. Returning `false` + /// means nothing -- could be sized, might not be. + /// + /// Note that we could never rely on the fact that a type such as `[_]` is trivially `!Sized` + /// because we could be in a type environment with a bound such as `[_]: Copy`. A function with + /// such a bound obviously never can be called, but that doesn't mean it shouldn't typecheck. + /// This is why this method doesn't return `Option<bool>`. + #[tracing::instrument(skip(tcx), level = "debug")] + pub fn has_trivial_sizedness(self, tcx: DbInterner<'db>, sizedness: SizedTraitKind) -> bool { + match self.kind() { + TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) + | TyKind::Uint(_) + | TyKind::Int(_) + | TyKind::Bool + | TyKind::Float(_) + | TyKind::FnDef(..) + | TyKind::FnPtr(..) + | TyKind::UnsafeBinder(_) + | TyKind::RawPtr(..) + | TyKind::Char + | TyKind::Ref(..) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Array(..) + | TyKind::Pat(..) + | TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::Never + | TyKind::Error(_) => true, + + TyKind::Str | TyKind::Slice(_) | TyKind::Dynamic(_, _, _) => match sizedness { + SizedTraitKind::Sized => false, + SizedTraitKind::MetaSized => true, + }, + + TyKind::Foreign(..) => match sizedness { + SizedTraitKind::Sized | SizedTraitKind::MetaSized => false, + }, + + TyKind::Tuple(tys) => { + 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::Alias(..) | TyKind::Param(_) | TyKind::Placeholder(..) | TyKind::Bound(..) => { + false + } + + TyKind::Infer(InferTy::TyVar(_)) => false, + + TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { + panic!("`has_trivial_sizedness` applied to unexpected type: {self:?}") + } + } + } + + /// Fast path helper for primitives which are always `Copy` and which + /// have a side-effect-free `Clone` impl. + /// + /// Returning true means the type is known to be pure and `Copy+Clone`. + /// Returning `false` means nothing -- could be `Copy`, might not be. + /// + /// This is mostly useful for optimizations, as these are the types + /// on which we can replace cloning with dereferencing. + pub fn is_trivially_pure_clone_copy(self) -> bool { + match self.kind() { + TyKind::Bool | TyKind::Char | TyKind::Never => true, + + // These aren't even `Clone` + TyKind::Str | TyKind::Slice(..) | TyKind::Foreign(..) | TyKind::Dynamic(..) => false, + + TyKind::Infer(InferTy::FloatVar(_) | InferTy::IntVar(_)) + | TyKind::Int(..) + | TyKind::Uint(..) + | TyKind::Float(..) => true, + + // ZST which can't be named are fine. + TyKind::FnDef(..) => true, + + TyKind::Array(element_ty, _len) => element_ty.is_trivially_pure_clone_copy(), + + // A 100-tuple isn't "trivial", so doing this only for reasonable sizes. + TyKind::Tuple(field_tys) => { + field_tys.len() <= 3 && field_tys.iter().all(Self::is_trivially_pure_clone_copy) + } + + TyKind::Pat(ty, _) => ty.is_trivially_pure_clone_copy(), + + // Sometimes traits aren't implemented for every ABI or arity, + // because we can't be generic over everything yet. + TyKind::FnPtr(..) => false, + + // Definitely absolutely not copy. + TyKind::Ref(_, _, Mutability::Mut) => false, + + // The standard library has a blanket Copy impl for shared references and raw pointers, + // for all unsized types. + TyKind::Ref(_, _, Mutability::Not) | TyKind::RawPtr(..) => true, + + TyKind::Coroutine(..) | TyKind::CoroutineWitness(..) => false, + + // Might be, but not "trivial" so just giving the safe answer. + TyKind::Adt(..) | TyKind::Closure(..) | TyKind::CoroutineClosure(..) => false, + + TyKind::UnsafeBinder(_) => false, + + // Needs normalization or revealing to determine, so no is the safe answer. + TyKind::Alias(..) => false, + + TyKind::Param(..) + | TyKind::Placeholder(..) + | TyKind::Bound(..) + | TyKind::Infer(..) + | TyKind::Error(..) => false, + } + } + + pub fn is_trivially_wf(self, tcx: DbInterner<'db>) -> bool { + match self.kind() { + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Str + | TyKind::Never + | TyKind::Param(_) + | TyKind::Placeholder(_) + | TyKind::Bound(..) => true, + + TyKind::Slice(ty) => { + ty.is_trivially_wf(tcx) && ty.has_trivial_sizedness(tcx, SizedTraitKind::Sized) + } + TyKind::RawPtr(ty, _) => ty.is_trivially_wf(tcx), + + TyKind::FnPtr(sig_tys, _) => { + sig_tys.skip_binder().inputs_and_output.iter().all(|ty| ty.is_trivially_wf(tcx)) + } + TyKind::Ref(_, ty, _) => ty.is_global() && ty.is_trivially_wf(tcx), + + TyKind::Infer(infer) => match infer { + InferTy::TyVar(_) => false, + InferTy::IntVar(_) | InferTy::FloatVar(_) => true, + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => true, + }, + + TyKind::Adt(_, _) + | TyKind::Tuple(_) + | TyKind::Array(..) + | TyKind::Foreign(_) + | TyKind::Pat(_, _) + | TyKind::FnDef(..) + | TyKind::UnsafeBinder(..) + | TyKind::Dynamic(..) + | TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Alias(..) + | TyKind::Error(_) => false, + } + } +} + +impl<'db> std::fmt::Debug for Ty<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().internee.fmt(f) + } +} + +impl<'db> std::fmt::Debug for InternedWrapperNoDebug<WithCachedTypeInfo<TyKind<'db>>> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.internee.fmt(f) + } +} + +impl<'db> IntoKind for Ty<'db> { + type Kind = TyKind<'db>; + + fn kind(self) -> Self::Kind { + self.inner().internee + } +} + +impl<'db> TypeVisitable<DbInterner<'db>> for Ty<'db> { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_ty(*self) + } +} + +impl<'db> TypeSuperVisitable<DbInterner<'db>> for Ty<'db> { + fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + match (*self).kind() { + TyKind::RawPtr(ty, _mutbl) => ty.visit_with(visitor), + TyKind::Array(typ, sz) => { + try_visit!(typ.visit_with(visitor)); + sz.visit_with(visitor) + } + TyKind::Slice(typ) => typ.visit_with(visitor), + TyKind::Adt(_, args) => args.visit_with(visitor), + TyKind::Dynamic(ref trait_ty, ref reg, _) => { + try_visit!(trait_ty.visit_with(visitor)); + reg.visit_with(visitor) + } + TyKind::Tuple(ts) => ts.visit_with(visitor), + TyKind::FnDef(_, args) => args.visit_with(visitor), + TyKind::FnPtr(ref sig_tys, _) => sig_tys.visit_with(visitor), + TyKind::UnsafeBinder(f) => f.visit_with(visitor), + TyKind::Ref(r, ty, _) => { + try_visit!(r.visit_with(visitor)); + ty.visit_with(visitor) + } + TyKind::Coroutine(_did, ref args) => args.visit_with(visitor), + TyKind::CoroutineWitness(_did, ref args) => args.visit_with(visitor), + TyKind::Closure(_did, ref args) => args.visit_with(visitor), + TyKind::CoroutineClosure(_did, ref args) => args.visit_with(visitor), + TyKind::Alias(_, ref data) => data.visit_with(visitor), + + TyKind::Pat(ty, pat) => { + try_visit!(ty.visit_with(visitor)); + pat.visit_with(visitor) + } + + TyKind::Error(guar) => guar.visit_with(visitor), + + TyKind::Bool + | TyKind::Char + | TyKind::Str + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(_) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Param(..) + | TyKind::Never + | TyKind::Foreign(..) => V::Result::output(), + } + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for Ty<'db> { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + folder.try_fold_ty(self) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + folder.fold_ty(self) + } +} + +impl<'db> TypeSuperFoldable<DbInterner<'db>> for Ty<'db> { + fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + let kind = match self.kind() { + TyKind::RawPtr(ty, mutbl) => TyKind::RawPtr(ty.try_fold_with(folder)?, mutbl), + TyKind::Array(typ, sz) => { + TyKind::Array(typ.try_fold_with(folder)?, sz.try_fold_with(folder)?) + } + TyKind::Slice(typ) => TyKind::Slice(typ.try_fold_with(folder)?), + TyKind::Adt(tid, args) => TyKind::Adt(tid, args.try_fold_with(folder)?), + TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic( + trait_ty.try_fold_with(folder)?, + region.try_fold_with(folder)?, + representation, + ), + TyKind::Tuple(ts) => TyKind::Tuple(ts.try_fold_with(folder)?), + TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.try_fold_with(folder)?), + TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.try_fold_with(folder)?, hdr), + TyKind::UnsafeBinder(f) => TyKind::UnsafeBinder(f.try_fold_with(folder)?), + TyKind::Ref(r, ty, mutbl) => { + TyKind::Ref(r.try_fold_with(folder)?, ty.try_fold_with(folder)?, mutbl) + } + TyKind::Coroutine(did, args) => TyKind::Coroutine(did, args.try_fold_with(folder)?), + TyKind::CoroutineWitness(did, args) => { + TyKind::CoroutineWitness(did, args.try_fold_with(folder)?) + } + TyKind::Closure(did, args) => TyKind::Closure(did, args.try_fold_with(folder)?), + TyKind::CoroutineClosure(did, args) => { + TyKind::CoroutineClosure(did, args.try_fold_with(folder)?) + } + TyKind::Alias(kind, data) => TyKind::Alias(kind, data.try_fold_with(folder)?), + TyKind::Pat(ty, pat) => { + TyKind::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?) + } + + TyKind::Bool + | TyKind::Char + | TyKind::Str + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Error(_) + | TyKind::Infer(_) + | TyKind::Param(..) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Never + | TyKind::Foreign(..) => return Ok(self), + }; + + Ok(if self.kind() == kind { self } else { Ty::new(folder.cx(), kind) }) + } + fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Self { + let kind = match self.kind() { + TyKind::RawPtr(ty, mutbl) => TyKind::RawPtr(ty.fold_with(folder), mutbl), + TyKind::Array(typ, sz) => TyKind::Array(typ.fold_with(folder), sz.fold_with(folder)), + TyKind::Slice(typ) => TyKind::Slice(typ.fold_with(folder)), + TyKind::Adt(tid, args) => TyKind::Adt(tid, args.fold_with(folder)), + TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic( + trait_ty.fold_with(folder), + region.fold_with(folder), + representation, + ), + TyKind::Tuple(ts) => TyKind::Tuple(ts.fold_with(folder)), + TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.fold_with(folder)), + TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.fold_with(folder), hdr), + TyKind::UnsafeBinder(f) => TyKind::UnsafeBinder(f.fold_with(folder)), + TyKind::Ref(r, ty, mutbl) => { + TyKind::Ref(r.fold_with(folder), ty.fold_with(folder), mutbl) + } + TyKind::Coroutine(did, args) => TyKind::Coroutine(did, args.fold_with(folder)), + TyKind::CoroutineWitness(did, args) => { + TyKind::CoroutineWitness(did, args.fold_with(folder)) + } + TyKind::Closure(did, args) => TyKind::Closure(did, args.fold_with(folder)), + TyKind::CoroutineClosure(did, args) => { + TyKind::CoroutineClosure(did, args.fold_with(folder)) + } + TyKind::Alias(kind, data) => TyKind::Alias(kind, data.fold_with(folder)), + TyKind::Pat(ty, pat) => TyKind::Pat(ty.fold_with(folder), pat.fold_with(folder)), + + TyKind::Bool + | TyKind::Char + | TyKind::Str + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Error(_) + | TyKind::Infer(_) + | TyKind::Param(..) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Never + | TyKind::Foreign(..) => return self, + }; + + if self.kind() == kind { self } else { Ty::new(folder.cx(), kind) } + } +} + +impl<'db> Relate<DbInterner<'db>> for Ty<'db> { + fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> { + relation.tys(a, b) + } +} + +impl<'db> Flags for Ty<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().outer_exclusive_binder + } +} + +impl<'db> rustc_type_ir::inherent::Ty<DbInterner<'db>> for Ty<'db> { + fn new_unit(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Tuple(Default::default())) + } + + fn new_bool(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Bool) + } + + fn new_u8(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Uint(rustc_type_ir::UintTy::U8)) + } + + fn new_usize(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Uint(rustc_type_ir::UintTy::Usize)) + } + + fn new_infer(interner: DbInterner<'db>, var: rustc_type_ir::InferTy) -> Self { + Ty::new(interner, TyKind::Infer(var)) + } + + fn new_var(interner: DbInterner<'db>, var: rustc_type_ir::TyVid) -> Self { + Ty::new(interner, TyKind::Infer(rustc_type_ir::InferTy::TyVar(var))) + } + + fn new_param(interner: DbInterner<'db>, param: ParamTy) -> Self { + Ty::new(interner, TyKind::Param(param)) + } + + fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderTy) -> Self { + Ty::new(interner, TyKind::Placeholder(param)) + } + + fn new_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundTy, + ) -> Self { + Ty::new(interner, TyKind::Bound(debruijn, var)) + } + + fn new_anon_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundVar, + ) -> Self { + Ty::new(interner, TyKind::Bound(debruijn, BoundTy { var, kind: BoundTyKind::Anon })) + } + + fn new_alias( + interner: DbInterner<'db>, + kind: rustc_type_ir::AliasTyKind, + alias_ty: rustc_type_ir::AliasTy<DbInterner<'db>>, + ) -> Self { + Ty::new(interner, TyKind::Alias(kind, alias_ty)) + } + + fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self { + Ty::new(interner, TyKind::Error(guar)) + } + + fn new_adt( + interner: DbInterner<'db>, + adt_def: <DbInterner<'db> as rustc_type_ir::Interner>::AdtDef, + args: GenericArgs<'db>, + ) -> Self { + Ty::new(interner, TyKind::Adt(adt_def, args)) + } + + fn new_foreign( + interner: DbInterner<'db>, + def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId, + ) -> Self { + Ty::new(interner, TyKind::Foreign(def_id)) + } + + fn new_dynamic( + interner: DbInterner<'db>, + preds: <DbInterner<'db> as rustc_type_ir::Interner>::BoundExistentialPredicates, + region: <DbInterner<'db> as rustc_type_ir::Interner>::Region, + kind: rustc_type_ir::DynKind, + ) -> Self { + Ty::new(interner, TyKind::Dynamic(preds, region, kind)) + } + + fn new_coroutine( + interner: DbInterner<'db>, + def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId, + args: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::Coroutine(def_id, args)) + } + + fn new_coroutine_closure( + interner: DbInterner<'db>, + def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId, + args: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::CoroutineClosure(def_id, args)) + } + + fn new_closure( + interner: DbInterner<'db>, + def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId, + args: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::Closure(def_id, args)) + } + + fn new_coroutine_witness( + interner: DbInterner<'db>, + def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId, + args: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::CoroutineWitness(def_id, args)) + } + + fn new_ptr(interner: DbInterner<'db>, ty: Self, mutbl: rustc_ast_ir::Mutability) -> Self { + Ty::new(interner, TyKind::RawPtr(ty, mutbl)) + } + + fn new_ref( + interner: DbInterner<'db>, + region: <DbInterner<'db> as rustc_type_ir::Interner>::Region, + ty: Self, + mutbl: rustc_ast_ir::Mutability, + ) -> Self { + Ty::new(interner, TyKind::Ref(region, ty, mutbl)) + } + + fn new_array_with_const_len( + interner: DbInterner<'db>, + ty: Self, + len: <DbInterner<'db> as rustc_type_ir::Interner>::Const, + ) -> Self { + Ty::new(interner, TyKind::Array(ty, len)) + } + + fn new_slice(interner: DbInterner<'db>, ty: Self) -> Self { + Ty::new(interner, TyKind::Slice(ty)) + } + + fn new_tup( + interner: DbInterner<'db>, + tys: &[<DbInterner<'db> as rustc_type_ir::Interner>::Ty], + ) -> Self { + Ty::new(interner, TyKind::Tuple(Tys::new_from_iter(interner, tys.iter().cloned()))) + } + + fn new_tup_from_iter<It, T>(interner: DbInterner<'db>, iter: It) -> T::Output + where + It: Iterator<Item = T>, + T: rustc_type_ir::CollectAndApply<Self, Self>, + { + T::collect_and_apply(iter, |ts| Ty::new_tup(interner, ts)) + } + + fn new_fn_def( + interner: DbInterner<'db>, + def_id: <DbInterner<'db> as rustc_type_ir::Interner>::DefId, + args: <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::FnDef(def_id, args)) + } + + fn new_fn_ptr( + interner: DbInterner<'db>, + sig: rustc_type_ir::Binder<DbInterner<'db>, rustc_type_ir::FnSig<DbInterner<'db>>>, + ) -> Self { + let (sig_tys, header) = sig.split(); + Ty::new(interner, TyKind::FnPtr(sig_tys, header)) + } + + fn new_pat( + interner: DbInterner<'db>, + ty: Self, + pat: <DbInterner<'db> as rustc_type_ir::Interner>::Pat, + ) -> Self { + Ty::new(interner, TyKind::Pat(ty, pat)) + } + + fn tuple_fields(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::Tys { + match self.kind() { + TyKind::Tuple(args) => args, + _ => panic!("tuple_fields called on non-tuple: {self:?}"), + } + } + + fn to_opt_closure_kind(self) -> Option<rustc_type_ir::ClosureKind> { + match self.kind() { + TyKind::Int(int_ty) => match int_ty { + IntTy::I8 => Some(ClosureKind::Fn), + IntTy::I16 => Some(ClosureKind::FnMut), + IntTy::I32 => Some(ClosureKind::FnOnce), + _ => unreachable!("cannot convert type `{:?}` to a closure kind", self), + }, + + // "Bound" types appear in canonical queries when the + // closure type is not yet known, and `Placeholder` and `Param` + // may be encountered in generic `AsyncFnKindHelper` goals. + TyKind::Bound(..) | TyKind::Placeholder(_) | TyKind::Param(_) | TyKind::Infer(_) => { + None + } + + TyKind::Error(_) => Some(ClosureKind::Fn), + + _ => unreachable!("cannot convert type `{:?}` to a closure kind", self), + } + } + + fn from_closure_kind(interner: DbInterner<'db>, kind: rustc_type_ir::ClosureKind) -> Self { + match kind { + ClosureKind::Fn => Ty::new(interner, TyKind::Int(IntTy::I8)), + ClosureKind::FnMut => Ty::new(interner, TyKind::Int(IntTy::I16)), + ClosureKind::FnOnce => Ty::new(interner, TyKind::Int(IntTy::I32)), + } + } + + fn from_coroutine_closure_kind( + interner: DbInterner<'db>, + kind: rustc_type_ir::ClosureKind, + ) -> Self { + match kind { + ClosureKind::Fn | ClosureKind::FnMut => Ty::new(interner, TyKind::Int(IntTy::I16)), + ClosureKind::FnOnce => Ty::new(interner, TyKind::Int(IntTy::I32)), + } + } + + fn discriminant_ty( + self, + interner: DbInterner<'db>, + ) -> <DbInterner<'db> as rustc_type_ir::Interner>::Ty { + match self.kind() { + TyKind::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type().to_ty(interner), + TyKind::Coroutine(_, args) => args.as_coroutine().discr_ty(interner), + + TyKind::Param(_) | TyKind::Alias(..) | TyKind::Infer(InferTy::TyVar(_)) => { + /* + let assoc_items = tcx.associated_item_def_ids( + tcx.require_lang_item(hir::LangItem::DiscriminantKind, None), + ); + TyKind::new_projection_from_args(tcx, assoc_items[0], tcx.mk_args(&[self.into()])) + */ + unimplemented!() + } + + TyKind::Pat(ty, _) => ty.discriminant_ty(interner), + + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Adt(..) + | TyKind::Foreign(_) + | TyKind::Str + | TyKind::Array(..) + | TyKind::Slice(_) + | TyKind::RawPtr(_, _) + | TyKind::Ref(..) + | TyKind::FnDef(..) + | TyKind::FnPtr(..) + | TyKind::Dynamic(..) + | TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::CoroutineWitness(..) + | TyKind::Never + | TyKind::Tuple(_) + | TyKind::Error(_) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => { + Ty::new(interner, TyKind::Uint(UintTy::U8)) + } + + TyKind::Bound(..) + | TyKind::Placeholder(_) + | TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { + panic!( + "`dself.iter().map(|v| v.try_fold_with(folder)).collect::<Result<_, _>>()?iscriminant_ty` applied to unexpected type: {self:?}" + ) + } + TyKind::UnsafeBinder(..) => unimplemented!(), + } + } + + fn new_unsafe_binder( + interner: DbInterner<'db>, + ty: rustc_type_ir::Binder< + DbInterner<'db>, + <DbInterner<'db> as rustc_type_ir::Interner>::Ty, + >, + ) -> Self { + Ty::new(interner, TyKind::UnsafeBinder(ty.into())) + } + + fn has_unsafe_fields(self) -> bool { + false + } +} + +interned_vec_db!(Tys, Ty); + +impl<'db> rustc_type_ir::inherent::Tys<DbInterner<'db>> for Tys<'db> { + fn inputs(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::FnInputTys { + Tys::new_from_iter( + DbInterner::conjure(), + self.as_slice().split_last().unwrap().1.iter().cloned(), + ) + } + + fn output(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::Ty { + *self.as_slice().split_last().unwrap().0 + } +} + +pub type PlaceholderTy = Placeholder<BoundTy>; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct ParamTy { + pub index: u32, +} + +impl ParamTy { + pub fn to_ty<'db>(self, interner: DbInterner<'db>) -> Ty<'db> { + Ty::new_param(interner, self.index, sym::MISSING_NAME.clone()) + } +} + +impl std::fmt::Debug for ParamTy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.index) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct BoundTy { + pub var: BoundVar, + pub kind: BoundTyKind, +} + +impl std::fmt::Debug for BoundTy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.kind { + BoundTyKind::Anon => write!(f, "{:?}", self.var), + BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum BoundTyKind { + Anon, + Param(SolverDefId), +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct ErrorGuaranteed; + +impl<'db> TypeVisitable<DbInterner<'db>> for ErrorGuaranteed { + fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_error(*self) + } +} + +impl<'db> TypeFoldable<DbInterner<'db>> for ErrorGuaranteed { + fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>( + self, + folder: &mut F, + ) -> Result<Self, F::Error> { + Ok(self) + } + fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self { + self + } +} + +impl ParamLike for ParamTy { + fn index(self) -> u32 { + self.index + } +} + +impl<'db> BoundVarLike<DbInterner<'db>> for BoundTy { + fn var(self) -> BoundVar { + self.var + } + + fn assert_eq(self, var: BoundVarKind) { + assert_eq!(self.kind, var.expect_ty()) + } +} + +impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderTy { + type Bound = BoundTy; + + fn universe(self) -> rustc_type_ir::UniverseIndex { + self.universe + } + + fn var(self) -> BoundVar { + self.bound.var + } + + fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { + Placeholder { universe: ui, bound: self.bound } + } + + fn new(ui: rustc_type_ir::UniverseIndex, bound: BoundTy) -> Self { + Placeholder { universe: ui, bound } + } + + fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } } + } +}
diff --git a/crates/hir-ty/src/next_solver/util.rs b/crates/hir-ty/src/next_solver/util.rs new file mode 100644 index 0000000..cedc203 --- /dev/null +++ b/crates/hir-ty/src/next_solver/util.rs
@@ -0,0 +1,1064 @@ +//! Various utilities for the next-trait-solver. + +use std::iter; +use std::ops::{self, ControlFlow}; + +use base_db::Crate; +use hir_def::lang_item::LangItem; +use hir_def::{BlockId, HasModule, ItemContainerId, Lookup}; +use intern::sym; +use la_arena::Idx; +use rustc_abi::{Float, HasDataLayout, Integer, IntegerType, Primitive, ReprOptions}; +use rustc_type_ir::data_structures::IndexMap; +use rustc_type_ir::inherent::{ + AdtDef, Const as _, GenericArg as _, GenericArgs as _, ParamEnv as _, Region as _, SliceLike, + Ty as _, +}; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SizedTraitKind; +use rustc_type_ir::{ + BoundVar, Canonical, DebruijnIndex, GenericArgKind, INNERMOST, Interner, PredicatePolarity, + TypeFlags, TypeVisitable, TypeVisitableExt, +}; +use rustc_type_ir::{ + ConstKind, CoroutineArgs, FloatTy, IntTy, RegionKind, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitor, UintTy, UniverseIndex, inherent::IntoKind, +}; +use rustc_type_ir::{InferCtxtLike, TypeFoldable}; + +use crate::lower_nextsolver::{LifetimeElisionKind, TyLoweringContext}; +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::{ + CanonicalVarKind, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, + TypingMode, +}; +use crate::{ + db::HirDatabase, + from_foreign_def_id, + method_resolution::{TraitImpls, TyFingerprint}, +}; + +use super::fold::{BoundVarReplacer, FnMutDelegate}; +use super::generics::generics; +use super::{ + AliasTerm, AliasTy, Binder, BoundRegion, BoundTy, BoundTyKind, BoundVarKind, BoundVarKinds, + CanonicalVars, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, GenericArg, + GenericArgs, Predicate, PredicateKind, ProjectionPredicate, Region, SolverContext, SolverDefId, + Term, TraitPredicate, TraitRef, Ty, TyKind, +}; + +#[derive(Clone, Debug)] +pub struct Discr<'db> { + /// Bit representation of the discriminant (e.g., `-128i8` is `0xFF_u128`). + pub val: u128, + pub ty: Ty<'db>, +} + +impl<'db> Discr<'db> { + /// Adds `1` to the value and wraps around if the maximum for the type is reached. + pub fn wrap_incr(self, interner: DbInterner<'db>) -> Self { + self.checked_add(interner, 1).0 + } + pub fn checked_add(self, interner: DbInterner<'db>, n: u128) -> (Self, bool) { + let (size, signed) = self.ty.int_size_and_signed(interner); + let (val, oflo) = if signed { + let min = size.signed_int_min(); + let max = size.signed_int_max(); + let val = size.sign_extend(self.val); + assert!(n < (i128::MAX as u128)); + let n = n as i128; + let oflo = val > max - n; + let val = if oflo { min + (n - (max - val) - 1) } else { val + n }; + // zero the upper bits + let val = val as u128; + let val = size.truncate(val); + (val, oflo) + } else { + let max = size.unsigned_int_max(); + let val = self.val; + let oflo = val > max - n; + let val = if oflo { n - (max - val) - 1 } else { val + n }; + (val, oflo) + }; + (Self { val, ty: self.ty }, oflo) + } +} + +pub trait IntegerTypeExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; + fn initial_discriminant<'db>(&self, interner: DbInterner<'db>) -> Discr<'db>; + fn disr_incr<'db>( + &self, + interner: DbInterner<'db>, + val: Option<Discr<'db>>, + ) -> Option<Discr<'db>>; +} + +impl IntegerTypeExt for IntegerType { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + match self { + IntegerType::Pointer(true) => Ty::new(interner, TyKind::Int(IntTy::Isize)), + IntegerType::Pointer(false) => Ty::new(interner, TyKind::Uint(UintTy::Usize)), + IntegerType::Fixed(i, s) => i.to_ty(interner, *s), + } + } + + fn initial_discriminant<'db>(&self, interner: DbInterner<'db>) -> Discr<'db> { + Discr { val: 0, ty: self.to_ty(interner) } + } + + fn disr_incr<'db>( + &self, + interner: DbInterner<'db>, + val: Option<Discr<'db>>, + ) -> Option<Discr<'db>> { + if let Some(val) = val { + assert_eq!(self.to_ty(interner), val.ty); + let (new, oflo) = val.checked_add(interner, 1); + if oflo { None } else { Some(new) } + } else { + Some(self.initial_discriminant(interner)) + } + } +} + +pub trait IntegerExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>, signed: bool) -> Ty<'db>; + fn from_int_ty<C: HasDataLayout>(cx: &C, ity: IntTy) -> Integer; + fn from_uint_ty<C: HasDataLayout>(cx: &C, ity: UintTy) -> Integer; + fn repr_discr<'db>( + interner: DbInterner<'db>, + ty: Ty<'db>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool); +} + +impl IntegerExt for Integer { + #[inline] + fn to_ty<'db>(&self, interner: DbInterner<'db>, signed: bool) -> Ty<'db> { + use Integer::*; + match (*self, signed) { + (I8, false) => Ty::new(interner, TyKind::Uint(UintTy::U8)), + (I16, false) => Ty::new(interner, TyKind::Uint(UintTy::U16)), + (I32, false) => Ty::new(interner, TyKind::Uint(UintTy::U32)), + (I64, false) => Ty::new(interner, TyKind::Uint(UintTy::U64)), + (I128, false) => Ty::new(interner, TyKind::Uint(UintTy::U128)), + (I8, true) => Ty::new(interner, TyKind::Int(IntTy::I8)), + (I16, true) => Ty::new(interner, TyKind::Int(IntTy::I16)), + (I32, true) => Ty::new(interner, TyKind::Int(IntTy::I32)), + (I64, true) => Ty::new(interner, TyKind::Int(IntTy::I64)), + (I128, true) => Ty::new(interner, TyKind::Int(IntTy::I128)), + } + } + + fn from_int_ty<C: HasDataLayout>(cx: &C, ity: IntTy) -> Integer { + use Integer::*; + match ity { + IntTy::I8 => I8, + IntTy::I16 => I16, + IntTy::I32 => I32, + IntTy::I64 => I64, + IntTy::I128 => I128, + IntTy::Isize => cx.data_layout().ptr_sized_integer(), + } + } + fn from_uint_ty<C: HasDataLayout>(cx: &C, ity: UintTy) -> Integer { + use Integer::*; + match ity { + UintTy::U8 => I8, + UintTy::U16 => I16, + UintTy::U32 => I32, + UintTy::U64 => I64, + UintTy::U128 => I128, + UintTy::Usize => cx.data_layout().ptr_sized_integer(), + } + } + + /// 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 + /// that shouldn't affect anything, other than maybe debuginfo. + fn repr_discr<'db>( + interner: DbInterner<'db>, + ty: Ty<'db>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool) { + // Theoretically, negative values could be larger in unsigned representation + // than the unsigned representation of the signed minimum. However, if there + // are any negative values, the only valid unsigned representation is u128 + // which can fit all i128 values, so the result remains unaffected. + let unsigned_fit = Integer::fit_unsigned(std::cmp::max(min as u128, max as u128)); + let signed_fit = std::cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); + + if let Some(ity) = repr.int { + let discr = Integer::from_attr(&interner, ity); + let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; + if discr < fit { + panic!( + "Integer::repr_discr: `#[repr]` hint too small for \ + discriminant range of enum `{ty:?}`" + ) + } + return (discr, ity.is_signed()); + } + + let at_least = if repr.c() { + // This is usually I32, however it can be different on some platforms, + // notably hexagon and arm-none/thumb-none + interner.data_layout().c_enum_min_size + } else { + // repr(Rust) enums try to be as small as possible + Integer::I8 + }; + + // If there are no negative values, we can use the unsigned fit. + if min >= 0 { + (std::cmp::max(unsigned_fit, at_least), false) + } else { + (std::cmp::max(signed_fit, at_least), true) + } + } +} + +pub trait FloatExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; + fn from_float_ty(fty: FloatTy) -> Self; +} + +impl FloatExt for Float { + #[inline] + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + use Float::*; + match *self { + F16 => Ty::new(interner, TyKind::Float(FloatTy::F16)), + F32 => Ty::new(interner, TyKind::Float(FloatTy::F32)), + F64 => Ty::new(interner, TyKind::Float(FloatTy::F64)), + F128 => Ty::new(interner, TyKind::Float(FloatTy::F128)), + } + } + + fn from_float_ty(fty: FloatTy) -> Self { + use Float::*; + match fty { + FloatTy::F16 => F16, + FloatTy::F32 => F32, + FloatTy::F64 => F64, + FloatTy::F128 => F128, + } + } +} + +pub trait PrimitiveExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; + fn to_int_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; +} + +impl PrimitiveExt for Primitive { + #[inline] + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + match *self { + Primitive::Int(i, signed) => i.to_ty(interner, signed), + Primitive::Float(f) => f.to_ty(interner), + Primitive::Pointer(_) => Ty::new( + interner, + TyKind::RawPtr( + Ty::new(interner, TyKind::Tuple(Default::default())), + rustc_ast_ir::Mutability::Mut, + ), + ), + } + } + + /// Return an *integer* type matching this primitive. + /// Useful in particular when dealing with enum discriminants. + #[inline] + fn to_int_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + match *self { + Primitive::Int(i, signed) => i.to_ty(interner, signed), + Primitive::Pointer(_) => { + let signed = false; + interner.data_layout().ptr_sized_integer().to_ty(interner, signed) + } + Primitive::Float(_) => panic!("floats do not have an int type"), + } + } +} + +impl<'db> HasDataLayout for DbInterner<'db> { + fn data_layout(&self) -> &rustc_abi::TargetDataLayout { + unimplemented!() + } +} + +pub trait CoroutineArgsExt<'db> { + fn discr_ty(&self, interner: DbInterner<'db>) -> Ty<'db>; +} + +impl<'db> CoroutineArgsExt<'db> for CoroutineArgs<DbInterner<'db>> { + /// The type of the state discriminant used in the coroutine type. + #[inline] + fn discr_ty(&self, interner: DbInterner<'db>) -> Ty<'db> { + Ty::new(interner, TyKind::Uint(UintTy::U32)) + } +} + +/// Finds the max universe present +pub struct MaxUniverse { + max_universe: UniverseIndex, +} + +impl Default for MaxUniverse { + fn default() -> Self { + Self::new() + } +} + +impl MaxUniverse { + pub fn new() -> Self { + MaxUniverse { max_universe: UniverseIndex::ROOT } + } + + pub fn max_universe(self) -> UniverseIndex { + self.max_universe + } +} + +impl<'db> TypeVisitor<DbInterner<'db>> for MaxUniverse { + type Result = (); + + fn visit_ty(&mut self, t: Ty<'db>) { + if let TyKind::Placeholder(placeholder) = t.kind() { + self.max_universe = UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: Const<'db>) { + if let ConstKind::Placeholder(placeholder) = c.kind() { + self.max_universe = UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + c.super_visit_with(self) + } + + fn visit_region(&mut self, r: Region<'db>) { + if let RegionKind::RePlaceholder(placeholder) = r.kind() { + self.max_universe = UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + } +} + +pub struct BottomUpFolder<'db, F, G, H> +where + F: FnMut(Ty<'db>) -> Ty<'db>, + G: FnMut(Region<'db>) -> Region<'db>, + H: FnMut(Const<'db>) -> Const<'db>, +{ + pub interner: DbInterner<'db>, + pub ty_op: F, + pub lt_op: G, + pub ct_op: H, +} + +impl<'db, F, G, H> TypeFolder<DbInterner<'db>> for BottomUpFolder<'db, F, G, H> +where + F: FnMut(Ty<'db>) -> Ty<'db>, + G: FnMut(Region<'db>) -> Region<'db>, + H: FnMut(Const<'db>) -> Const<'db>, +{ + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + let t = ty.super_fold_with(self); + (self.ty_op)(t) + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + // This one is a little different, because `super_fold_with` is not + // implemented on non-recursive `Region`. + (self.lt_op)(r) + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + let ct = ct.super_fold_with(self); + (self.ct_op)(ct) + } +} + +pub(crate) fn for_trait_impls( + db: &dyn HirDatabase, + krate: Crate, + block: Option<BlockId>, + trait_id: hir_def::TraitId, + self_ty_fp: Option<TyFingerprint>, + mut f: impl FnMut(&TraitImpls) -> ControlFlow<()>, +) -> ControlFlow<()> { + // Note: Since we're using `impls_for_trait` and `impl_provided_for`, + // only impls where the trait can be resolved should ever reach Chalk. + // `impl_datum` relies on that and will panic if the trait can't be resolved. + let in_deps = db.trait_impls_in_deps(krate); + let in_self = db.trait_impls_in_crate(krate); + let trait_module = trait_id.module(db); + let type_module = match self_ty_fp { + Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(db)), + Some(TyFingerprint::ForeignType(type_id)) => Some(from_foreign_def_id(type_id).module(db)), + Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(db)), + _ => None, + }; + + let mut def_blocks = + [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())]; + + let block_impls = iter::successors(block, |&block_id| { + cov_mark::hit!(block_local_impls); + block_id.loc(db).module.containing_block() + }) + .inspect(|&block_id| { + // make sure we don't search the same block twice + def_blocks.iter_mut().for_each(|block| { + if *block == Some(block_id) { + *block = None; + } + }); + }) + .filter_map(|block_id| db.trait_impls_in_block(block_id)); + f(&in_self)?; + for it in in_deps.iter().map(ops::Deref::deref) { + f(it)?; + } + for it in block_impls { + f(&it)?; + } + for it in def_blocks.into_iter().flatten().filter_map(|it| db.trait_impls_in_block(it)) { + f(&it)?; + } + ControlFlow::Continue(()) +} + +// FIXME(next-trait-solver): uplift +pub fn sizedness_constraint_for_ty<'db>( + interner: DbInterner<'db>, + sizedness: SizedTraitKind, + ty: Ty<'db>, +) -> Option<Ty<'db>> { + use rustc_type_ir::TyKind::*; + + match ty.kind() { + // these are always sized + Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) + | FnPtr(..) | Array(..) | Closure(..) | CoroutineClosure(..) | Coroutine(..) + | CoroutineWitness(..) | Never => None, + + // these are never sized + Str | Slice(..) | Dynamic(_, _, rustc_type_ir::DynKind::Dyn) => match sizedness { + // Never `Sized` + SizedTraitKind::Sized => Some(ty), + // Always `MetaSized` + SizedTraitKind::MetaSized => None, + }, + + // Maybe `Sized` or `MetaSized` + Param(..) | Alias(..) | Error(_) => Some(ty), + + // We cannot instantiate the binder, so just return the *original* type back, + // but only if the inner type has a sized constraint. Thus we skip the binder, + // but don't actually use the result from `sized_constraint_for_ty`. + UnsafeBinder(inner_ty) => { + sizedness_constraint_for_ty(interner, sizedness, inner_ty.skip_binder()).map(|_| ty) + } + + // Never `MetaSized` or `Sized` + Foreign(..) => Some(ty), + + // Recursive cases + Pat(ty, _) => sizedness_constraint_for_ty(interner, sizedness, ty), + + Tuple(tys) => tys + .into_iter() + .last() + .and_then(|ty| sizedness_constraint_for_ty(interner, sizedness, ty)), + + Adt(adt, args) => { + let tail_ty = + EarlyBinder::bind(adt.all_field_tys(interner).skip_binder().into_iter().last()?) + .instantiate(interner, args); + sizedness_constraint_for_ty(interner, sizedness, tail_ty) + } + + Placeholder(..) | Bound(..) | Infer(..) => { + panic!("unexpected type `{ty:?}` in sizedness_constraint_for_ty") + } + } +} + +pub fn apply_args_to_binder<'db, T: TypeFoldable<DbInterner<'db>>>( + b: Binder<'db, T>, + args: GenericArgs<'db>, + interner: DbInterner<'db>, +) -> T { + let types = &mut |ty: BoundTy| args.as_slice()[ty.var.index()].expect_ty(); + let regions = &mut |region: BoundRegion| args.as_slice()[region.var.index()].expect_region(); + let consts = &mut |const_: BoundVar| args.as_slice()[const_.index()].expect_const(); + let mut instantiate = BoundVarReplacer::new(interner, FnMutDelegate { types, regions, consts }); + b.skip_binder().fold_with(&mut instantiate) +} + +pub(crate) fn mini_canonicalize<'db, T: TypeFoldable<DbInterner<'db>>>( + mut context: SolverContext<'db>, + val: T, +) -> Canonical<DbInterner<'db>, T> { + let mut canon = MiniCanonicalizer { + context: &mut context, + db: DebruijnIndex::ZERO, + vars: IndexMap::default(), + }; + let canon_val = val.fold_with(&mut canon); + let vars = canon.vars; + Canonical { + value: canon_val, + max_universe: UniverseIndex::from_u32(1), + variables: CanonicalVars::new_from_iter( + context.cx(), + vars.iter().map(|(k, v)| match (*k).kind() { + GenericArgKind::Type(ty) => match ty.kind() { + TyKind::Int(..) | TyKind::Uint(..) => { + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) + } + TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::Float, + ), + _ => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ZERO), + ), + }, + GenericArgKind::Lifetime(_) => { + rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ZERO) + } + GenericArgKind::Const(_) => { + rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ZERO) + } + }), + ), + } +} + +struct MiniCanonicalizer<'a, 'db> { + context: &'a mut SolverContext<'db>, + db: DebruijnIndex, + vars: IndexMap<GenericArg<'db>, usize>, +} + +impl<'db> TypeFolder<DbInterner<'db>> for MiniCanonicalizer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.context.cx() + } + + fn fold_binder<T: TypeFoldable<DbInterner<'db>>>( + &mut self, + t: rustc_type_ir::Binder<DbInterner<'db>, T>, + ) -> rustc_type_ir::Binder<DbInterner<'db>, T> { + self.db.shift_in(1); + let res = t.map_bound(|t| t.fold_with(self)); + self.db.shift_out(1); + res + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + match t.kind() { + rustc_type_ir::TyKind::Bound(db, _) => { + if db >= self.db { + panic!("Unexpected bound var"); + } + t + } + rustc_type_ir::TyKind::Infer(infer) => { + let t = match infer { + rustc_type_ir::InferTy::TyVar(vid) => { + self.context.opportunistic_resolve_ty_var(vid) + } + rustc_type_ir::InferTy::IntVar(vid) => { + self.context.opportunistic_resolve_int_var(vid) + } + rustc_type_ir::InferTy::FloatVar(vid) => { + self.context.opportunistic_resolve_float_var(vid) + } + _ => t, + }; + let len = self.vars.len(); + let var = *self.vars.entry(t.into()).or_insert(len); + Ty::new( + self.cx(), + TyKind::Bound( + self.db, + BoundTy { kind: super::BoundTyKind::Anon, var: BoundVar::from_usize(var) }, + ), + ) + } + _ => t.super_fold_with(self), + } + } + + fn fold_region( + &mut self, + r: <DbInterner<'db> as rustc_type_ir::Interner>::Region, + ) -> <DbInterner<'db> as rustc_type_ir::Interner>::Region { + match r.kind() { + RegionKind::ReBound(db, _) => { + if db >= self.db { + panic!("Unexpected bound var"); + } + r + } + RegionKind::ReVar(vid) => { + let len = self.vars.len(); + let var = *self.vars.entry(r.into()).or_insert(len); + Region::new( + self.cx(), + RegionKind::ReBound( + self.db, + BoundRegion { + kind: super::BoundRegionKind::Anon, + var: BoundVar::from_usize(var), + }, + ), + ) + } + _ => r, + } + } + + fn fold_const( + &mut self, + c: <DbInterner<'db> as rustc_type_ir::Interner>::Const, + ) -> <DbInterner<'db> as rustc_type_ir::Interner>::Const { + match c.kind() { + ConstKind::Bound(db, _) => { + if db >= self.db { + panic!("Unexpected bound var"); + } + c + } + ConstKind::Infer(infer) => { + let len = self.vars.len(); + let var = *self.vars.entry(c.into()).or_insert(len); + Const::new(self.cx(), ConstKind::Bound(self.db, BoundVar::from_usize(var))) + } + _ => c.super_fold_with(self), + } + } +} + +pub fn explicit_item_bounds<'db>( + interner: DbInterner<'db>, + def_id: SolverDefId, +) -> EarlyBinder<'db, Clauses<'db>> { + let db = interner.db(); + match def_id { + SolverDefId::TypeAliasId(type_alias) => { + let trait_ = match type_alias.lookup(db).container { + ItemContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + + // Lower bounds -- we could/should maybe move this to a separate query in `lower` + let type_alias_data = db.type_alias_signature(type_alias); + let generic_params = generics(db, type_alias.into()); + 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 trait_args = GenericArgs::identity_for_item(interner, trait_.into()); + 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(); + for bound in &type_alias_data.bounds { + ctx.lower_type_bound(bound, interner_ty, false).for_each(|pred| { + bounds.push(pred); + }); + } + + if !ctx.unsized_types.contains(&interner_ty) { + let sized_trait = LangItem::Sized + .resolve_trait(ctx.db, interner.krate.expect("Must have interner.krate")); + let sized_bound = sized_trait.map(|trait_id| { + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_iter(interner, [interner_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }); + bounds.extend(sized_bound); + bounds.shrink_to_fit(); + } + + rustc_type_ir::EarlyBinder::bind(Clauses::new_from_iter(interner, bounds)) + } + SolverDefId::InternedOpaqueTyId(id) => { + let full_id = db.lookup_intern_impl_trait_id(id); + match full_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let datas = db + .return_type_impl_traits_ns(func) + .expect("impl trait id without impl traits"); + let datas = (*datas).as_ref().skip_binder(); + let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())]; + EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone())) + } + crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => { + let datas = db + .type_alias_impl_traits_ns(alias) + .expect("impl trait id without impl traits"); + let datas = (*datas).as_ref().skip_binder(); + let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())]; + EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone())) + } + crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { + if let Some((future_trait, future_output)) = LangItem::Future + .resolve_trait(db, interner.krate.expect("Must have interner.krate")) + .and_then(|trait_| { + let alias = trait_.trait_items(db).associated_type_by_name( + &hir_expand::name::Name::new_symbol_root(sym::Output.clone()), + )?; + Some((trait_, alias)) + }) + { + let args = GenericArgs::identity_for_item(interner, def_id); + let out = args.as_slice()[0]; + let mut predicates = vec![]; + + let item_ty = Ty::new_alias( + interner, + rustc_type_ir::AliasTyKind::Opaque, + AliasTy::new_from_args(interner, def_id, args), + ); + + let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + polarity: rustc_type_ir::PredicatePolarity::Positive, + trait_ref: TraitRef::new_from_args( + interner, + future_trait.into(), + GenericArgs::new_from_iter(interner, [item_ty.into()]), + ), + })); + predicates.push(Clause(Predicate::new( + interner, + Binder::bind_with_vars( + kind, + BoundVarKinds::new_from_iter( + interner, + [BoundVarKind::Ty(BoundTyKind::Anon)], + ), + ), + ))); + let sized_trait = LangItem::Sized + .resolve_trait(db, interner.krate.expect("Must have interner.krate")); + if let Some(sized_trait_) = sized_trait { + let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + polarity: rustc_type_ir::PredicatePolarity::Positive, + trait_ref: TraitRef::new_from_args( + interner, + sized_trait_.into(), + GenericArgs::new_from_iter(interner, [item_ty.into()]), + ), + })); + predicates.push(Clause(Predicate::new( + interner, + Binder::bind_with_vars( + kind, + BoundVarKinds::new_from_iter( + interner, + [BoundVarKind::Ty(BoundTyKind::Anon)], + ), + ), + ))); + } + let kind = + PredicateKind::Clause(ClauseKind::Projection(ProjectionPredicate { + projection_term: AliasTerm::new_from_args( + interner, + future_output.into(), + GenericArgs::new_from_iter(interner, [item_ty.into()]), + ), + term: match out.kind() { + GenericArgKind::Lifetime(lt) => panic!(), + GenericArgKind::Type(ty) => Term::Ty(ty), + GenericArgKind::Const(const_) => Term::Const(const_), + }, + })); + predicates.push(Clause(Predicate::new( + interner, + Binder::bind_with_vars( + kind, + BoundVarKinds::new_from_iter( + interner, + [BoundVarKind::Ty(BoundTyKind::Anon)], + ), + ), + ))); + EarlyBinder::bind(Clauses::new_from_iter(interner, predicates)) + } else { + // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. + EarlyBinder::bind(Clauses::new_from_iter(interner, [])) + } + } + } + } + _ => panic!("Unexpected GeneridDefId"), + } +} + +pub struct ContainsTypeErrors; + +impl<'db> TypeVisitor<DbInterner<'db>> for ContainsTypeErrors { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result { + match t.kind() { + rustc_type_ir::TyKind::Error(_) => ControlFlow::Break(()), + _ => t.super_visit_with(self), + } + } +} + +/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. +pub struct PlaceholderReplacer<'a, 'db> { + infcx: &'a InferCtxt<'db>, + mapped_regions: FxIndexMap<PlaceholderRegion, BoundRegion>, + mapped_types: FxIndexMap<Placeholder<BoundTy>, BoundTy>, + mapped_consts: FxIndexMap<PlaceholderConst, BoundVar>, + universe_indices: &'a [Option<UniverseIndex>], + current_index: DebruijnIndex, +} + +impl<'a, 'db> PlaceholderReplacer<'a, 'db> { + pub fn replace_placeholders<T: TypeFoldable<DbInterner<'db>>>( + infcx: &'a InferCtxt<'db>, + mapped_regions: FxIndexMap<PlaceholderRegion, BoundRegion>, + mapped_types: FxIndexMap<Placeholder<BoundTy>, BoundTy>, + mapped_consts: FxIndexMap<PlaceholderConst, BoundVar>, + universe_indices: &'a [Option<UniverseIndex>], + value: T, + ) -> T { + let mut replacer = PlaceholderReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + current_index: INNERMOST, + }; + value.fold_with(&mut replacer) + } +} + +impl<'db> TypeFolder<DbInterner<'db>> for PlaceholderReplacer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn fold_binder<T: TypeFoldable<DbInterner<'db>>>( + &mut self, + t: Binder<'db, T>, + ) -> Binder<'db, T> { + if !t.has_placeholders() && !t.has_infer() { + return t; + } + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r0: Region<'db>) -> Region<'db> { + let r1 = match r0.kind() { + RegionKind::ReVar(vid) => self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.interner, vid), + _ => r0, + }; + + let r2 = match r1.kind() { + RegionKind::RePlaceholder(p) => { + let replace_var = self.mapped_regions.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| panic!("Unexpected placeholder universe.")); + let db = DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Region::new_bound(self.cx(), db, *replace_var) + } + None => r1, + } + } + _ => r1, + }; + + tracing::debug!(?r0, ?r1, ?r2, "fold_region"); + + r2 + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + let ty = self.infcx.shallow_resolve(ty); + match ty.kind() { + TyKind::Placeholder(p) => { + let replace_var = self.mapped_types.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| panic!("Unexpected placeholder universe.")); + let db = DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Ty::new_bound(self.infcx.interner, db, *replace_var) + } + None => { + if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } + } + } + + _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self), + _ => ty, + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + let ct = self.infcx.shallow_resolve_const(ct); + if let ConstKind::Placeholder(p) = ct.kind() { + let replace_var = self.mapped_consts.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| panic!("Unexpected placeholder universe.")); + let db = DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Const::new_bound(self.infcx.interner, db, *replace_var) + } + None => { + if ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } + } + } else { + ct.super_fold_with(self) + } + } +} + +pub(crate) fn needs_normalization<'db, T: TypeVisitable<DbInterner<'db>>>( + infcx: &InferCtxt<'db>, + value: &T, +) -> bool { + let mut flags = TypeFlags::HAS_ALIAS; + + // Opaques are treated as rigid outside of `TypingMode::PostAnalysis`, + // so we can ignore those. + match infcx.typing_mode() { + // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(TypeFlags::HAS_TY_OPAQUE), + TypingMode::PostAnalysis => {} + } + + value.has_type_flags(flags) +} + +pub fn sizedness_fast_path<'db>( + tcx: DbInterner<'db>, + predicate: Predicate<'db>, + param_env: ParamEnv<'db>, +) -> bool { + // Proving `Sized`/`MetaSized`, very often on "obviously sized" types like + // `&T`, accounts for about 60% percentage of the predicates we have to prove. No need to + // canonicalize and all that for such cases. + if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = predicate.kind().skip_binder() + && trait_pred.polarity == PredicatePolarity::Positive + { + let sizedness = match tcx.as_lang_item(trait_pred.def_id()) { + Some(TraitSolverLangItem::Sized) => SizedTraitKind::Sized, + Some(TraitSolverLangItem::MetaSized) => SizedTraitKind::MetaSized, + _ => return false, + }; + + // FIXME(sized_hierarchy): this temporarily reverts the `sized_hierarchy` feature + // while a proper fix for `tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs` + // is pending a proper fix + if matches!(sizedness, SizedTraitKind::MetaSized) { + return true; + } + + if trait_pred.self_ty().has_trivial_sizedness(tcx, sizedness) { + tracing::debug!("fast path -- trivial sizedness"); + return true; + } + + if matches!(trait_pred.self_ty().kind(), TyKind::Param(_) | TyKind::Placeholder(_)) { + for clause in param_env.caller_bounds().iter() { + if let ClauseKind::Trait(clause_pred) = clause.kind().skip_binder() + && clause_pred.polarity == PredicatePolarity::Positive + && clause_pred.self_ty() == trait_pred.self_ty() + && (clause_pred.def_id() == trait_pred.def_id() + || (sizedness == SizedTraitKind::MetaSized + && tcx.is_lang_item(clause_pred.def_id(), TraitSolverLangItem::Sized))) + { + return true; + } + } + } + } + + false +}
diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs index b5de0e5..775136d 100644 --- a/crates/hir-ty/src/test_db.rs +++ b/crates/hir-ty/src/test_db.rs
@@ -149,7 +149,7 @@ .into_iter() .filter_map(|file_id| { let text = self.file_text(file_id.file_id(self)); - let annotations = extract_annotations(&text.text(self)); + let annotations = extract_annotations(text.text(self)); if annotations.is_empty() { return None; }
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index 9605a0b..d98b660 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs
@@ -6,14 +6,12 @@ mod macros; mod method_resolution; mod never_type; +mod opaque_types; mod patterns; mod regression; mod simple; +mod trait_aliases; mod traits; -mod type_alias_impl_traits; - -use std::env; -use std::sync::LazyLock; use base_db::{Crate, SourceDatabase}; use expect_test::Expect; @@ -35,8 +33,6 @@ ast::{self, AstNode, HasName}, }; use test_fixture::WithFixture; -use tracing_subscriber::{Registry, layer::SubscriberExt}; -use tracing_tree::HierarchicalLayer; use triomphe::Arc; use crate::{ @@ -44,6 +40,7 @@ db::HirDatabase, display::{DisplayTarget, HirDisplay}, infer::{Adjustment, TypeMismatch}, + setup_tracing, test_db::TestDB, }; @@ -51,23 +48,6 @@ // against snapshots of the expected results using expect. Use // `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots. -fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> { - static ENABLE: LazyLock<bool> = LazyLock::new(|| env::var("CHALK_DEBUG").is_ok()); - if !*ENABLE { - return None; - } - - let filter: tracing_subscriber::filter::Targets = - env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default(); - let layer = HierarchicalLayer::default() - .with_indent_lines(true) - .with_ansi(false) - .with_indent_amount(2) - .with_writer(std::io::stderr); - let subscriber = Registry::default().with(filter).with(layer); - Some(tracing::subscriber::set_default(subscriber)) -} - #[track_caller] fn check_types(#[rust_analyzer::rust_fixture] ra_fixture: &str) { check_impl(ra_fixture, false, true, false)
diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs index dbc68ee..5893894 100644 --- a/crates/hir-ty/src/tests/closure_captures.rs +++ b/crates/hir-ty/src/tests/closure_captures.rs
@@ -12,9 +12,10 @@ use crate::mir::MirSpan; use crate::test_db::TestDB; -use super::visit_module; +use super::{setup_tracing, visit_module}; fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let _tracing = setup_tracing(); let (db, file_id) = TestDB::with_single_file(ra_fixture); let module = db.module_for_file(file_id.file_id(&db)); let def_map = module.def_map(&db);
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index 3894b4b..3b7d4d2 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs
@@ -96,7 +96,7 @@ fn test() { let x = if true { foo(&[1]) - // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize) } else { &[1] }; @@ -148,7 +148,7 @@ fn test(i: i32) { let x = match i { 2 => foo(&[2]), - // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize) 1 => &[1], _ => &[3], }; @@ -484,6 +484,8 @@ ); } +// FIXME(next-solver): We could learn more from the `&S` -> `&dyn Foo<i8, _>` coercion if we followed the rustc model +// where unsized is successful if all unsizing trait goals are certain (and non-unsizing goals are delayed). #[test] fn coerce_unsize_trait_object_simple() { check_types( @@ -503,8 +505,8 @@ //^ S<i8, i16> let obj: &dyn Bar<_, i8, i16> = &S; //^ S<i8, i16> - let obj: &dyn Foo<i8, _> = &S; - //^ S<i8, {unknown}> + //let obj: &dyn Foo<i8, _> = &S; + // S<{unknown}, {unknown}> }"#, ); } @@ -543,9 +545,9 @@ fn test() { let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; - //^^^^^^^^^^^^^^^^^^^^^ expected &'? Foo<[usize]>, got &'? Foo<[i32; 3]> + //^^^^^^^^^^^^^^^^^^^^^ type: &'? Foo<[usize; 3]> let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); - //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &'? Bar<[usize]>, got &'? Bar<[i32; 3]> + //^^^^^^^^^^^^^^^^^^^^^^^^^^ type: &'? Bar<[usize; 3]> } "#, ); @@ -899,7 +901,7 @@ fn index(&self, index: usize) -> &Self::Output { &() } } -impl core::ops::IndexMut for StructMut { +impl core::ops::IndexMut<usize> for StructMut { fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut () } } fn test() {
diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs index 3159499..df9061d 100644 --- a/crates/hir-ty/src/tests/incremental.rs +++ b/crates/hir-ty/src/tests/incremental.rs
@@ -519,6 +519,7 @@ ); } +// FIXME(next-solver): does this test make sense with fast path? #[test] fn add_struct_invalidates_trait_solve() { let (mut db, file_id) = TestDB::with_single_file( @@ -559,7 +560,7 @@ let _inference_result = db.infer(def); } }, - &[("trait_solve_shim", 2)], + &[("trait_solve_shim", 0)], expect_test::expect![[r#" [ "source_root_crates_shim", @@ -606,21 +607,17 @@ "callable_item_signature_shim", "adt_variance_shim", "variances_of_shim", - "trait_solve_shim", - "trait_datum_shim", - "generic_predicates_shim", - "adt_datum_shim", "trait_impls_in_deps_shim", "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", "type_for_adt_tracked", - "impl_datum_shim", - "generic_predicates_shim", - "program_clauses_for_chalk_env_shim", + "impl_trait_with_diagnostics_ns_shim", + "impl_self_ty_with_diagnostics_ns_shim", + "generic_predicates_ns_shim", + "generic_predicates_ns_shim", "value_ty_shim", "generic_predicates_shim", - "trait_solve_shim", "lang_item", ] "#]], @@ -703,10 +700,13 @@ "impl_signature_with_source_map_shim", "impl_signature_shim", "callable_item_signature_shim", - "generic_predicates_shim", "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", + "impl_trait_with_diagnostics_ns_shim", + "impl_self_ty_with_diagnostics_ns_shim", + "generic_predicates_ns_shim", + "generic_predicates_ns_shim", "generic_predicates_shim", ] "#]],
diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs index ea7a113..5d088e4 100644 --- a/crates/hir-ty/src/tests/macros.rs +++ b/crates/hir-ty/src/tests/macros.rs
@@ -197,17 +197,17 @@ 39..442 '{ ...!(); }': () 73..94 'spam!(...am!())': {unknown} 100..119 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter - 100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize> + 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': ! - 100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize> - 100..119 'for _ ...!() {}': &'? mut IntoIterator::IntoIter<isize> - 100..119 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&'? mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item> - 100..119 'for _ ...!() {}': Option<IntoIterator::Item<isize>> + 100..119 'for _ ...!() {}': {unknown} + 100..119 'for _ ...!() {}': &'? mut {unknown} + 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 100..119 'for _ ...!() {}': Option<{unknown}> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () - 104..105 '_': IntoIterator::Item<isize> + 104..105 '_': {unknown} 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize 140..156 'while ...!() {}': ! @@ -291,17 +291,17 @@ 53..456 '{ ...!(); }': () 87..108 'spam!(...am!())': {unknown} 114..133 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter - 114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize> + 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': ! - 114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize> - 114..133 'for _ ...!() {}': &'? mut IntoIterator::IntoIter<isize> - 114..133 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&'? mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item> - 114..133 'for _ ...!() {}': Option<IntoIterator::Item<isize>> + 114..133 'for _ ...!() {}': {unknown} + 114..133 'for _ ...!() {}': &'? mut {unknown} + 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 114..133 'for _ ...!() {}': Option<{unknown}> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () - 118..119 '_': IntoIterator::Item<isize> + 118..119 '_': {unknown} 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize 154..170 'while ...!() {}': !
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index c58ca6c..f09b3ef 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1134,6 +1134,7 @@ fn dyn_trait_super_trait_not_in_scope() { check_infer( r#" + //- minicore: dispatch_from_dyn mod m { pub trait SuperTrait { fn foo(&self) -> u32 { 0 } @@ -1309,7 +1310,7 @@ fn dyn_trait_method_priority() { check_types( r#" -//- minicore: from +//- minicore: from, dispatch_from_dyn trait Trait { fn into(&self) -> usize { 0 } } @@ -1824,6 +1825,33 @@ } #[test] +fn deref_fun_3() { + check_types( + r#" +//- minicore: receiver + +struct A<T, U>(T, U); +struct B<T>(T); +struct C<T>(T); + +impl<T> core::ops::Deref for A<B<T>, u32> { + type Target = B<T>; + fn deref(&self) -> &B<T> { &self.0 } +} + +fn make<T>() -> T { loop {} } + +fn test() { + let a1 = A(make(), make()); + let _: usize = (*a1).0; + a1; + //^^ A<B<usize>, u32> +} +"#, + ); +} + +#[test] fn deref_into_inference_var() { check_types( r#" @@ -2077,7 +2105,7 @@ } fn test() { Box::new(Foo).foo(); - //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not)) + //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?5, Not)) } "#, ); @@ -2095,7 +2123,7 @@ use core::mem::ManuallyDrop; fn test() { ManuallyDrop::new(Foo).foo(); - //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?4, Not)) + //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?6, Not)) } "#, );
diff --git a/crates/hir-ty/src/tests/type_alias_impl_traits.rs b/crates/hir-ty/src/tests/opaque_types.rs similarity index 92% rename from crates/hir-ty/src/tests/type_alias_impl_traits.rs rename to crates/hir-ty/src/tests/opaque_types.rs index e2b7bf3..3657854 100644 --- a/crates/hir-ty/src/tests/type_alias_impl_traits.rs +++ b/crates/hir-ty/src/tests/opaque_types.rs
@@ -159,3 +159,19 @@ "#]], ) } + +#[test] +fn leak_auto_traits() { + check_no_mismatches( + r#" +//- minicore: send +fn foo() -> impl Sized {} + +fn is_send<T: Send>(_: T) {} + +fn main() { + is_send(foo()); +} + "#, + ); +}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 238753e..25061e1 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs
@@ -773,7 +773,7 @@ "#, expect![[r#" 379..383 'self': &'? mut PeerSet<D> - 401..424 '{ ... }': dyn Future<Output = ()> + '? + 401..424 '{ ... }': dyn Future<Output = ()> + 'static 411..418 'loop {}': ! 416..418 '{}': () 575..579 'self': &'? mut Self @@ -781,6 +781,9 @@ ); } +// FIXME(next-solver): Though `Repeat: IntoIterator` does not hold here, we +// should be able to do better at given type hints (with Chalk, we did `IntoIterator::Item<Repeat<...>>`) +// From what I can tell, the point of this test is to not panic though. #[test] fn issue_4966() { check_infer( @@ -824,11 +827,11 @@ 311..317 'repeat': Repeat<Map<impl Fn(&'? f64) -> f64>> 320..345 'Repeat...nner }': Repeat<Map<impl Fn(&'? f64) -> f64>> 338..343 'inner': Map<impl Fn(&'? f64) -> f64> - 356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>> - 362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>, Repeat<Map<impl Fn(&'? f64) -> f64>>>(Repeat<Map<impl Fn(&'? f64) -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>> - 362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>> + 356..359 'vec': Vec<{unknown}> + 362..371 'from_iter': fn from_iter<{unknown}, Repeat<Map<impl Fn(&'? f64) -> f64>>>(Repeat<Map<impl Fn(&'? f64) -> f64>>) -> Vec<{unknown}> + 362..379 'from_i...epeat)': Vec<{unknown}> 372..378 'repeat': Repeat<Map<impl Fn(&'? f64) -> f64>> - 386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>> + 386..389 'vec': Vec<{unknown}> 386..399 'vec.foo_bar()': {unknown} "#]], ); @@ -1224,6 +1227,8 @@ #[test] fn for_loop_block_expr_iterable() { + // FIXME(next-solver): it would be nice to be able to hint `IntoIterator::IntoIter<()>` instead of just `{unknown}` + // (even though `(): IntoIterator` does not hold) check_infer( r#" //- minicore: iterator @@ -1236,17 +1241,17 @@ expect![[r#" 10..68 '{ ... } }': () 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter - 16..66 'for _ ... }': IntoIterator::IntoIter<()> + 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': ! - 16..66 'for _ ... }': IntoIterator::IntoIter<()> - 16..66 'for _ ... }': &'? mut IntoIterator::IntoIter<()> - 16..66 'for _ ... }': fn next<IntoIterator::IntoIter<()>>(&'? mut IntoIterator::IntoIter<()>) -> Option<<IntoIterator::IntoIter<()> as Iterator>::Item> - 16..66 'for _ ... }': Option<IntoIterator::Item<()>> + 16..66 'for _ ... }': {unknown} + 16..66 'for _ ... }': &'? mut {unknown} + 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 16..66 'for _ ... }': Option<{unknown}> 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () - 20..21 '_': IntoIterator::Item<()> + 20..21 '_': {unknown} 25..39 '{ let x = 0; }': () 31..32 'x': i32 35..36 '0': i32 @@ -1283,7 +1288,6 @@ #[test] fn bug_11242() { - // FIXME: wrong, should be u32 check_types( r#" fn foo<A, B>() @@ -1292,7 +1296,7 @@ B: IntoIterator<Item = usize>, { let _x: <A as IntoIterator>::Item; - // ^^ {unknown} + // ^^ u32 } pub trait Iterator { @@ -1495,7 +1499,7 @@ fn regression_11688_3() { check_types( r#" - //- minicore: iterator + //- minicore: iterator, dispatch_from_dyn struct Ar<T, const N: u8>(T); fn f<const LEN: usize, T, const BASE: u8>( num_zeros: usize, @@ -1514,6 +1518,7 @@ fn regression_11688_4() { check_types( r#" + //- minicore: dispatch_from_dyn trait Bar<const C: usize> { fn baz(&self) -> [i32; C]; } @@ -2035,11 +2040,11 @@ r#" fn test() { let x = S::foo::<'static, &()>(&S); - // ^ Wrap<'?, ()> + // ^ Wrap<'static, ()> let x = S::foo::<&()>(&S); // ^ Wrap<'?, ()> let x = S.foo::<'static, &()>(); - // ^ Wrap<'?, ()> + // ^ Wrap<'static, ()> let x = S.foo::<&()>(); // ^ Wrap<'?, ()> } @@ -2349,3 +2354,37 @@ "#]], ); } + +#[test] +fn rust_destruct_option_clone() { + check_types( + r#" +//- minicore: option, drop +fn test(o: &Option<i32>) { + o.my_clone(); + //^^^^^^^^^^^^ Option<i32> +} +pub trait MyClone: Sized { + fn my_clone(&self) -> Self; +} +impl<T> const MyClone for Option<T> +where + T: ~const MyClone + ~const Destruct, +{ + fn my_clone(&self) -> Self { + match self { + Some(x) => Some(x.my_clone()), + None => None, + } + } +} +impl const MyClone for i32 { + fn my_clone(&self) -> Self { + *self + } +} +#[lang = "destruct"] +pub trait Destruct {} +"#, + ); +}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index b154e59..a995a45 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs
@@ -2923,7 +2923,7 @@ // ^^ impl Fn() let c4 = f1(); - // ^^ impl FnOnce() + ?Sized + // ^^ impl FnOnce() f2(|| { 0 }); // ^^^^^^^^ impl FnOnce() -> i32 @@ -3922,7 +3922,7 @@ expect![[r#" 110..127 '{ ...z(); }': () 116..122 'T::baz': fn baz<T>() -> <{unknown} as Foo>::Gat<'?> - 116..124 'T::baz()': Foo::Gat<'?, {unknown}> + 116..124 'T::baz()': {unknown} "#]], ); }
diff --git a/crates/hir-ty/src/tests/trait_aliases.rs b/crates/hir-ty/src/tests/trait_aliases.rs new file mode 100644 index 0000000..302ce55 --- /dev/null +++ b/crates/hir-ty/src/tests/trait_aliases.rs
@@ -0,0 +1,21 @@ +use crate::tests::check_types; + +#[test] +fn projection() { + check_types( + r#" +#![feature(trait_alias)] + +pub trait A { + type Output; +} + +pub trait B = A<Output = u32>; + +pub fn a<T: B>(x: T::Output) { + x; +// ^ u32 +} +"#, + ); +}
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 56e31a1..2e4346a 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs
@@ -1691,7 +1691,7 @@ get2(y); get(set(S)); get2(set(S)); - get2(S::<str>); + get2(S::<usize>); }"#, expect![[r#" 49..50 't': T @@ -1703,7 +1703,7 @@ 166..167 't': T 256..257 'x': T 262..263 'y': impl Trait<Type = i64> - 289..397 '{ ...r>); }': () + 289..399 '{ ...e>); }': () 295..298 'get': fn get<T>(T) -> <T as Trait>::Type 295..301 'get(x)': u32 299..300 'x': T @@ -1726,9 +1726,9 @@ 367..370 'set': fn set<S<u64>>(S<u64>) -> S<u64> 367..373 'set(S)': S<u64> 371..372 'S': S<u64> - 380..384 'get2': fn get2<str, S<str>>(S<str>) -> str - 380..394 'get2(S::<str>)': str - 385..393 'S::<str>': S<str> + 380..384 'get2': fn get2<usize, S<usize>>(S<usize>) -> usize + 380..396 'get2(S...size>)': usize + 385..395 'S::<usize>': S<usize> "#]], ); } @@ -2740,7 +2740,7 @@ fn dyn_trait_through_chalk() { check_types( r#" -//- minicore: deref +//- minicore: deref, unsize, dispatch_from_dyn struct Box<T: ?Sized> {} impl<T: ?Sized> core::ops::Deref for Box<T> { type Target = T; @@ -3228,7 +3228,7 @@ fn infer_dyn_fn_output() { check_types( r#" -//- minicore: fn +//- minicore: fn, dispatch_from_dyn fn foo() { let f: &dyn Fn() -> i32; f(); @@ -4255,8 +4255,8 @@ 127..128 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + '?) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait<Assoc<i32> = &'a i32> + '?) - 170..184 'v.get::<i32>()': &'? i32 - 170..192 'v.get:...eref()': &'? i32 + 170..184 'v.get::<i32>()': {unknown} + 170..192 'v.get:...eref()': &'? {unknown} "#]], ); } @@ -4785,30 +4785,30 @@ fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {} "#, expect![[r#" - 139..140 'f': impl Fn({unknown}) + ?Sized + 139..140 'f': impl Fn({unknown}) 161..193 '{ ...oo); }': () 171..174 'foo': S 177..178 'S': S - 184..185 'f': impl Fn({unknown}) + ?Sized + 184..185 'f': impl Fn({unknown}) 184..190 'f(foo)': () 186..189 'foo': S - 251..252 'f': impl Fn(&'? {unknown}) + ?Sized + 251..252 'f': impl Fn(&'? {unknown}) 274..307 '{ ...oo); }': () 284..287 'foo': S 290..291 'S': S - 297..298 'f': impl Fn(&'? {unknown}) + ?Sized + 297..298 'f': impl Fn(&'? {unknown}) 297..304 'f(&foo)': () 299..303 '&foo': &'? S 300..303 'foo': S - 325..328 'bar': impl Bar<{unknown}> + ?Sized + 325..328 'bar': impl Bar<{unknown}> 350..352 '{}': () - 405..408 'bar': impl Bar<&'? {unknown}> + ?Sized + 405..408 'bar': impl Bar<&'? {unknown}> 431..433 '{}': () - 447..450 'baz': impl Baz<Assoc = impl Foo + ?Sized> + ?Sized + 447..450 'baz': impl Baz<Assoc = impl Foo> 480..482 '{}': () - 500..503 'baz': impl Baz<Assoc = &'a impl Foo + 'a + ?Sized> + ?Sized + 500..503 'baz': impl Baz<Assoc = &'a impl Foo + 'a> 544..546 '{}': () - 560..563 'baz': impl Baz<Assoc = Qux<impl Foo + ?Sized>> + ?Sized + 560..563 'baz': impl Baz<Assoc = Qux<impl Foo>> 598..600 '{}': () "#]], ) @@ -4930,3 +4930,67 @@ "#]], ); } + +#[test] +fn new_solver_crash_1() { + check_infer( + r#" +pub trait Deserializer<'de> { + type Error; +} + +fn deserialize_abs_pathbuf<'de, D>(de: D) -> D::Error +where + D: Deserializer<'de>, +{ +} +"#, + expect![[r#" + 84..86 'de': D + 135..138 '{ }': Deserializer::Error<'de, D> + "#]], + ); +} + +#[test] +fn new_solver_crash_2() { + check_infer( + r#" +//- minicore: deref, send, sync +use core::ops::Deref; + +trait Error {} + +struct AnyhowError; + +impl Deref for AnyhowError { + type Target = dyn Error + Send + Sync; + + fn deref(&self) -> &Self::Target { loop {} } +} + +impl AnyhowError { + fn downcast<T>(self) {} +} + + +fn main() { + let e = AnyhowError; + e.downcast::<()>(); +} +"#, + expect![[r#" + 147..151 'self': &'? AnyhowError + 170..181 '{ loop {} }': &'? (dyn Error + Send + Sync + 'static) + 172..179 'loop {}': ! + 177..179 '{}': () + 223..227 'self': AnyhowError + 229..231 '{}': () + 246..298 '{ ...>(); }': () + 256..257 'e': AnyhowError + 260..271 'AnyhowError': AnyhowError + 277..278 'e': AnyhowError + 277..295 'e.down...<()>()': () + "#]], + ); +}
diff --git a/crates/hir-ty/src/tls.rs b/crates/hir-ty/src/tls.rs index f53409a..fe4cf7a 100644 --- a/crates/hir-ty/src/tls.rs +++ b/crates/hir-ty/src/tls.rs
@@ -10,6 +10,7 @@ }; use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId}; +#[allow(unused)] pub(crate) use unsafe_tls::{set_current_program, with_current_program}; pub(crate) struct DebugContext<'a>(&'a dyn HirDatabase); @@ -136,6 +137,7 @@ if PROGRAM.is_set() { PROGRAM.with(|prog| op(Some(prog))) } else { op(None) } } + #[allow(dead_code)] pub(crate) fn set_current_program<OP, R>(p: &dyn HirDatabase, op: OP) -> R where OP: FnOnce() -> R,
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 7414b4f..51cf5be 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs
@@ -1,43 +1,38 @@ //! Trait solving using Chalk. use core::fmt; -use std::env::var; use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable}; -use chalk_recursive::Cache; -use chalk_solve::{Solver, logging_db::LoggingRustIrDatabase, rust_ir}; +use chalk_solve::rust_ir; use base_db::Crate; use hir_def::{BlockId, TraitId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; +use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt}; +use rustc_type_ir::{ + InferCtxtLike, TypingMode, + inherent::{SliceLike, Span as _}, + solve::Certainty, +}; use span::Edition; -use stdx::{never, panic_context}; +use stdx::never; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy, - ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, db::HirDatabase, - infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder, + AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, + ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, + db::HirDatabase, + infer::unify::InferenceTable, + next_solver::{ + DbInterner, GenericArg, SolverContext, Span, + infer::{DbInternerInferExt, InferCtxt}, + mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, + util::mini_canonicalize, + }, + utils::UnevaluatedConstEvaluatorFolder, }; -/// This controls how much 'time' we give the Chalk solver before giving up. -const CHALK_SOLVER_FUEL: i32 = 1000; - -#[derive(Debug, Copy, Clone)] -pub(crate) struct ChalkContext<'a> { - pub(crate) db: &'a dyn HirDatabase, - pub(crate) krate: Crate, - pub(crate) block: Option<BlockId>, -} - -fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { - let overflow_depth = - var("CHALK_OVERFLOW_DEPTH").ok().and_then(|s| s.parse().ok()).unwrap_or(500); - let max_size = var("CHALK_SOLVER_MAX_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(150); - chalk_recursive::RecursiveSolver::new(overflow_depth, max_size, Some(Cache::new())) -} - /// A set of clauses that we assume to be true. E.g. if we are inside this function: /// ```rust /// fn foo<T: Default>(t: T) {} @@ -103,13 +98,43 @@ table.resolve_completely(ty) } +fn identity_subst( + binders: chalk_ir::CanonicalVarKinds<Interner>, +) -> chalk_ir::Canonical<chalk_ir::Substitution<Interner>> { + let identity_subst = chalk_ir::Substitution::from_iter( + Interner, + binders.iter(Interner).enumerate().map(|(index, c)| { + let index_db = chalk_ir::BoundVar::new(DebruijnIndex::INNERMOST, index); + match &c.kind { + chalk_ir::VariableKind::Ty(_) => { + chalk_ir::GenericArgData::Ty(TyKind::BoundVar(index_db).intern(Interner)) + .intern(Interner) + } + chalk_ir::VariableKind::Lifetime => chalk_ir::GenericArgData::Lifetime( + chalk_ir::LifetimeData::BoundVar(index_db).intern(Interner), + ) + .intern(Interner), + chalk_ir::VariableKind::Const(ty) => chalk_ir::GenericArgData::Const( + chalk_ir::ConstData { + ty: ty.clone(), + value: chalk_ir::ConstValue::BoundVar(index_db), + } + .intern(Interner), + ) + .intern(Interner), + } + }), + ); + chalk_ir::Canonical { binders, value: identity_subst } +} + /// Solve a trait goal using Chalk. pub(crate) fn trait_solve_query( db: &dyn HirDatabase, krate: Crate, block: Option<BlockId>, goal: Canonical<InEnvironment<Goal>>, -) -> Option<Solution> { +) -> NextTraitSolveResult { let _p = tracing::info_span!("trait_solve_query", detail = ?match &goal.value.goal.data(Interner) { GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => db .trait_signature(it.hir_trait_id()) @@ -125,11 +150,113 @@ alias: AliasTy::Projection(projection_ty), .. }))) = &goal.value.goal.data(Interner) + && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) { - if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) { - // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible - return Some(Solution::Ambig(Guidance::Unknown)); + // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible + return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone())); + } + + // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So + // we should get rid of it when talking to chalk. + let goal = goal + .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST) + .unwrap(); + + // We currently don't deal with universes (I think / hope they're not yet + // relevant for our use cases?) + next_trait_solve(db, krate, block, goal) +} + +fn solve_nextsolver<'db>( + db: &'db dyn HirDatabase, + krate: Crate, + block: Option<BlockId>, + goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>, +) -> Result< + (HasChanged, Certainty, rustc_type_ir::Canonical<DbInterner<'db>, Vec<GenericArg<'db>>>), + rustc_type_ir::solve::NoSolution, +> { + // FIXME: should use analysis_in_body, but that needs GenericDefId::Block + let context = SolverContext( + DbInterner::new_with(db, Some(krate), block) + .infer_ctxt() + .build(TypingMode::non_body_analysis()), + ); + + match goal.canonical.value.goal.data(Interner) { + // FIXME: args here should be...what? not empty + GoalData::All(goals) if goals.is_empty(Interner) => { + return Ok((HasChanged::No, Certainty::Yes, mini_canonicalize(context, vec![]))); } + _ => {} + } + + let goal = goal.canonical.to_nextsolver(context.cx()); + 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 vars = + var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); + let canonical_var_values = mini_canonicalize(context, vars); + + let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values)); + + tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); + + res +} + +#[derive(Clone, Debug, PartialEq)] +pub enum NextTraitSolveResult { + Certain(chalk_ir::Canonical<chalk_ir::ConstrainedSubst<Interner>>), + Uncertain(chalk_ir::Canonical<chalk_ir::Substitution<Interner>>), + 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(..)) + } +} + +/// Solve a trait goal using Chalk. +pub fn next_trait_solve( + db: &dyn HirDatabase, + krate: Crate, + block: Option<BlockId>, + goal: Canonical<InEnvironment<Goal>>, +) -> NextTraitSolveResult { + let detail = match &goal.value.goal.data(Interner) { + GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => { + db.trait_signature(it.hir_trait_id()).name.display(db, Edition::LATEST).to_string() + } + GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(), + _ => "??".to_owned(), + }; + let _p = tracing::info_span!("next_trait_solve", ?detail).entered(); + tracing::info!("next_trait_solve({:?})", goal.value.goal); + + if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq { + alias: AliasTy::Projection(projection_ty), + .. + }))) = &goal.value.goal.data(Interner) + && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) + { + // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible + // FIXME + return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone())); } // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So @@ -141,70 +268,44 @@ // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; - solve(db, krate, block, &u_canonical) -} + tracing::info!(?u_canonical); -fn solve( - db: &dyn HirDatabase, - krate: Crate, - block: Option<BlockId>, - goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>, -) -> Option<chalk_solve::Solution<Interner>> { - let _p = tracing::info_span!("solve", ?krate, ?block).entered(); - let context = ChalkContext { db, krate, block }; - tracing::debug!("solve goal: {:?}", goal); - let mut solver = create_chalk_solver(); + let next_solver_res = solve_nextsolver(db, krate, block, &u_canonical); - let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL); - - let should_continue = || { - db.unwind_if_revision_cancelled(); - let remaining = fuel.get(); - fuel.set(remaining - 1); - if remaining == 0 { - tracing::debug!("fuel exhausted"); + match next_solver_res { + Err(_) => NextTraitSolveResult::NoSolution, + Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( + convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args), + ), + Ok((_, Certainty::Maybe(_), args)) => { + let subst = convert_canonical_args_for_result( + DbInterner::new_with(db, Some(krate), block), + args, + ); + NextTraitSolveResult::Uncertain(chalk_ir::Canonical { + binders: subst.binders, + value: subst.value.subst, + }) } - remaining > 0 - }; - - let mut solve = || { - let _ctx = if is_chalk_debug() || is_chalk_print() { - Some(panic_context::enter(format!("solving {goal:?}"))) - } else { - None - }; - let solution = if is_chalk_print() { - let logging_db = - LoggingRustIrDatabaseLoggingOnDrop(LoggingRustIrDatabase::new(context)); - solver.solve_limited(&logging_db.0, goal, &should_continue) - } else { - solver.solve_limited(&context, goal, &should_continue) - }; - - tracing::debug!("solve({:?}) => {:?}", goal, solution); - - solution - }; - - // don't set the TLS for Chalk unless Chalk debugging is active, to make - // extra sure we only use it for debugging - if is_chalk_debug() { crate::tls::set_current_program(db, solve) } else { solve() } -} - -struct LoggingRustIrDatabaseLoggingOnDrop<'a>(LoggingRustIrDatabase<Interner, ChalkContext<'a>>); - -impl Drop for LoggingRustIrDatabaseLoggingOnDrop<'_> { - fn drop(&mut self) { - tracing::info!("chalk program:\n{}", self.0); } } -fn is_chalk_debug() -> bool { - std::env::var("CHALK_DEBUG").is_ok() -} +/// Solve a trait goal using Chalk. +pub fn next_trait_solve_in_ctxt<'db, 'a>( + infer_ctxt: &'a InferCtxt<'db>, + goal: crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>, +) -> Result<(HasChanged, Certainty), rustc_type_ir::solve::NoSolution> { + tracing::info!(?goal); -fn is_chalk_print() -> bool { - std::env::var("CHALK_PRINT").is_ok() + let context = <&SolverContext<'db>>::from(infer_ctxt); + + let res = context.evaluate_root_goal(goal, Span::dummy(), None); + + 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)]
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index d07c1aa..209ec79 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs
@@ -333,13 +333,13 @@ constant: Const, _outer_binder: DebruijnIndex, ) -> Result<Const, Self::Error> { - if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value { - if let ConstScalar::UnevaluatedConst(id, subst) = &c.interned { - if let Ok(eval) = self.db.const_eval(*id, subst.clone(), None) { - return Ok(eval); - } else { - return Ok(unknown_const(constant.data(Interner).ty.clone())); - } + if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value + && let ConstScalar::UnevaluatedConst(id, subst) = &c.interned + { + if let Ok(eval) = self.db.const_eval(*id, subst.clone(), None) { + return Ok(eval); + } else { + return Ok(unknown_const(constant.data(Interner).ty.clone())); } } Ok(constant)
diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs index 08a215f..d8a0585 100644 --- a/crates/hir-ty/src/variance.rs +++ b/crates/hir-ty/src/variance.rs
@@ -990,7 +990,6 @@ ModuleDefId::AdtId(it) => it.into(), ModuleDefId::ConstId(it) => it.into(), ModuleDefId::TraitId(it) => it.into(), - ModuleDefId::TraitAliasId(it) => it.into(), ModuleDefId::TypeAliasId(it) => it.into(), _ => return, }) @@ -1021,10 +1020,6 @@ let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } - GenericDefId::TraitAliasId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } GenericDefId::TypeAliasId(it) => { let loc = it.lookup(&db); loc.source(&db).value.name().unwrap()
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index c68ff70..dfa3938 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml
@@ -22,6 +22,8 @@ triomphe.workspace = true indexmap.workspace = true +ra-ap-rustc_type_ir.workspace = true + # local deps base-db.workspace = true cfg.workspace = true @@ -36,6 +38,9 @@ [dev-dependencies] expect-test.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +tracing-tree.workspace = true # local deps test-utils.workspace = true
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index c8645b6..928753e 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs
@@ -19,7 +19,7 @@ use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, Field, Function, GenericParam, HasCrate, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, - Struct, Trait, TraitAlias, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, + Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, }; pub trait HasAttrs { @@ -48,7 +48,6 @@ (Static, StaticId), (Const, ConstId), (Trait, TraitId), - (TraitAlias, TraitAliasId), (TypeAlias, TypeAliasId), (Macro, MacroId), (Function, FunctionId), @@ -137,7 +136,6 @@ AttrDefId::StaticId(it) => it.resolver(db), AttrDefId::ConstId(it) => it.resolver(db), AttrDefId::TraitId(it) => it.resolver(db), - AttrDefId::TraitAliasId(it) => it.resolver(db), AttrDefId::TypeAliasId(it) => it.resolver(db), AttrDefId::ImplId(it) => it.resolver(db), AttrDefId::ExternBlockId(it) => it.resolver(db), @@ -216,10 +214,6 @@ DocLinkDef::ModuleDef(def) }); } - TypeNs::TraitAliasId(_) => { - // XXX: Do these get resolved? - return None; - } TypeNs::ModuleId(_) => { return None; }
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index c1e814e..fca0162 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs
@@ -604,13 +604,13 @@ } } BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => { - if let Ok(source_ptr) = source_map.expr_syntax(if_expr) { - if let Some(ptr) = source_ptr.value.cast::<ast::IfExpr>() { - return Some( - RemoveUnnecessaryElse { if_expr: InFile::new(source_ptr.file_id, ptr) } - .into(), - ); - } + if let Ok(source_ptr) = source_map.expr_syntax(if_expr) + && let Some(ptr) = source_ptr.value.cast::<ast::IfExpr>() + { + return Some( + RemoveUnnecessaryElse { if_expr: InFile::new(source_ptr.file_id, ptr) } + .into(), + ); } } }
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 2960ebe..b9c5131 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs
@@ -23,8 +23,8 @@ use crate::{ Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, - Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitAlias, TraitRef, TupleField, - TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, + Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, TupleField, TyBuilder, + Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, }; impl HirDisplay for Function { @@ -751,6 +751,7 @@ impl HirDisplay for Trait { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + // FIXME(trait-alias) needs special handling to print the equal sign write_trait_header(self, f)?; let def_id = GenericDefId::TraitId(self.id); let has_where_clause = write_where_clause(def_id, f)?; @@ -802,22 +803,6 @@ Ok(()) } -impl HirDisplay for TraitAlias { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; - let data = f.db.trait_alias_signature(self.id); - write!(f, "trait {}", data.name.display(f.db, f.edition()))?; - let def_id = GenericDefId::TraitAliasId(self.id); - write_generic_params(def_id, f)?; - f.write_str(" = ")?; - // FIXME: Currently we lower every bounds in a trait alias as a trait bound on `Self` i.e. - // `trait Foo = Bar` is stored and displayed as `trait Foo = where Self: Bar`, which might - // be less readable. - write_where_clause(def_id, f)?; - Ok(()) - } -} - impl HirDisplay for TypeAlias { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index c644669..bc025c5 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs
@@ -37,7 +37,6 @@ (hir_def::EnumId, crate::Enum), (hir_def::TypeAliasId, crate::TypeAlias), (hir_def::TraitId, crate::Trait), - (hir_def::TraitAliasId, crate::TraitAlias), (hir_def::StaticId, crate::Static), (hir_def::ConstId, crate::Const), (hir_def::FunctionId, crate::Function), @@ -113,7 +112,6 @@ ModuleDefId::ConstId(it) => ModuleDef::Const(it.into()), ModuleDefId::StaticId(it) => ModuleDef::Static(it.into()), ModuleDefId::TraitId(it) => ModuleDef::Trait(it.into()), - ModuleDefId::TraitAliasId(it) => ModuleDef::TraitAlias(it.into()), ModuleDefId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), ModuleDefId::BuiltinType(it) => ModuleDef::BuiltinType(it.into()), ModuleDefId::MacroId(it) => ModuleDef::Macro(it.into()), @@ -131,7 +129,6 @@ ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()), ModuleDef::Static(it) => ModuleDefId::StaticId(it.into()), ModuleDef::Trait(it) => ModuleDefId::TraitId(it.into()), - ModuleDef::TraitAlias(it) => ModuleDefId::TraitAliasId(it.into()), ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()), ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it.into()), ModuleDef::Macro(it) => ModuleDefId::MacroId(it.into()), @@ -177,7 +174,6 @@ GenericDef::Function(it) => GenericDefId::FunctionId(it.id), GenericDef::Adt(it) => GenericDefId::AdtId(it.into()), GenericDef::Trait(it) => GenericDefId::TraitId(it.id), - GenericDef::TraitAlias(it) => GenericDefId::TraitAliasId(it.id), GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id), GenericDef::Impl(it) => GenericDefId::ImplId(it.id), GenericDef::Const(it) => GenericDefId::ConstId(it.id), @@ -192,7 +188,6 @@ GenericDefId::FunctionId(it) => GenericDef::Function(it.into()), GenericDefId::AdtId(it) => GenericDef::Adt(it.into()), GenericDefId::TraitId(it) => GenericDef::Trait(it.into()), - GenericDefId::TraitAliasId(it) => GenericDef::TraitAlias(it.into()), GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()), GenericDefId::ImplId(it) => GenericDef::Impl(it.into()), GenericDefId::ConstId(it) => GenericDef::Const(it.into()),
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index 4767d47..ae82275 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs
@@ -14,8 +14,7 @@ use crate::{ Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl, InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, - Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, - db::HirDatabase, + Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, db::HirDatabase, }; pub trait HasSource { @@ -168,12 +167,6 @@ Some(self.id.lookup(db).source(db)) } } -impl HasSource for TraitAlias { - type Ast = ast::TraitAlias; - fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> { - Some(self.id.lookup(db).source(db)) - } -} impl HasSource for TypeAlias { type Ast = ast::TypeAlias; fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> { @@ -202,7 +195,7 @@ } impl HasSource for TypeOrConstParam { - type Ast = Either<ast::TypeOrConstParam, ast::TraitOrAlias>; + type Ast = Either<ast::TypeOrConstParam, ast::Trait>; fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> { let child_source = self.id.parent.child_source(db); child_source.map(|it| it.get(self.id.local_id).cloned()).transpose()
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 4ddb04b..4eb50c1 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs
@@ -20,6 +20,12 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![recursion_limit = "512"] +#[cfg(feature = "in-rust-tree")] +extern crate rustc_type_ir; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_type_ir as rustc_type_ir; + mod attrs; mod from_id; mod has_source; @@ -46,7 +52,7 @@ CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, SyntheticSyntax, - TraitAliasId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, + TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, expr_store::{ExpressionStoreDiagnostics, ExpressionStoreSourceMap}, hir::{ BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat, @@ -54,7 +60,10 @@ }, item_tree::ImportAlias, layout::{self, ReprOptions, TargetDataLayout}, - nameres::{self, assoc::TraitItems, diagnostics::DefDiagnostic}, + nameres::{ + assoc::TraitItems, + diagnostics::{DefDiagnostic, DefDiagnosticKind}, + }, per_ns::PerNs, resolver::{HasResolver, Resolver}, signatures::{ImplFlags, StaticFlags, TraitFlags, VariantFields}, @@ -76,11 +85,11 @@ layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution, mir::{MutBorrowKind, interpret_mir}, + next_solver::{DbInterner, GenericArgs, SolverDefId, infer::InferCtxt}, primitive::UintTy, traits::FnTrait, }; use itertools::Itertools; -use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::{AstIdNode, Edition, FileId}; @@ -187,6 +196,10 @@ } impl Crate { + pub fn base(self) -> base_db::Crate { + self.id + } + pub fn origin(self, db: &dyn HirDatabase) -> CrateOrigin { self.id.data(db).origin.clone() } @@ -320,7 +333,6 @@ Const(Const), Static(Static), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), BuiltinType(BuiltinType), Macro(Macro), @@ -333,7 +345,6 @@ Const, Static, Trait, - TraitAlias, TypeAlias, BuiltinType, Macro @@ -360,7 +371,6 @@ ModuleDef::Const(it) => Some(it.module(db)), ModuleDef::Static(it) => Some(it.module(db)), ModuleDef::Trait(it) => Some(it.module(db)), - ModuleDef::TraitAlias(it) => Some(it.module(db)), ModuleDef::TypeAlias(it) => Some(it.module(db)), ModuleDef::Macro(it) => Some(it.module(db)), ModuleDef::BuiltinType(_) => None, @@ -389,7 +399,6 @@ ModuleDef::Const(it) => it.name(db)?, ModuleDef::Adt(it) => it.name(db), ModuleDef::Trait(it) => it.name(db), - ModuleDef::TraitAlias(it) => it.name(db), ModuleDef::Function(it) => it.name(db), ModuleDef::Variant(it) => it.name(db), ModuleDef::TypeAlias(it) => it.name(db), @@ -412,7 +421,6 @@ Adt::Union(it) => it.id.into(), }, ModuleDef::Trait(it) => it.id.into(), - ModuleDef::TraitAlias(it) => it.id.into(), ModuleDef::Function(it) => it.id.into(), ModuleDef::TypeAlias(it) => it.id.into(), ModuleDef::Module(it) => it.id.into(), @@ -452,7 +460,6 @@ ModuleDef::Module(_) | ModuleDef::Adt(_) | ModuleDef::Trait(_) - | ModuleDef::TraitAlias(_) | ModuleDef::TypeAlias(_) | ModuleDef::Macro(_) | ModuleDef::BuiltinType(_) => None, @@ -465,7 +472,6 @@ ModuleDef::Function(it) => Some(it.into()), ModuleDef::Adt(it) => Some(it.into()), ModuleDef::Trait(it) => Some(it.into()), - ModuleDef::TraitAlias(it) => Some(it.into()), ModuleDef::TypeAlias(it) => Some(it.into()), ModuleDef::Module(_) | ModuleDef::Variant(_) @@ -485,7 +491,6 @@ ModuleDef::Const(it) => it.attrs(db), ModuleDef::Static(it) => it.attrs(db), ModuleDef::Trait(it) => it.attrs(db), - ModuleDef::TraitAlias(it) => it.attrs(db), ModuleDef::TypeAlias(it) => it.attrs(db), ModuleDef::Macro(it) => it.attrs(db), ModuleDef::BuiltinType(_) => return None, @@ -511,7 +516,6 @@ ModuleDef::Const(it) => it.visibility(db), ModuleDef::Static(it) => it.visibility(db), ModuleDef::Trait(it) => it.visibility(db), - ModuleDef::TraitAlias(it) => it.visibility(db), ModuleDef::TypeAlias(it) => it.visibility(db), ModuleDef::Variant(it) => it.visibility(db), ModuleDef::Macro(it) => it.visibility(db), @@ -1020,21 +1024,21 @@ m: Macro, ) { let id = db.macro_def(m.id); - if let hir_expand::db::TokenExpander::DeclarativeMacro(expander) = db.macro_expander(id) { - if let Some(e) = expander.mac.err() { - let Some(ast) = id.ast_id().left() else { - never!("declarative expander for non decl-macro: {:?}", e); - return; - }; - let krate = HasModule::krate(&m.id, db); - let edition = krate.data(db).edition; - emit_def_diagnostic_( - db, - acc, - &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() }, - edition, - ); - } + if let hir_expand::db::TokenExpander::DeclarativeMacro(expander) = db.macro_expander(id) + && let Some(e) = expander.mac.err() + { + let Some(ast) = id.ast_id().left() else { + never!("declarative expander for non decl-macro: {:?}", e); + return; + }; + let krate = HasModule::krate(&m.id, db); + let edition = krate.data(db).edition; + emit_def_diagnostic_( + db, + acc, + &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() }, + edition, + ); } } @@ -1247,6 +1251,25 @@ pub(crate) id: LocalFieldId, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedField<'db> { + pub(crate) inner: Field, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedField<'db> { + /// Returns the type as in the signature of the struct. + pub fn ty(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { + let krate = self.inner.krate(db); + let interner = DbInterner::new_with(db, Some(krate.base()), None); + + let var_id = self.inner.parent.into(); + let field = db.field_types_ns(var_id)[self.inner.id]; + let ty = field.instantiate(interner, self.args); + TypeNs::new(db, var_id, ty) + } +} + #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct TupleField { pub owner: DefWithBodyId, @@ -1444,6 +1467,11 @@ pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { db.attrs(self.id.into()).is_unstable() } + + pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> { + let args = infer_ctxt.fresh_args_for_item(self.id.into()); + InstantiatedStruct { inner: self, args } + } } impl HasVisibility for Struct { @@ -1455,6 +1483,35 @@ } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedStruct<'db> { + pub(crate) inner: Struct, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedStruct<'db> { + pub fn fields(self, db: &dyn HirDatabase) -> Vec<InstantiatedField<'db>> { + self.inner + .id + .fields(db) + .fields() + .iter() + .map(|(id, _)| InstantiatedField { + inner: Field { parent: self.inner.into(), id }, + args: self.args, + }) + .collect() + } + + pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { + let krate = self.inner.krate(db); + let interner = DbInterner::new_with(db, Some(krate.base()), None); + + let ty = db.ty_ns(self.inner.id.into()); + TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Union { pub(crate) id: UnionId, } @@ -1598,6 +1655,22 @@ } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedEnum<'db> { + pub(crate) inner: Enum, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedEnum<'db> { + pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { + let krate = self.inner.krate(db); + let interner = DbInterner::new_with(db, Some(krate.base()), None); + + let ty = db.ty_ns(self.inner.id.into()); + TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args)) + } +} + impl From<&Variant> for DefWithBodyId { fn from(&v: &Variant) -> Self { DefWithBodyId::VariantId(v.into()) @@ -1673,6 +1746,38 @@ pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { db.attrs(self.id.into()).is_unstable() } + + 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()); + InstantiatedVariant { inner: self, args } + } +} + +// FIXME: Rename to `EnumVariant` +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedVariant<'db> { + pub(crate) inner: Variant, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedVariant<'db> { + pub fn parent_enum(self, db: &dyn HirDatabase) -> InstantiatedEnum<'db> { + InstantiatedEnum { inner: self.inner.id.lookup(db).parent.into(), args: self.args } + } + + pub fn fields(self, db: &dyn HirDatabase) -> Vec<InstantiatedField<'db>> { + self.inner + .id + .fields(db) + .fields() + .iter() + .map(|(id, _)| InstantiatedField { + inner: Field { parent: self.inner.into(), id }, + args: self.args, + }) + .collect() + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -2564,10 +2669,10 @@ Callee::Closure(closure, _) => { let c = db.lookup_intern_closure(closure.into()); let body = db.body(c.0); - if let Expr::Closure { args, .. } = &body[c.1] { - if let Pat::Bind { id, .. } = &body[args[self.idx]] { - return Some(Local { parent: c.0, binding_id: *id }); - } + if let Expr::Closure { args, .. } = &body[c.1] + && let Pat::Bind { id, .. } = &body[args[self.idx]] + { + return Some(Local { parent: c.0, binding_id: *id }); } None } @@ -2761,26 +2866,20 @@ pub fn render_debug(&self, db: &dyn HirDatabase) -> Result<String, MirEvalError> { let data = self.const_.data(Interner); - if let TyKind::Scalar(s) = data.ty.kind(Interner) { - if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) { - if let hir_ty::ConstValue::Concrete(c) = &data.value { - if let hir_ty::ConstScalar::Bytes(b, _) = &c.interned { - let value = u128::from_le_bytes(mir::pad16(b, false)); - let value_signed = - i128::from_le_bytes(mir::pad16(b, matches!(s, Scalar::Int(_)))); - let mut result = if let Scalar::Int(_) = s { - value_signed.to_string() - } else { - value.to_string() - }; - if value >= 10 { - format_to!(result, " ({value:#X})"); - return Ok(result); - } else { - return Ok(result); - } - } - } + if let TyKind::Scalar(s) = data.ty.kind(Interner) + && matches!(s, Scalar::Int(_) | Scalar::Uint(_)) + && let hir_ty::ConstValue::Concrete(c) = &data.value + && let hir_ty::ConstScalar::Bytes(b, _) = &c.interned + { + let value = u128::from_le_bytes(mir::pad16(b, false)); + let value_signed = i128::from_le_bytes(mir::pad16(b, matches!(s, Scalar::Int(_)))); + let mut result = + if let Scalar::Int(_) = s { value_signed.to_string() } else { value.to_string() }; + if value >= 10 { + format_to!(result, " ({value:#X})"); + return Ok(result); + } else { + return Ok(result); } } mir::render_const_using_debug_impl(db, self.def, &self.const_) @@ -2938,29 +3037,6 @@ } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct TraitAlias { - pub(crate) id: TraitAliasId, -} - -impl TraitAlias { - pub fn module(self, db: &dyn HirDatabase) -> Module { - Module { id: self.id.lookup(db).container } - } - - pub fn name(self, db: &dyn HirDatabase) -> Name { - db.trait_alias_signature(self.id).name.clone() - } -} - -impl HasVisibility for TraitAlias { - fn visibility(&self, db: &dyn HirDatabase) -> Visibility { - let loc = self.id.lookup(db); - let source = loc.source(db); - visibility_from_ast(db, self.id, source.map(|src| src.visibility())) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TypeAlias { pub(crate) id: TypeAliasId, } @@ -3582,7 +3658,6 @@ Function(Function), Adt(Adt), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), Impl(Impl), // consts can have type parameters from their parents (i.e. associated consts of traits) @@ -3593,7 +3668,6 @@ Function, Adt(Struct, Enum, Union), Trait, - TraitAlias, TypeAlias, Impl, Const, @@ -3643,7 +3717,6 @@ GenericDef::Function(it) => it.id.into(), GenericDef::Adt(it) => it.into(), GenericDef::Trait(it) => it.id.into(), - GenericDef::TraitAlias(it) => it.id.into(), GenericDef::TypeAlias(it) => it.id.into(), GenericDef::Impl(it) => it.id.into(), GenericDef::Const(it) => it.id.into(), @@ -3668,7 +3741,6 @@ GenericDefId::FunctionId(it) => db.function_signature_with_source_map(it).1, GenericDefId::ImplId(it) => db.impl_signature_with_source_map(it).1, GenericDefId::StaticId(_) => return, - GenericDefId::TraitAliasId(it) => db.trait_alias_signature_with_source_map(it).1, GenericDefId::TraitId(it) => db.trait_signature_with_source_map(it).1, GenericDefId::TypeAliasId(it) => db.type_alias_signature_with_source_map(it).1, }; @@ -3705,7 +3777,6 @@ GenericDef::Adt(Adt::Enum(_)) => "enum", GenericDef::Adt(Adt::Union(_)) => "union", GenericDef::Trait(_) => "trait", - GenericDef::TraitAlias(_) => "trait alias", GenericDef::TypeAlias(_) => "type alias", GenericDef::Impl(_) => "impl", GenericDef::Const(_) => "constant", @@ -4421,10 +4492,10 @@ let impls = db.trait_impls_in_crate(id); all.extend(impls.for_trait(trait_.id).map(Self::from)) } - if let Some(block) = module.id.containing_block() { - if let Some(trait_impls) = db.trait_impls_in_block(block) { - all.extend(trait_impls.for_trait(trait_.id).map(Self::from)); - } + if let Some(block) = module.id.containing_block() + && let Some(trait_impls) = db.trait_impls_in_block(block) + { + all.extend(trait_impls.for_trait(trait_.id).map(Self::from)); } all } @@ -5078,7 +5149,7 @@ binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(self.env.krate, self.env.block, goal).is_some() + !db.trait_solve(self.env.krate, self.env.block, goal).no_solution() } pub fn normalize_trait_assoc_type( @@ -5833,6 +5904,55 @@ } } +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct TypeNs<'db> { + env: Arc<TraitEnvironment>, + ty: hir_ty::next_solver::Ty<'db>, + _pd: PhantomCovariantLifetime<'db>, +} + +impl<'db> TypeNs<'db> { + fn new( + db: &'db dyn HirDatabase, + lexical_env: impl HasResolver, + ty: hir_ty::next_solver::Ty<'db>, + ) -> Self { + let resolver = lexical_env.resolver(db); + let environment = resolver + .generic_def() + .map_or_else(|| TraitEnvironment::empty(resolver.krate()), |d| db.trait_environment(d)); + TypeNs { env: environment, ty, _pd: PhantomCovariantLifetime::new() } + } + + // FIXME: Find better API that also handles const generics + pub fn impls_trait(&self, infcx: InferCtxt<'db>, trait_: Trait, args: &[TypeNs<'db>]) -> bool { + let args = GenericArgs::new_from_iter( + infcx.interner, + [self.ty].into_iter().chain(args.iter().map(|t| t.ty)).map(|t| t.into()), + ); + let trait_ref = hir_ty::next_solver::TraitRef::new( + infcx.interner, + SolverDefId::TraitId(trait_.id), + 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( + infcx.interner, + hir_ty::next_solver::ParamEnv::empty(), + predicate, + ); + 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)) + } +} + #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct InlineAsmOperand { owner: DefWithBodyId, @@ -6244,12 +6364,6 @@ } } -impl HasCrate for TraitAlias { - fn krate(&self, db: &dyn HirDatabase) -> Crate { - self.module(db).krate() - } -} - impl HasCrate for Static { fn krate(&self, db: &dyn HirDatabase) -> Crate { self.module(db).krate() @@ -6343,12 +6457,6 @@ } } -impl HasContainer for TraitAlias { - fn container(&self, db: &dyn HirDatabase) -> ItemContainer { - ItemContainer::Module(Module { id: self.id.lookup(db).container }) - } -} - impl HasContainer for ExternBlock { fn container(&self, db: &dyn HirDatabase) -> ItemContainer { ItemContainer::Module(Module { id: self.id.lookup(db).container }) @@ -6482,3 +6590,6 @@ fn as_name_opt(name: Option<impl AsName>) -> Name { name.map_or_else(Name::missing, |name| name.as_name()) } + +pub use hir_ty::next_solver; +pub use hir_ty::setup_tracing;
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index adba592..b43165f 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs
@@ -46,8 +46,8 @@ Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam, Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, - Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, - Type, TypeAlias, TypeParam, Union, Variant, VariantDef, + Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TupleField, Type, + TypeAlias, TypeParam, Union, Variant, VariantDef, db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{SourceAnalyzer, name_hygiene, resolve_hir_path}, @@ -85,8 +85,7 @@ | ModuleDef::Function(_) | ModuleDef::Module(_) | ModuleDef::Static(_) - | ModuleDef::Trait(_) - | ModuleDef::TraitAlias(_), + | ModuleDef::Trait(_), ) => None, PathResolution::Def(ModuleDef::TypeAlias(alias)) => { Some(TypeNs::TypeAliasId((*alias).into())) @@ -343,10 +342,6 @@ self.imp.to_def(s) } - pub fn to_trait_alias_def(&self, t: &ast::TraitAlias) -> Option<TraitAlias> { - self.imp.to_def(t) - } - pub fn to_trait_def(&self, t: &ast::Trait) -> Option<Trait> { self.imp.to_def(t) } @@ -933,19 +928,18 @@ InFile::new(file.file_id, last), false, &mut |InFile { value: last, file_id: last_fid }, _ctx| { - if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() { - if first_fid == last_fid { - if let Some(p) = first.parent() { - let range = first.text_range().cover(last.text_range()); - let node = find_root(&p) - .covering_element(range) - .ancestors() - .take_while(|it| it.text_range() == range) - .find_map(N::cast); - if let Some(node) = node { - res.push(node); - } - } + if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() + && first_fid == last_fid + && let Some(p) = first.parent() + { + let range = first.text_range().cover(last.text_range()); + let node = find_root(&p) + .covering_element(range) + .ancestors() + .take_while(|it| it.text_range() == range) + .find_map(N::cast); + if let Some(node) = node { + res.push(node); } } }, @@ -1242,29 +1236,27 @@ adt, )) })?; - let mut res = None; for (_, derive_attr, derives) in derives { // as there may be multiple derives registering the same helper // name, we gotta make sure to call this for all of them! // FIXME: We need to call `f` for all of them as well though! - res = res.or(process_expansion_for_token( - ctx, - &mut stack, - derive_attr, - )); + process_expansion_for_token(ctx, &mut stack, derive_attr); for derive in derives.into_iter().flatten() { - res = res - .or(process_expansion_for_token(ctx, &mut stack, derive)); + process_expansion_for_token(ctx, &mut stack, derive); } } // remove all tokens that are within the derives expansion filter_duplicates(tokens, adt.syntax().text_range()); - Some(res) + Some(()) }); // if we found derives, we can early exit. There is no way we can be in any // macro call at this point given we are not in a token tree - if let Some(res) = res { - return res; + if let Some(()) = res { + // Note: derives do not remap the original token. Furthermore, we want + // the original token to be before the derives in the list, because if they + // upmap to the same token and we deduplicate them (e.g. in rename), we + // want the original token to remain, not the derive. + return None; } } // Then check for token trees, that means we are either in a function-like macro or @@ -1391,10 +1383,10 @@ } })() .is_none(); - if was_not_remapped { - if let ControlFlow::Break(b) = f(InFile::new(expansion, token), ctx) { - return Some(b); - } + if was_not_remapped + && let ControlFlow::Break(b) = f(InFile::new(expansion, token), ctx) + { + return Some(b); } } } @@ -2068,14 +2060,12 @@ break false; } - if let Some(parent) = ast::Expr::cast(parent.clone()) { - if let Some(ExprOrPatId::ExprId(expr_id)) = + if let Some(parent) = ast::Expr::cast(parent.clone()) + && let Some(ExprOrPatId::ExprId(expr_id)) = source_map.node_expr(InFile { file_id, value: &parent }) - { - if let Expr::Unsafe { .. } = body[expr_id] { - break true; - } - } + && let Expr::Unsafe { .. } = body[expr_id] + { + break true; } let Some(parent_) = parent.parent() else { break false }; @@ -2143,7 +2133,6 @@ (crate::Enum, ast::Enum, enum_to_def), (crate::Union, ast::Union, union_to_def), (crate::Trait, ast::Trait, trait_to_def), - (crate::TraitAlias, ast::TraitAlias, trait_alias_to_def), (crate::Impl, ast::Impl, impl_to_def), (crate::TypeAlias, ast::TypeAlias, type_alias_to_def), (crate::Const, ast::Const, const_to_def), @@ -2354,32 +2343,30 @@ impl RenameConflictsVisitor<'_> { fn resolve_path(&mut self, node: ExprOrPatId, path: &Path) { - if let Path::BarePath(path) = path { - if let Some(name) = path.as_ident() { - if *name.symbol() == self.new_name { - if let Some(conflicting) = self.resolver.rename_will_conflict_with_renamed( - self.db, - name, - path, - self.body.expr_or_pat_path_hygiene(node), - self.to_be_renamed, - ) { - self.conflicts.insert(conflicting); - } - } else if *name.symbol() == self.old_name { - if let Some(conflicting) = - self.resolver.rename_will_conflict_with_another_variable( - self.db, - name, - path, - self.body.expr_or_pat_path_hygiene(node), - &self.new_name, - self.to_be_renamed, - ) - { - self.conflicts.insert(conflicting); - } + if let Path::BarePath(path) = path + && let Some(name) = path.as_ident() + { + if *name.symbol() == self.new_name { + if let Some(conflicting) = self.resolver.rename_will_conflict_with_renamed( + self.db, + name, + path, + self.body.expr_or_pat_path_hygiene(node), + self.to_be_renamed, + ) { + self.conflicts.insert(conflicting); } + } else if *name.symbol() == self.old_name + && let Some(conflicting) = self.resolver.rename_will_conflict_with_another_variable( + self.db, + name, + path, + self.body.expr_or_pat_path_hygiene(node), + &self.new_name, + self.to_be_renamed, + ) + { + self.conflicts.insert(conflicting); } } }
diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs index e7db93d..5019a59 100644 --- a/crates/hir/src/semantics/child_by_source.rs +++ b/crates/hir/src/semantics/child_by_source.rs
@@ -154,9 +154,6 @@ } ModuleDefId::StaticId(id) => insert_item_loc(db, map, file_id, id, keys::STATIC), ModuleDefId::TraitId(id) => insert_item_loc(db, map, file_id, id, keys::TRAIT), - ModuleDefId::TraitAliasId(id) => { - insert_item_loc(db, map, file_id, id, keys::TRAIT_ALIAS) - } ModuleDefId::AdtId(adt) => match adt { AdtId::StructId(id) => insert_item_loc(db, map, file_id, id, keys::STRUCT), AdtId::UnionId(id) => insert_item_loc(db, map, file_id, id, keys::UNION),
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 71ee0f6..44df4d8 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs
@@ -89,8 +89,8 @@ use hir_def::{ AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, - Lookup, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, - UnionId, UseId, VariantId, + Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, + UseId, VariantId, dyn_map::{ DynMap, keys::{self, Key}, @@ -252,12 +252,6 @@ pub(super) fn trait_to_def(&mut self, src: InFile<&ast::Trait>) -> Option<TraitId> { self.to_def(src, keys::TRAIT) } - pub(super) fn trait_alias_to_def( - &mut self, - src: InFile<&ast::TraitAlias>, - ) -> Option<TraitAliasId> { - self.to_def(src, keys::TRAIT_ALIAS) - } pub(super) fn impl_to_def(&mut self, src: InFile<&ast::Impl>) -> Option<ImplId> { self.to_def(src, keys::IMPL) } @@ -555,9 +549,6 @@ } ast::Item::Enum(it) => this.enum_to_def(InFile::new(file_id, it)).map(Into::into), ast::Item::Trait(it) => this.trait_to_def(InFile::new(file_id, it)).map(Into::into), - ast::Item::TraitAlias(it) => { - this.trait_alias_to_def(InFile::new(file_id, it)).map(Into::into) - } ast::Item::TypeAlias(it) => { this.type_alias_to_def(InFile::new(file_id, it)).map(Into::into) } @@ -636,9 +627,6 @@ ast::Item::TypeAlias(it) => ChildContainer::GenericDefId( self.type_alias_to_def(container.with_value(it))?.into(), ), - ast::Item::TraitAlias(it) => ChildContainer::GenericDefId( - self.trait_alias_to_def(container.with_value(it))?.into(), - ), ast::Item::Struct(it) => { let def = self.struct_to_def(container.with_value(it))?; let is_in_body = it.field_list().is_some_and(|it| {
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 0b554a9..126392a 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs
@@ -10,7 +10,7 @@ use crate::{ Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait, - TraitAlias, TupleField, Type, TypeAlias, Variant, + TupleField, Type, TypeAlias, Variant, db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; @@ -995,11 +995,11 @@ // Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are // trying to resolve foo::bar. - if let Some(use_tree) = parent().and_then(ast::UseTree::cast) { - if use_tree.coloncolon_token().is_some() { - return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &store) - .map(|it| (it, None)); - } + if let Some(use_tree) = parent().and_then(ast::UseTree::cast) + && use_tree.coloncolon_token().is_some() + { + return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &store) + .map(|it| (it, None)); } let meta_path = path @@ -1035,24 +1035,19 @@ // } // ``` Some(it) if matches!(it, PathResolution::Def(ModuleDef::BuiltinType(_))) => { - if let Some(mod_path) = hir_path.mod_path() { - if let Some(ModuleDefId::ModuleId(id)) = + if let Some(mod_path) = hir_path.mod_path() + && let Some(ModuleDefId::ModuleId(id)) = self.resolver.resolve_module_path_in_items(db, mod_path).take_types() + { + let parent_hir_name = parent_hir_path.segments().get(1).map(|it| it.name); + let module = crate::Module { id }; + if module + .scope(db, None) + .into_iter() + .any(|(name, _)| Some(&name) == parent_hir_name) { - let parent_hir_name = - parent_hir_path.segments().get(1).map(|it| it.name); - let module = crate::Module { id }; - if module - .scope(db, None) - .into_iter() - .any(|(name, _)| Some(&name) == parent_hir_name) - { - return Some(( - PathResolution::Def(ModuleDef::Module(module)), - None, - )); - }; - } + return Some((PathResolution::Def(ModuleDef::Module(module)), None)); + }; } Some((it, None)) } @@ -1282,22 +1277,22 @@ db: &'db dyn HirDatabase, macro_expr: InFile<&ast::MacroExpr>, ) -> bool { - if let Some((def, body, sm, Some(infer))) = self.body_() { - if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) { - let mut is_unsafe = false; - let mut walk_expr = |expr_id| { - unsafe_operations(db, infer, def, body, expr_id, &mut |inside_unsafe_block| { - is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No - }) - }; - 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) - } + if let Some((def, body, sm, Some(infer))) = self.body_() + && let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) + { + let mut is_unsafe = false; + let mut walk_expr = |expr_id| { + unsafe_operations(db, infer, def, body, expr_id, &mut |inside_unsafe_block| { + is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No + }) + }; + 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) } - return is_unsafe; } + return is_unsafe; } false } @@ -1575,12 +1570,11 @@ // If we are in a TypeNs for a Trait, and we have an unresolved name, try to resolve it as a type // within the trait's associated types. - if let (Some(unresolved), &TypeNs::TraitId(trait_id)) = (&unresolved, &ty) { - if let Some(type_alias_id) = + if let (Some(unresolved), &TypeNs::TraitId(trait_id)) = (&unresolved, &ty) + && let Some(type_alias_id) = trait_id.trait_items(db).associated_type_by_name(unresolved.name) - { - return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into())); - } + { + return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into())); } let res = match ty { @@ -1593,7 +1587,6 @@ TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), - TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()), TypeNs::ModuleId(it) => PathResolution::Def(ModuleDef::Module(it.into())), }; match unresolved { @@ -1726,12 +1719,11 @@ // If we are in a TypeNs for a Trait, and we have an unresolved name, try to resolve it as a type // within the trait's associated types. - if let (Some(unresolved), &TypeNs::TraitId(trait_id)) = (&unresolved, &ty) { - if let Some(type_alias_id) = + if let (Some(unresolved), &TypeNs::TraitId(trait_id)) = (&unresolved, &ty) + && let Some(type_alias_id) = trait_id.trait_items(db).associated_type_by_name(unresolved.name) - { - return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into())); - } + { + return Some(PathResolution::Def(ModuleDefId::from(type_alias_id).into())); } let res = match ty { @@ -1744,7 +1736,6 @@ TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), - TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()), TypeNs::ModuleId(it) => PathResolution::Def(ModuleDef::Module(it.into())), }; match unresolved {
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index dca1019..d8c624e 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs
@@ -148,9 +148,6 @@ let trait_do_not_complete = this.push_decl(id, name, false, None); this.collect_from_trait(id, trait_do_not_complete); } - ModuleDefId::TraitAliasId(id) => { - this.push_decl(id, name, false, None); - } ModuleDefId::TypeAliasId(id) => { this.push_decl(id, name, false, None); }
diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs index 4b354e6..e408921 100644 --- a/crates/hir/src/term_search.rs +++ b/crates/hir/src/term_search.rs
@@ -122,10 +122,10 @@ } // Collapse suggestions if there are many - if let Some(res) = &res { - if res.len() > self.many_threshold { - return Some(vec![Expr::Many(ty.clone())]); - } + if let Some(res) = &res + && res.len() > self.many_threshold + { + return Some(vec![Expr::Many(ty.clone())]); } res @@ -160,10 +160,10 @@ } // Collapse suggestions if there are many - if let Some(res) = &res { - if res.len() > self.many_threshold { - return Some(vec![Expr::Many(ty.clone())]); - } + if let Some(res) = &res + && res.len() > self.many_threshold + { + return Some(vec![Expr::Many(ty.clone())]); } res
diff --git a/crates/hir/src/term_search/expr.rs b/crates/hir/src/term_search/expr.rs index 8438319..78f534d 100644 --- a/crates/hir/src/term_search/expr.rs +++ b/crates/hir/src/term_search/expr.rs
@@ -336,10 +336,10 @@ if let Expr::Method { func, params, .. } = self { res.extend(params.iter().flat_map(|it| it.traits_used(db))); - if let Some(it) = func.as_assoc_item(db) { - if let Some(it) = it.container_or_implemented_trait(db) { - res.push(it); - } + if let Some(it) = func.as_assoc_item(db) + && let Some(it) = it.container_or_implemented_trait(db) + { + res.push(it); } }
diff --git a/crates/ide-assists/src/handlers/add_lifetime_to_type.rs b/crates/ide-assists/src/handlers/add_lifetime_to_type.rs index dcdc7ea..27dbdcf 100644 --- a/crates/ide-assists/src/handlers/add_lifetime_to_type.rs +++ b/crates/ide-assists/src/handlers/add_lifetime_to_type.rs
@@ -82,10 +82,10 @@ record_field_list .fields() .filter_map(|r_field| { - if let ast::Type::RefType(ref_type) = r_field.ty()? { - if ref_type.lifetime().is_none() { - return Some(ref_type); - } + if let ast::Type::RefType(ref_type) = r_field.ty()? + && ref_type.lifetime().is_none() + { + return Some(ref_type); } None @@ -102,10 +102,10 @@ ast::FieldList::RecordFieldList(record_list) => record_list .fields() .filter_map(|f| { - if let ast::Type::RefType(ref_type) = f.ty()? { - if ref_type.lifetime().is_none() { - return Some(ref_type); - } + if let ast::Type::RefType(ref_type) = f.ty()? + && ref_type.lifetime().is_none() + { + return Some(ref_type); } None @@ -114,10 +114,10 @@ ast::FieldList::TupleFieldList(tuple_field_list) => tuple_field_list .fields() .filter_map(|f| { - if let ast::Type::RefType(ref_type) = f.ty()? { - if ref_type.lifetime().is_none() { - return Some(ref_type); - } + if let ast::Type::RefType(ref_type) = f.ty()? + && ref_type.lifetime().is_none() + { + return Some(ref_type); } None
diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 9f9d219..7e03eb3 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -2,6 +2,7 @@ use syntax::{ Edition, ast::{self, AstNode, make}, + syntax_editor::{Position, SyntaxEditor}, }; use crate::{ @@ -147,45 +148,74 @@ let target = impl_def.syntax().text_range(); acc.add(AssistId::quick_fix(assist_id), label, target, |edit| { - let new_impl_def = edit.make_mut(impl_def.clone()); - let first_new_item = add_trait_assoc_items_to_impl( + let new_item = add_trait_assoc_items_to_impl( &ctx.sema, ctx.config, &missing_items, trait_, - &new_impl_def, + &impl_def, &target_scope, ); + let Some((first_new_item, other_items)) = new_item.split_first() else { + return; + }; + + let mut first_new_item = if let DefaultMethods::No = mode + && let ast::AssocItem::Fn(func) = &first_new_item + && let Some(body) = try_gen_trait_body( + ctx, + func, + trait_ref, + &impl_def, + target_scope.krate().edition(ctx.sema.db), + ) + && let Some(func_body) = func.body() + { + let mut func_editor = SyntaxEditor::new(first_new_item.syntax().clone_subtree()); + func_editor.replace(func_body.syntax(), body.syntax()); + ast::AssocItem::cast(func_editor.finish().new_root().clone()) + } else { + Some(first_new_item.clone()) + }; + + let new_assoc_items = first_new_item + .clone() + .into_iter() + .chain(other_items.iter().cloned()) + .collect::<Vec<_>>(); + + let mut editor = edit.make_editor(impl_def.syntax()); + if let Some(assoc_item_list) = impl_def.assoc_item_list() { + assoc_item_list.add_items(&mut editor, new_assoc_items); + } else { + let assoc_item_list = make::assoc_item_list(Some(new_assoc_items)).clone_for_update(); + editor.insert_all( + Position::after(impl_def.syntax()), + vec![make::tokens::whitespace(" ").into(), assoc_item_list.syntax().clone().into()], + ); + first_new_item = assoc_item_list.assoc_items().next(); + } + if let Some(cap) = ctx.config.snippet_cap { let mut placeholder = None; - if let DefaultMethods::No = mode { - if let ast::AssocItem::Fn(func) = &first_new_item { - if try_gen_trait_body( - ctx, - func, - trait_ref, - &impl_def, - target_scope.krate().edition(ctx.sema.db), - ) - .is_none() - { - if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) - { - if m.syntax().text() == "todo!()" { - placeholder = Some(m); - } - } - } - } + if let DefaultMethods::No = mode + && let Some(ast::AssocItem::Fn(func)) = &first_new_item + && let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) + && m.syntax().text() == "todo!()" + { + placeholder = Some(m); } if let Some(macro_call) = placeholder { - edit.add_placeholder_snippet(cap, macro_call); - } else { - edit.add_tabstop_before(cap, first_new_item); + let placeholder = edit.make_placeholder_snippet(cap); + editor.add_annotation(macro_call.syntax(), placeholder); + } else if let Some(first_new_item) = first_new_item { + let tabstop = edit.make_tabstop_before(cap); + editor.add_annotation(first_new_item.syntax(), tabstop); }; }; + edit.add_file_edits(ctx.vfs_file_id(), editor); }) } @@ -195,7 +225,7 @@ trait_ref: hir::TraitRef<'_>, impl_def: &ast::Impl, edition: Edition, -) -> Option<()> { +) -> Option<ast::BlockExpr> { let trait_path = make::ext::ident_path( &trait_ref.trait_().name(ctx.db()).display(ctx.db(), edition).to_string(), ); @@ -322,7 +352,7 @@ } #[test] - fn test_impl_def_without_braces() { + fn test_impl_def_without_braces_macro() { check_assist( add_missing_impl_members, r#" @@ -341,6 +371,33 @@ } #[test] + fn test_impl_def_without_braces_tabstop_first_item() { + check_assist( + add_missing_impl_members, + r#" +trait Foo { + type Output; + fn foo(&self); +} +struct S; +impl Foo for S { $0 }"#, + r#" +trait Foo { + type Output; + fn foo(&self); +} +struct S; +impl Foo for S { + $0type Output; + + fn foo(&self) { + todo!() + } +}"#, + ); + } + + #[test] fn fill_in_type_params_1() { check_assist( add_missing_impl_members, @@ -2364,4 +2421,53 @@ }"#, ); } + + #[test] + fn test_qualify_ident_pat_in_default_members() { + check_assist( + add_missing_default_members, + r#" +//- /lib.rs crate:b new_source_root:library +pub enum State { + Active, + Inactive, +} + +use State::*; + +pub trait Checker { + fn check(&self) -> State; + + fn is_active(&self) -> bool { + match self.check() { + Active => true, + Inactive => false, + } + } +} +//- /main.rs crate:a deps:b +struct MyChecker; + +impl b::Checker for MyChecker { + fn check(&self) -> b::State { + todo!(); + }$0 +}"#, + r#" +struct MyChecker; + +impl b::Checker for MyChecker { + fn check(&self) -> b::State { + todo!(); + } + + $0fn is_active(&self) -> bool { + match self.check() { + b::State::Active => true, + b::State::Inactive => false, + } + } +}"#, + ); + } }
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index 3b447d1..753a9e5 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -207,10 +207,10 @@ // negate all tail expressions in the closure body let tail_cb = &mut |e: &_| tail_cb_impl(&mut editor, &make, e); walk_expr(&closure_body, &mut |expr| { - if let ast::Expr::ReturnExpr(ret_expr) = expr { - if let Some(ret_expr_arg) = &ret_expr.expr() { - for_each_tail_expr(ret_expr_arg, tail_cb); - } + if let ast::Expr::ReturnExpr(ret_expr) = expr + && let Some(ret_expr_arg) = &ret_expr.expr() + { + for_each_tail_expr(ret_expr_arg, tail_cb); } }); for_each_tail_expr(&closure_body, tail_cb);
diff --git a/crates/ide-assists/src/handlers/convert_bool_then.rs b/crates/ide-assists/src/handlers/convert_bool_then.rs index d7b7e8d..9d5d3f2 100644 --- a/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/crates/ide-assists/src/handlers/convert_bool_then.rs
@@ -86,12 +86,11 @@ e @ ast::Expr::CallExpr(_) => Some(e.clone()), _ => None, }; - if let Some(ast::Expr::CallExpr(call)) = e { - if let Some(arg_list) = call.arg_list() { - if let Some(arg) = arg_list.args().next() { - editor.replace(call.syntax(), arg.syntax()); - } - } + if let Some(ast::Expr::CallExpr(call)) = e + && let Some(arg_list) = call.arg_list() + && let Some(arg) = arg_list.args().next() + { + editor.replace(call.syntax(), arg.syntax()); } }); let edit = editor.finish(); @@ -276,12 +275,12 @@ e @ ast::Expr::CallExpr(_) => Some(e.clone()), _ => None, }; - if let Some(ast::Expr::CallExpr(call)) = e { - if let Some(ast::Expr::PathExpr(p)) = call.expr() { - let res = p.path().and_then(|p| sema.resolve_path(&p)); - if let Some(hir::PathResolution::Def(hir::ModuleDef::Variant(v))) = res { - return invalid |= v != some_variant; - } + if let Some(ast::Expr::CallExpr(call)) = e + && let Some(ast::Expr::PathExpr(p)) = call.expr() + { + let res = p.path().and_then(|p| sema.resolve_path(&p)); + if let Some(hir::PathResolution::Def(hir::ModuleDef::Variant(v))) = res { + return invalid |= v != some_variant; } } invalid = true
diff --git a/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index f73b8c4..c208c8b 100644 --- a/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
@@ -13,12 +13,7 @@ use itertools::Itertools; use syntax::{ AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, - ast::{ - self, HasName, - edit::IndentLevel, - edit_in_place::{AttrsOwnerEdit, Indent}, - make, - }, + ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, make}, }; use crate::{ @@ -506,18 +501,6 @@ } fn make_bool_enum(make_pub: bool) -> ast::Enum { - let enum_def = make::enum_( - if make_pub { Some(make::visibility_pub()) } else { None }, - make::name("Bool"), - None, - None, - make::variant_list(vec![ - make::variant(None, make::name("True"), None, None), - make::variant(None, make::name("False"), None, None), - ]), - ) - .clone_for_update(); - let derive_eq = make::attr_outer(make::meta_token_tree( make::ext::ident_path("derive"), make::token_tree( @@ -529,11 +512,19 @@ NodeOrToken::Token(make::tokens::ident("Eq")), ], ), - )) - .clone_for_update(); - enum_def.add_attr(derive_eq); - - enum_def + )); + make::enum_( + [derive_eq], + if make_pub { Some(make::visibility_pub()) } else { None }, + make::name("Bool"), + None, + None, + make::variant_list(vec![ + make::variant(None, make::name("True"), None, None), + make::variant(None, make::name("False"), None, None), + ]), + ) + .clone_for_update() } #[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 43515de..5f526ec 100644 --- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -101,21 +101,21 @@ // but we need to locate `AstPtr`s inside the body. let mut wrap_body_in_block = true; if let ast::Expr::BlockExpr(block) = &body { - if let Some(async_token) = block.async_token() { - if !is_async { - is_async = true; - ret_ty = ret_ty.future_output(ctx.db())?; - let token_idx = async_token.index(); - let whitespace_tokens_after_count = 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(), - ); - } + if let Some(async_token) = block.async_token() + && !is_async + { + is_async = true; + ret_ty = ret_ty.future_output(ctx.db())?; + let token_idx = async_token.index(); + let whitespace_tokens_after_count = 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(), + ); } if let Some(gen_token) = block.gen_token() { is_gen = true; @@ -236,6 +236,7 @@ }; let mut fn_ = make::fn_( None, + None, closure_name_or_default.clone(), closure_type_params, closure_where_clause, @@ -513,10 +514,10 @@ CaptureKind::MutableRef | CaptureKind::UniqueSharedRef => true, CaptureKind::Move => return place, }; - if let ast::Expr::PrefixExpr(expr) = &place { - if expr.op_kind() == Some(ast::UnaryOp::Deref) { - return expr.expr().expect("`display_place_source_code()` produced an invalid expr"); - } + if let ast::Expr::PrefixExpr(expr) = &place + && expr.op_kind() == Some(ast::UnaryOp::Deref) + { + return expr.expr().expect("`display_place_source_code()` produced an invalid expr"); } make::expr_ref(place, needs_mut) } @@ -642,11 +643,11 @@ expr = ast::Expr::cast(parent).unwrap(); continue; } - if let Some(stmt_list) = ast::StmtList::cast(parent) { - if let Some(block) = stmt_list.syntax().parent().and_then(ast::BlockExpr::cast) { - expr = ast::Expr::BlockExpr(block); - continue; - } + if let Some(stmt_list) = ast::StmtList::cast(parent) + && let Some(block) = stmt_list.syntax().parent().and_then(ast::BlockExpr::cast) + { + expr = ast::Expr::BlockExpr(block); + continue; } break; } @@ -662,12 +663,11 @@ if let Some(let_stmt) = ast::LetStmt::cast(ancestor.clone()) { break 'find_expr let_stmt.initializer(); } - if ast::MatchArm::can_cast(ancestor.kind()) { - if let Some(match_) = + if ast::MatchArm::can_cast(ancestor.kind()) + && let Some(match_) = ancestor.parent().and_then(|it| it.parent()).and_then(ast::MatchExpr::cast) - { - break 'find_expr match_.expr(); - } + { + break 'find_expr match_.expr(); } if ast::ExprStmt::can_cast(ancestor.kind()) { break;
diff --git a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index db41927..7d8b763 100644 --- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
@@ -1,9 +1,7 @@ use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait}; -use itertools::Itertools; -use syntax::{ - ast::{self, AstNode, HasGenericArgs, HasName, make}, - ted, -}; +use syntax::ast::edit::IndentLevel; +use syntax::ast::{self, AstNode, HasGenericArgs, HasName, make}; +use syntax::syntax_editor::{Element, Position}; use crate::{AssistContext, AssistId, Assists}; @@ -49,11 +47,12 @@ }; let associated_items = impl_.assoc_item_list()?; + let associated_l_curly = associated_items.l_curly_token()?; let from_fn = associated_items.assoc_items().find_map(|item| { - if let ast::AssocItem::Fn(f) = item { - if f.name()?.text() == "from" { - return Some(f); - } + if let ast::AssocItem::Fn(f) = item + && f.name()?.text() == "from" + { + return Some(f); }; None })?; @@ -75,33 +74,29 @@ "Convert From to TryFrom", impl_.syntax().text_range(), |builder| { - let trait_ty = builder.make_mut(trait_ty); - let from_fn_return_type = builder.make_mut(from_fn_return_type); - let from_fn_name = builder.make_mut(from_fn_name); - let tail_expr = builder.make_mut(tail_expr); - let return_exprs = return_exprs.map(|r| builder.make_mut(r)).collect_vec(); - let associated_items = builder.make_mut(associated_items); - - ted::replace( + let mut editor = builder.make_editor(impl_.syntax()); + editor.replace( trait_ty.syntax(), make::ty(&format!("TryFrom<{from_type}>")).syntax().clone_for_update(), ); - ted::replace( + editor.replace( from_fn_return_type.syntax(), make::ty("Result<Self, Self::Error>").syntax().clone_for_update(), ); - ted::replace(from_fn_name.syntax(), make::name("try_from").syntax().clone_for_update()); - ted::replace( + editor + .replace(from_fn_name.syntax(), make::name("try_from").syntax().clone_for_update()); + editor.replace( tail_expr.syntax(), wrap_ok(tail_expr.clone()).syntax().clone_for_update(), ); for r in return_exprs { let t = r.expr().unwrap_or_else(make::ext::expr_unit); - ted::replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update()); + editor.replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update()); } let error_type = ast::AssocItem::TypeAlias(make::ty_alias( + None, "Error", None, None, @@ -110,15 +105,24 @@ )) .clone_for_update(); - if let Some(cap) = ctx.config.snippet_cap { - if let ast::AssocItem::TypeAlias(type_alias) = &error_type { - if let Some(ty) = type_alias.ty() { - builder.add_placeholder_snippet(cap, ty); - } - } + if let Some(cap) = ctx.config.snippet_cap + && let ast::AssocItem::TypeAlias(type_alias) = &error_type + && let Some(ty) = type_alias.ty() + { + let placeholder = builder.make_placeholder_snippet(cap); + editor.add_annotation(ty.syntax(), placeholder); } - associated_items.add_item_at_start(error_type); + let indent = IndentLevel::from_token(&associated_l_curly) + 1; + editor.insert_all( + Position::after(associated_l_curly), + vec![ + make::tokens::whitespace(&format!("\n{indent}")).syntax_element(), + error_type.syntax().syntax_element(), + make::tokens::whitespace("\n").syntax_element(), + ], + ); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) }
diff --git a/crates/ide-assists/src/handlers/convert_into_to_from.rs b/crates/ide-assists/src/handlers/convert_into_to_from.rs index b80276a..3d9cde0e 100644 --- a/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/crates/ide-assists/src/handlers/convert_into_to_from.rs
@@ -65,10 +65,10 @@ }; let into_fn = impl_.assoc_item_list()?.assoc_items().find_map(|item| { - if let ast::AssocItem::Fn(f) = item { - if f.name()?.text() == "into" { - return Some(f); - } + if let ast::AssocItem::Fn(f) = item + && f.name()?.text() == "into" + { + return Some(f); }; None })?;
diff --git a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index bf1cfc7..2ea032f 100644 --- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -13,7 +13,6 @@ edit::{AstNodeEdit, IndentLevel}, make, }, - ted, }; use crate::{ @@ -132,7 +131,6 @@ "Convert to guarded return", target, |edit| { - let if_expr = edit.make_mut(if_expr); let if_indent_level = IndentLevel::from_node(if_expr.syntax()); let replacement = match if_let_pat { None => { @@ -168,8 +166,9 @@ .take_while(|i| *i != end_of_then), ) .collect(); - - ted::replace_with_many(if_expr.syntax(), then_statements) + let mut editor = edit.make_editor(if_expr.syntax()); + editor.replace_with_many(if_expr.syntax(), then_statements); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -214,7 +213,6 @@ "Convert to guarded return", target, |edit| { - let let_stmt = edit.make_mut(let_stmt); let let_indent_level = IndentLevel::from_node(let_stmt.syntax()); let replacement = { @@ -227,8 +225,9 @@ let let_else_stmt = let_else_stmt.indent(let_indent_level); let_else_stmt.syntax().clone() }; - - ted::replace(let_stmt.syntax(), replacement) + let mut editor = edit.make_editor(let_stmt.syntax()); + editor.replace(let_stmt.syntax(), replacement); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) }
diff --git a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index cca4cb9..247c101 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs
@@ -265,10 +265,10 @@ let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); walk_expr(&body, &mut |expr| { - if let ast::Expr::ReturnExpr(ret_expr) = expr { - if let Some(ret_expr_arg) = &ret_expr.expr() { - for_each_tail_expr(ret_expr_arg, tail_cb); - } + if let ast::Expr::ReturnExpr(ret_expr) = expr + && let Some(ret_expr_arg) = &ret_expr.expr() + { + for_each_tail_expr(ret_expr_arg, tail_cb); } }); for_each_tail_expr(&body, tail_cb);
diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 8075619..3d78895 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -1,9 +1,14 @@ use either::Either; +use hir::FileRangeWrapper; use ide_db::defs::{Definition, NameRefClass}; +use std::ops::RangeInclusive; use syntax::{ - SyntaxKind, SyntaxNode, - ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility}, - match_ast, ted, + SyntaxElement, SyntaxKind, SyntaxNode, T, TextSize, + ast::{ + self, AstNode, HasAttrs, HasGenericParams, HasVisibility, syntax_factory::SyntaxFactory, + }, + match_ast, + syntax_editor::{Element, Position, SyntaxEditor}, }; use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder}; @@ -71,7 +76,7 @@ Either::Right(v) => Either::Right(ctx.sema.to_def(v)?), }; let target = strukt_or_variant.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range(); - + let syntax = strukt_or_variant.as_ref().either(|s| s.syntax(), |v| v.syntax()); acc.add( AssistId::refactor_rewrite("convert_tuple_struct_to_named_struct"), "Convert to named struct", @@ -79,58 +84,55 @@ |edit| { let names = generate_names(tuple_fields.fields()); edit_field_references(ctx, edit, tuple_fields.fields(), &names); + let mut editor = edit.make_editor(syntax); edit_struct_references(ctx, edit, strukt_def, &names); - edit_struct_def(ctx, edit, &strukt_or_variant, tuple_fields, names); + edit_struct_def(&mut editor, &strukt_or_variant, tuple_fields, names); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } fn edit_struct_def( - ctx: &AssistContext<'_>, - edit: &mut SourceChangeBuilder, + editor: &mut SyntaxEditor, strukt: &Either<ast::Struct, ast::Variant>, tuple_fields: ast::TupleFieldList, names: Vec<ast::Name>, ) { let record_fields = tuple_fields.fields().zip(names).filter_map(|(f, name)| { - let field = ast::make::record_field(f.visibility(), name, f.ty()?).clone_for_update(); - ted::insert_all( - ted::Position::first_child_of(field.syntax()), + let field = ast::make::record_field(f.visibility(), name, f.ty()?); + let mut field_editor = SyntaxEditor::new(field.syntax().clone()); + field_editor.insert_all( + Position::first_child_of(field.syntax()), f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(), ); - Some(field) + ast::RecordField::cast(field_editor.finish().new_root().clone()) }); - let record_fields = ast::make::record_field_list(record_fields); - let tuple_fields_text_range = tuple_fields.syntax().text_range(); - - edit.edit_file(ctx.vfs_file_id()); + let make = SyntaxFactory::without_mappings(); + let record_fields = make.record_field_list(record_fields); + let tuple_fields_before = Position::before(tuple_fields.syntax()); if let Either::Left(strukt) = strukt { if let Some(w) = strukt.where_clause() { - edit.delete(w.syntax().text_range()); - edit.insert( - tuple_fields_text_range.start(), - ast::make::tokens::single_newline().text(), - ); - edit.insert(tuple_fields_text_range.start(), w.syntax().text()); + editor.delete(w.syntax()); + let mut insert_element = Vec::new(); + insert_element.push(ast::make::tokens::single_newline().syntax_element()); + insert_element.push(w.syntax().clone_for_update().syntax_element()); if w.syntax().last_token().is_none_or(|t| t.kind() != SyntaxKind::COMMA) { - edit.insert(tuple_fields_text_range.start(), ","); + insert_element.push(ast::make::token(T![,]).into()); } - edit.insert( - tuple_fields_text_range.start(), - ast::make::tokens::single_newline().text(), - ); + insert_element.push(ast::make::tokens::single_newline().syntax_element()); + editor.insert_all(tuple_fields_before, insert_element); } else { - edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); + editor.insert(tuple_fields_before, ast::make::tokens::single_space()); } if let Some(t) = strukt.semicolon_token() { - edit.delete(t.text_range()); + editor.delete(t); } } else { - edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); + editor.insert(tuple_fields_before, ast::make::tokens::single_space()); } - edit.replace(tuple_fields_text_range, record_fields.to_string()); + editor.replace(tuple_fields.syntax(), record_fields.syntax()); } fn edit_struct_references( @@ -145,27 +147,22 @@ }; let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); - let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> { + let edit_node = |node: SyntaxNode| -> Option<SyntaxNode> { + let make = SyntaxFactory::without_mappings(); match_ast! { match node { ast::TupleStructPat(tuple_struct_pat) => { - let file_range = ctx.sema.original_range_opt(&node)?; - edit.edit_file(file_range.file_id.file_id(ctx.db())); - edit.replace( - file_range.range, - ast::make::record_pat_with_fields( - tuple_struct_pat.path()?, - ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map( - |(pat, name)| { - ast::make::record_pat_field( - ast::make::name_ref(&name.to_string()), - pat, - ) - }, - ), None), - ) - .to_string(), - ); + Some(make.record_pat_with_fields( + tuple_struct_pat.path()?, + ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map( + |(pat, name)| { + ast::make::record_pat_field( + ast::make::name_ref(&name.to_string()), + pat, + ) + }, + ), None), + ).syntax().clone()) }, // for tuple struct creations like Foo(42) ast::CallExpr(call_expr) => { @@ -181,10 +178,8 @@ } let arg_list = call_expr.syntax().descendants().find_map(ast::ArgList::cast)?; - - edit.replace( - ctx.sema.original_range(&node).range, - ast::make::record_expr( + Some( + make.record_expr( path, ast::make::record_expr_field_list(arg_list.args().zip(names).map( |(expr, name)| { @@ -194,25 +189,58 @@ ) }, )), - ) - .to_string(), - ); + ).syntax().clone() + ) }, - _ => return None, + _ => None, } } - Some(()) }; for (file_id, refs) in usages { - edit.edit_file(file_id.file_id(ctx.db())); - for r in refs { - for node in r.name.syntax().ancestors() { - if edit_node(edit, node).is_some() { - break; + let source = ctx.sema.parse(file_id); + let source = source.syntax(); + + let mut editor = edit.make_editor(source); + for r in refs.iter().rev() { + if let Some((old_node, new_node)) = r + .name + .syntax() + .ancestors() + .find_map(|node| Some((node.clone(), edit_node(node.clone())?))) + { + if let Some(old_node) = ctx.sema.original_syntax_node_rooted(&old_node) { + editor.replace(old_node, new_node); + } else { + let FileRangeWrapper { file_id: _, range } = ctx.sema.original_range(&old_node); + let parent = source.covering_element(range); + match parent { + SyntaxElement::Token(token) => { + editor.replace(token, new_node.syntax_element()); + } + SyntaxElement::Node(parent_node) => { + // replace the part of macro + // ``` + // foo!(a, Test::A(0)); + // ^^^^^^^^^^^^^^^ // parent_node + // ^^^^^^^^^^ // replace_range + // ``` + let start = parent_node + .children_with_tokens() + .find(|t| t.text_range().contains(range.start())); + let end = parent_node + .children_with_tokens() + .find(|t| t.text_range().contains(range.end() - TextSize::new(1))); + if let (Some(start), Some(end)) = (start, end) { + let replace_range = RangeInclusive::new(start, end); + editor.replace_all(replace_range, vec![new_node.into()]); + } + } + } } } } + edit.add_file_edits(file_id.file_id(ctx.db()), editor); } } @@ -230,22 +258,28 @@ let def = Definition::Field(field); let usages = def.usages(&ctx.sema).all(); for (file_id, refs) in usages { - edit.edit_file(file_id.file_id(ctx.db())); + let source = ctx.sema.parse(file_id); + let source = source.syntax(); + let mut editor = edit.make_editor(source); for r in refs { - if let Some(name_ref) = r.name.as_name_ref() { - edit.replace(ctx.sema.original_range(name_ref.syntax()).range, name.text()); + if let Some(name_ref) = r.name.as_name_ref() + && let Some(original) = ctx.sema.original_ast_node(name_ref.clone()) + { + editor.replace(original.syntax(), name.syntax()); } } + edit.add_file_edits(file_id.file_id(ctx.db()), editor); } } } fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Name> { + let make = SyntaxFactory::without_mappings(); fields .enumerate() .map(|(i, _)| { let idx = i + 1; - ast::make::name(&format!("field{idx}")) + make.name(&format!("field{idx}")) }) .collect() } @@ -1013,8 +1047,7 @@ pub struct $0Foo(#[my_custom_attr] u32); "#, r#" -pub struct Foo { #[my_custom_attr] -field1: u32 } +pub struct Foo { #[my_custom_attr]field1: u32 } "#, ); }
diff --git a/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs b/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs index e582aa8..1af5db1 100644 --- a/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs +++ b/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
@@ -100,10 +100,10 @@ sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr, ) -> Option<ArmBodyExpression> { - if let ast::Expr::Literal(lit) = expr { - if let ast::LiteralKind::Bool(b) = lit.kind() { - return Some(ArmBodyExpression::Literal(b)); - } + if let ast::Expr::Literal(lit) = expr + && let ast::LiteralKind::Bool(b) = lit.kind() + { + return Some(ArmBodyExpression::Literal(b)); } if !sema.type_of_expr(expr)?.original.is_bool() {
diff --git a/crates/ide-assists/src/handlers/desugar_try_expr.rs b/crates/ide-assists/src/handlers/desugar_try_expr.rs index efadde9..9976e34 100644 --- a/crates/ide-assists/src/handlers/desugar_try_expr.rs +++ b/crates/ide-assists/src/handlers/desugar_try_expr.rs
@@ -106,73 +106,73 @@ }, ); - if let Some(let_stmt) = try_expr.syntax().parent().and_then(ast::LetStmt::cast) { - if let_stmt.let_else().is_none() { - let pat = let_stmt.pat()?; - acc.add( - AssistId::refactor_rewrite("desugar_try_expr_let_else"), - "Replace try expression with let else", - target, - |builder| { - let make = SyntaxFactory::with_mappings(); - let mut editor = builder.make_editor(let_stmt.syntax()); + if let Some(let_stmt) = try_expr.syntax().parent().and_then(ast::LetStmt::cast) + && let_stmt.let_else().is_none() + { + let pat = let_stmt.pat()?; + acc.add( + AssistId::refactor_rewrite("desugar_try_expr_let_else"), + "Replace try expression with let else", + target, + |builder| { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(let_stmt.syntax()); - let indent_level = IndentLevel::from_node(let_stmt.syntax()); - let new_let_stmt = make.let_else_stmt( - try_enum.happy_pattern(pat), - let_stmt.ty(), - expr, - make.block_expr( - iter::once( - make.expr_stmt( - make.expr_return(Some(match try_enum { - TryEnum::Option => make.expr_path(make.ident_path("None")), - TryEnum::Result => make - .expr_call( - make.expr_path(make.ident_path("Err")), - make.arg_list(iter::once( - match ctx.config.expr_fill_default { - ExprFillDefaultMode::Todo => make - .expr_macro( - make.ident_path("todo"), - make.token_tree( - syntax::SyntaxKind::L_PAREN, - [], - ), - ) - .into(), - ExprFillDefaultMode::Underscore => { - make.expr_underscore().into() - } - ExprFillDefaultMode::Default => make - .expr_macro( - make.ident_path("todo"), - make.token_tree( - syntax::SyntaxKind::L_PAREN, - [], - ), - ) - .into(), - }, - )), - ) - .into(), - })) - .indent(indent_level + 1) - .into(), - ) + let indent_level = IndentLevel::from_node(let_stmt.syntax()); + let new_let_stmt = make.let_else_stmt( + try_enum.happy_pattern(pat), + let_stmt.ty(), + expr, + make.block_expr( + iter::once( + make.expr_stmt( + make.expr_return(Some(match try_enum { + TryEnum::Option => make.expr_path(make.ident_path("None")), + TryEnum::Result => make + .expr_call( + make.expr_path(make.ident_path("Err")), + make.arg_list(iter::once( + match ctx.config.expr_fill_default { + ExprFillDefaultMode::Todo => make + .expr_macro( + make.ident_path("todo"), + make.token_tree( + syntax::SyntaxKind::L_PAREN, + [], + ), + ) + .into(), + ExprFillDefaultMode::Underscore => { + make.expr_underscore().into() + } + ExprFillDefaultMode::Default => make + .expr_macro( + make.ident_path("todo"), + make.token_tree( + syntax::SyntaxKind::L_PAREN, + [], + ), + ) + .into(), + }, + )), + ) + .into(), + })) + .indent(indent_level + 1) .into(), - ), - None, - ) - .indent(indent_level), - ); - editor.replace(let_stmt.syntax(), new_let_stmt.syntax()); - editor.add_mappings(make.finish_with_mappings()); - builder.add_file_edits(ctx.vfs_file_id(), editor); - }, - ); - } + ) + .into(), + ), + None, + ) + .indent(indent_level), + ); + editor.replace(let_stmt.syntax(), new_let_stmt.syntax()); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); + }, + ); } Some(()) }
diff --git a/crates/ide-assists/src/handlers/expand_glob_import.rs b/crates/ide-assists/src/handlers/expand_glob_import.rs index 307414c..4b6d0b3 100644 --- a/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/crates/ide-assists/src/handlers/expand_glob_import.rs
@@ -9,7 +9,6 @@ use syntax::{ AstNode, Direction, SyntaxNode, SyntaxToken, T, ast::{self, Use, UseTree, VisibilityKind, make}, - ted, }; use crate::{ @@ -165,8 +164,6 @@ let filtered_defs = if reexport_public_items { refs_in_target } else { refs_in_target.used_refs(ctx) }; - let use_tree = builder.make_mut(use_tree); - let names_to_import = find_names_to_import(filtered_defs, imported_defs); let expanded = make::use_tree_list(names_to_import.iter().map(|n| { let path = make::ext::ident_path( @@ -176,22 +173,24 @@ })) .clone_for_update(); + let mut editor = builder.make_editor(use_tree.syntax()); match use_tree.star_token() { Some(star) => { let needs_braces = use_tree.path().is_some() && names_to_import.len() != 1; if needs_braces { - ted::replace(star, expanded.syntax()) + editor.replace(star, expanded.syntax()) } else { let without_braces = expanded .syntax() .children_with_tokens() .filter(|child| !matches!(child.kind(), T!['{'] | T!['}'])) .collect(); - ted::replace_with_many(star, without_braces) + editor.replace_with_many(star, without_braces) } } None => never!(), } + builder.add_file_edits(ctx.vfs_file_id(), editor); } fn get_export_visibility_kind(use_item: &Use) -> VisibilityKind { @@ -272,16 +271,16 @@ .clone() .into_iter() .filter(|r| { - if let Definition::Trait(tr) = r.def { - if tr.items(ctx.db()).into_iter().any(|ai| { + if let Definition::Trait(tr) = r.def + && tr.items(ctx.db()).into_iter().any(|ai| { if let AssocItem::Function(f) = ai { def_is_referenced_in(Definition::Function(f), ctx) } else { false } - }) { - return true; - } + }) + { + return true; } def_is_referenced_in(r.def, ctx)
diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index 54699a9..e3c7ea1 100644 --- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
@@ -7,9 +7,8 @@ use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::WHITESPACE, - T, - ast::{self, make}, - ted, + SyntaxToken, T, + ast::{self, TokenTree, make, syntax_factory::SyntaxFactory}, }; // Assist: extract_expressions_from_format_string @@ -58,13 +57,12 @@ "Extract format expressions", tt.syntax().text_range(), |edit| { - let tt = edit.make_mut(tt); - // Extract existing arguments in macro - let tokens = tt.token_trees_and_tokens().collect_vec(); + let mut raw_tokens = tt.token_trees_and_tokens().skip(1).collect_vec(); + let format_string_index = format_str_index(&raw_tokens, &fmt_string); + let tokens = raw_tokens.split_off(format_string_index); let existing_args = if let [ - _opening_bracket, NodeOrToken::Token(_format_string), _args_start_comma, tokens @ .., @@ -93,9 +91,11 @@ // Start building the new args let mut existing_args = existing_args.into_iter(); - let mut new_tt_bits = vec![NodeOrToken::Token(make::tokens::literal(&new_fmt))]; + let mut new_tt_bits = raw_tokens; let mut placeholder_indexes = vec![]; + new_tt_bits.push(NodeOrToken::Token(make::tokens::literal(&new_fmt))); + for arg in extracted_args { if matches!(arg, Arg::Expr(_) | Arg::Placeholder) { // insert ", " before each arg @@ -131,8 +131,10 @@ } // Insert new args - let new_tt = make::token_tree(tt_delimiter, new_tt_bits).clone_for_update(); - ted::replace(tt.syntax(), new_tt.syntax()); + let make = SyntaxFactory::with_mappings(); + let new_tt = make.token_tree(tt_delimiter, new_tt_bits); + let mut editor = edit.make_editor(tt.syntax()); + editor.replace(tt.syntax(), new_tt.syntax()); if let Some(cap) = ctx.config.snippet_cap { // Add placeholder snippets over placeholder args @@ -145,21 +147,38 @@ }; if stdx::always!(placeholder.kind() == T![_]) { - edit.add_placeholder_snippet_token(cap, placeholder); + let annotation = edit.make_placeholder_snippet(cap); + editor.add_annotation(placeholder, annotation); } } // Add the final tabstop after the format literal - if let Some(NodeOrToken::Token(literal)) = new_tt.token_trees_and_tokens().nth(1) { - edit.add_tabstop_after_token(cap, literal); + if let Some(NodeOrToken::Token(literal)) = + new_tt.token_trees_and_tokens().nth(1 + format_string_index) + { + let annotation = edit.make_tabstop_after(cap); + editor.add_annotation(literal, annotation); } } + editor.add_mappings(make.finish_with_mappings()); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ); Some(()) } +fn format_str_index( + raw_tokens: &[NodeOrToken<TokenTree, SyntaxToken>], + fmt_string: &ast::String, +) -> usize { + let fmt_string = fmt_string.syntax(); + raw_tokens + .iter() + .position(|tt| tt.as_token().is_some_and(|tt| tt == fmt_string)) + .unwrap_or_default() +} + #[cfg(test)] mod tests { use super::*; @@ -184,6 +203,24 @@ } #[test] + fn multiple_middle_arg_on_write() { + check_assist( + extract_expressions_from_format_string, + r#" +//- minicore: write +fn main() { + write!(writer(), "{} {x + 1:b} {}$0", y + 2, 2); +} +"#, + r#" +fn main() { + write!(writer(), "{} {:b} {}"$0, y + 2, x + 1, 2); +} +"#, + ); + } + + #[test] fn single_arg() { check_assist( extract_expressions_from_format_string,
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 00cbef1..d88e331 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -175,10 +175,10 @@ let fn_def = format_function(ctx, module, &fun, old_indent).clone_for_update(); - if let Some(cap) = ctx.config.snippet_cap { - if let Some(name) = fn_def.name() { - builder.add_tabstop_before(cap, name); - } + if let Some(cap) = ctx.config.snippet_cap + && let Some(name) = fn_def.name() + { + builder.add_tabstop_before(cap, name); } let fn_def = match fun.self_param_adt(ctx) { @@ -289,10 +289,10 @@ // Covering element returned the parent block of one or multiple statements that have been selected if let Some(stmt_list) = ast::StmtList::cast(node.clone()) { - if let Some(block_expr) = stmt_list.syntax().parent().and_then(ast::BlockExpr::cast) { - if block_expr.syntax().text_range() == selection_range { - return FunctionBody::from_expr(block_expr.into()); - } + if let Some(block_expr) = stmt_list.syntax().parent().and_then(ast::BlockExpr::cast) + && block_expr.syntax().text_range() == selection_range + { + return FunctionBody::from_expr(block_expr.into()); } // Extract the full statements. @@ -915,11 +915,10 @@ ast::Fn(fn_) => { let func = sema.to_def(&fn_)?; let mut ret_ty = func.ret_type(sema.db); - if func.is_async(sema.db) { - if let Some(async_ret) = func.async_ret_type(sema.db) { + if func.is_async(sema.db) + && let Some(async_ret) = func.async_ret_type(sema.db) { ret_ty = async_ret; } - } (fn_.const_token().is_some(), fn_.body().map(ast::Expr::BlockExpr), Some(ret_ty)) }, ast::Static(statik) => { @@ -1172,19 +1171,19 @@ /// Search `parent`'s ancestors for items with potentially applicable generic parameters fn generic_parents(parent: &SyntaxNode) -> Vec<GenericParent> { let mut list = Vec::new(); - if let Some(parent_item) = parent.ancestors().find_map(ast::Item::cast) { - if let ast::Item::Fn(ref fn_) = parent_item { - if let Some(parent_parent) = - parent_item.syntax().parent().and_then(|it| it.parent()).and_then(ast::Item::cast) - { - match parent_parent { - ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)), - ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)), - _ => (), - } + if let Some(parent_item) = parent.ancestors().find_map(ast::Item::cast) + && let ast::Item::Fn(ref fn_) = parent_item + { + if let Some(parent_parent) = + parent_item.syntax().parent().and_then(|it| it.parent()).and_then(ast::Item::cast) + { + match parent_parent { + ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)), + ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)), + _ => (), } - list.push(GenericParent::Fn(fn_.clone())); } + list.push(GenericParent::Fn(fn_.clone())); } list } @@ -1337,10 +1336,10 @@ // see https://github.com/rust-lang/rust-analyzer/pull/7535#discussion_r570048550 let mut res = FxIndexSet::default(); body.walk_pat(&mut |pat| { - if let ast::Pat::IdentPat(pat) = pat { - if let Some(local) = sema.to_def(&pat) { - res.insert(local); - } + if let ast::Pat::IdentPat(pat) = pat + && let Some(local) = sema.to_def(&pat) + { + res.insert(local); } }); res @@ -1445,11 +1444,11 @@ 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 { - if 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![,])); - } + 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![,])); } } @@ -1643,6 +1642,7 @@ make::fn_( None, + None, fun_name, generic_params, where_clause, @@ -2120,29 +2120,29 @@ _ => {} }, WalkEvent::Leave(e) => { - if nested_scope.is_none() { - if let Some(expr) = ast::Expr::cast(e.clone()) { - 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 nested_scope.is_none() + && let Some(expr) = ast::Expr::cast(e.clone()) + { + 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()) } - 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()) - } + } + 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()) } - 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()) - } + } + 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()) } - _ => { - // do nothing - } + } + _ => { + // do nothing } } }
diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs index b82b798..dad19bf 100644 --- a/crates/ide-assists/src/handlers/extract_module.rs +++ b/crates/ide-assists/src/handlers/extract_module.rs
@@ -1,6 +1,5 @@ -use std::iter; +use std::ops::RangeInclusive; -use either::Either; use hir::{HasSource, ModuleSource}; use ide_db::{ FileId, FxHashMap, FxHashSet, @@ -69,13 +68,12 @@ let mut impl_parent: Option<ast::Impl> = None; let mut impl_child_count: usize = 0; - if let Some(parent_assoc_list) = node.parent() { - if let Some(parent_impl) = parent_assoc_list.parent() { - if let Some(impl_) = ast::Impl::cast(parent_impl) { - impl_child_count = parent_assoc_list.children().count(); - impl_parent = Some(impl_); - } - } + if let Some(parent_assoc_list) = node.parent() + && let Some(parent_impl) = parent_assoc_list.parent() + && let Some(impl_) = ast::Impl::cast(parent_impl) + { + impl_child_count = parent_assoc_list.children().count(); + impl_parent = Some(impl_); } let mut curr_parent_module: Option<ast::Module> = None; @@ -83,7 +81,15 @@ curr_parent_module = ast::Module::cast(mod_syn_opt); } - let mut module = extract_target(&node, ctx.selection_trimmed())?; + let selection_range = ctx.selection_trimmed(); + let (mut module, module_text_range) = if let Some(item) = ast::Item::cast(node.clone()) { + let module = extract_single_target(&item); + (module, node.text_range()) + } else { + let (module, range) = extract_child_target(&node, selection_range)?; + let module_text_range = range.start().text_range().cover(range.end().text_range()); + (module, module_text_range) + }; if module.body_items.is_empty() { return None; } @@ -93,7 +99,7 @@ acc.add( AssistId::refactor_extract("extract_module"), "Extract Module", - module.text_range, + module_text_range, |builder| { //This takes place in three steps: // @@ -111,17 +117,17 @@ //for change_visibility and usages for first point mentioned above in the process let (usages_to_be_processed, record_fields, use_stmts_to_be_inserted) = - module.get_usages_and_record_fields(ctx); + module.get_usages_and_record_fields(ctx, module_text_range); builder.edit_file(ctx.vfs_file_id()); use_stmts_to_be_inserted.into_iter().for_each(|(_, use_stmt)| { builder.insert(ctx.selection_trimmed().end(), format!("\n{use_stmt}")); }); - let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx); + let import_items = module.resolve_imports(curr_parent_module, ctx); module.change_visibility(record_fields); - let module_def = generate_module_def(&impl_parent, &mut module, old_item_indent); + let module_def = generate_module_def(&impl_parent, module, old_item_indent).to_string(); let mut usages_to_be_processed_for_cur_file = vec![]; for (file_id, usages) in usages_to_be_processed { @@ -158,15 +164,12 @@ builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}")); } else { - for import_path_text_range in import_paths_to_be_removed { - if module.text_range.intersect(import_path_text_range).is_some() { - module.text_range = module.text_range.cover(import_path_text_range); - } else { - builder.delete(import_path_text_range); + for import_item in import_items { + if !module_text_range.contains_range(import_item) { + builder.delete(import_item); } } - - builder.replace(module.text_range, module_def) + builder.replace(module_text_range, module_def) } }, ) @@ -174,38 +177,50 @@ fn generate_module_def( parent_impl: &Option<ast::Impl>, - module: &mut Module, + module: Module, old_indent: IndentLevel, -) -> String { - let (items_to_be_processed, new_item_indent) = if parent_impl.is_some() { - (Either::Left(module.body_items.iter()), old_indent + 2) +) -> ast::Module { + let Module { name, body_items, use_items } = module; + let items = if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { + let assoc_items = body_items + .into_iter() + .map(|item| item.syntax().clone()) + .filter_map(ast::AssocItem::cast) + .map(|it| it.indent(IndentLevel(1))) + .collect_vec(); + let assoc_item_list = make::assoc_item_list(Some(assoc_items)); + let impl_ = make::impl_(None, None, None, self_ty.clone(), None, Some(assoc_item_list)); + // Add the import for enum/struct corresponding to given impl block + let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax()); + let mut module_body_items = use_items; + module_body_items.insert(0, use_impl); + module_body_items.push(ast::Item::Impl(impl_)); + module_body_items } else { - (Either::Right(module.use_items.iter().chain(module.body_items.iter())), old_indent + 1) + [use_items, body_items].concat() }; - let mut body = items_to_be_processed - .map(|item| item.indent(IndentLevel(1))) - .map(|item| format!("{new_item_indent}{item}")) - .join("\n\n"); + let items = items.into_iter().map(|it| it.reset_indent().indent(IndentLevel(1))).collect_vec(); + let module_body = make::item_list(Some(items)); - if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { - let impl_indent = old_indent + 1; - body = format!("{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}"); + let module_name = make::name(name); + make::mod_(module_name, Some(module_body)).indent(old_indent) +} - // Add the import for enum/struct corresponding to given impl block - module.make_use_stmt_of_node_with_super(self_ty.syntax()); - for item in module.use_items.iter() { - body = format!("{impl_indent}{item}\n\n{body}"); - } - } +fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode) -> ast::Item { + let super_path = make::ext::ident_path("super"); + let node_path = make::ext::ident_path(&node_syntax.to_string()); + let use_ = make::use_( + None, + None, + make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false), + ); - let module_name = module.name; - format!("mod {module_name} {{\n{body}\n{old_indent}}}") + ast::Item::from(use_) } #[derive(Debug)] struct Module { - text_range: TextRange, name: &'static str, /// All items except use items. body_items: Vec<ast::Item>, @@ -215,22 +230,37 @@ use_items: Vec<ast::Item>, } -fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Module> { +fn extract_single_target(node: &ast::Item) -> Module { + let (body_items, use_items) = if matches!(node, ast::Item::Use(_)) { + (Vec::new(), vec![node.clone()]) + } else { + (vec![node.clone()], Vec::new()) + }; + let name = "modname"; + Module { name, body_items, use_items } +} + +fn extract_child_target( + node: &SyntaxNode, + selection_range: TextRange, +) -> Option<(Module, RangeInclusive<SyntaxNode>)> { let selected_nodes = node .children() .filter(|node| selection_range.contains_range(node.text_range())) - .chain(iter::once(node.clone())); - let (use_items, body_items) = selected_nodes .filter_map(ast::Item::cast) - .partition(|item| matches!(item, ast::Item::Use(..))); - - Some(Module { text_range: selection_range, name: "modname", body_items, use_items }) + .collect_vec(); + let start = selected_nodes.first()?.syntax().clone(); + let end = selected_nodes.last()?.syntax().clone(); + let (use_items, body_items): (Vec<ast::Item>, Vec<ast::Item>) = + selected_nodes.into_iter().partition(|item| matches!(item, ast::Item::Use(..))); + Some((Module { name: "modname", body_items, use_items }, start..=end)) } impl Module { fn get_usages_and_record_fields( &self, ctx: &AssistContext<'_>, + replace_range: TextRange, ) -> (FxHashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>, FxHashMap<TextSize, ast::Use>) { let mut adt_fields = Vec::new(); @@ -248,7 +278,7 @@ ast::Adt(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Adt(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx, replace_range,node_def, &mut refs, &mut use_stmts_to_be_inserted); //Enum Fields are not allowed to explicitly specify pub, it is implied match it { @@ -282,30 +312,30 @@ ast::TypeAlias(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::TypeAlias(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Const(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Const(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Static(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Static(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Fn(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Function(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Macro(it) => { if let Some(nod) = ctx.sema.to_def(&it) { - self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); } }, _ => (), @@ -319,6 +349,7 @@ fn expand_and_group_usages_file_wise( &self, ctx: &AssistContext<'_>, + replace_range: TextRange, node_def: Definition, refs_in_files: &mut FxHashMap<FileId, Vec<(TextRange, String)>>, use_stmts_to_be_inserted: &mut FxHashMap<TextSize, ast::Use>, @@ -328,7 +359,7 @@ syntax::NodeOrToken::Node(node) => node, syntax::NodeOrToken::Token(tok) => tok.parent().unwrap(), // won't panic }; - let out_of_sel = |node: &SyntaxNode| !self.text_range.contains_range(node.text_range()); + let out_of_sel = |node: &SyntaxNode| !replace_range.contains_range(node.text_range()); let mut use_stmts_set = FxHashSet::default(); for (file_id, refs) in node_def.usages(&ctx.sema).all() { @@ -436,10 +467,10 @@ } }) .for_each(|(node, def)| { - if node_set.insert(node.to_string()) { - if let Some(import) = self.process_def_in_sel(def, &node, &module, ctx) { - check_intersection_and_push(&mut imports_to_remove, import); - } + if node_set.insert(node.to_string()) + && let Some(import) = self.process_def_in_sel(def, &node, &module, ctx) + { + check_intersection_and_push(&mut imports_to_remove, import); } }) } @@ -528,7 +559,8 @@ // mod -> ust_stmt transversal // true | false -> super import insertion // true | true -> super import insertion - self.make_use_stmt_of_node_with_super(use_node); + let super_use_node = make_use_stmt_of_node_with_super(use_node); + self.use_items.insert(0, super_use_node); } None => {} } @@ -542,33 +574,34 @@ import_path_to_be_removed = Some(text_range); } - if def_in_mod && def_out_sel { - if let Some(first_path_in_use_tree) = use_tree_str.last() { - let first_path_in_use_tree_str = first_path_in_use_tree.to_string(); - if !first_path_in_use_tree_str.contains("super") - && !first_path_in_use_tree_str.contains("crate") - { - let super_path = make::ext::ident_path("super"); - use_tree_str.push(super_path); - } + if def_in_mod + && def_out_sel + && let Some(first_path_in_use_tree) = use_tree_str.last() + { + let first_path_in_use_tree_str = first_path_in_use_tree.to_string(); + if !first_path_in_use_tree_str.contains("super") + && !first_path_in_use_tree_str.contains("crate") + { + let super_path = make::ext::ident_path("super"); + use_tree_str.push(super_path); } } use_tree_paths = Some(use_tree_str); } else if def_in_mod && def_out_sel { - self.make_use_stmt_of_node_with_super(use_node); + let super_use_node = make_use_stmt_of_node_with_super(use_node); + self.use_items.insert(0, super_use_node); } } if let Some(mut use_tree_paths) = use_tree_paths { use_tree_paths.reverse(); - if uses_exist_out_sel || !uses_exist_in_sel || !def_in_mod || !def_out_sel { - if let Some(first_path_in_use_tree) = use_tree_paths.first() { - if first_path_in_use_tree.to_string().contains("super") { - use_tree_paths.insert(0, make::ext::ident_path("super")); - } - } + if (uses_exist_out_sel || !uses_exist_in_sel || !def_in_mod || !def_out_sel) + && let Some(first_path_in_use_tree) = use_tree_paths.first() + && first_path_in_use_tree.to_string().contains("super") + { + use_tree_paths.insert(0, make::ext::ident_path("super")); } let is_item = matches!( @@ -580,13 +613,13 @@ | Definition::Const(_) | Definition::Static(_) | Definition::Trait(_) - | Definition::TraitAlias(_) | Definition::TypeAlias(_) ); if (def_out_sel || !is_item) && use_stmt_not_in_sel { let use_ = make::use_( None, + None, make::use_tree(make::join_paths(use_tree_paths), None, None, false), ); self.use_items.insert(0, ast::Item::from(use_)); @@ -596,19 +629,6 @@ import_path_to_be_removed } - fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) -> ast::Item { - let super_path = make::ext::ident_path("super"); - let node_path = make::ext::ident_path(&node_syntax.to_string()); - let use_ = make::use_( - None, - make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false), - ); - - let item = ast::Item::from(use_); - self.use_items.insert(0, item.clone()); - item - } - fn process_use_stmt_for_import_resolve( &self, use_stmt: Option<ast::Use>, @@ -691,11 +711,9 @@ _ => source.file_id.original_file(ctx.db()).file_id(ctx.db()) == curr_file_id, }; - if have_same_parent { - if let ModuleSource::Module(module_) = source.value { - let in_sel = !selection_range.contains_range(module_.syntax().text_range()); - return (have_same_parent, in_sel); - } + if have_same_parent && let ModuleSource::Module(module_) = source.value { + let in_sel = !selection_range.contains_range(module_.syntax().text_range()); + return (have_same_parent, in_sel); } return (have_same_parent, false); @@ -772,12 +790,12 @@ .filter(|x| x.to_string() != path.to_string()) .filter_map(ast::UseTree::cast) .find_map(|use_tree| { - if let Some(upper_tree_path) = use_tree.path() { - if upper_tree_path.to_string() != path.to_string() { - use_tree_str.push(upper_tree_path.clone()); - get_use_tree_paths_from_path(upper_tree_path, use_tree_str); - return Some(use_tree); - } + if let Some(upper_tree_path) = use_tree.path() + && upper_tree_path.to_string() != path.to_string() + { + use_tree_str.push(upper_tree_path.clone()); + get_use_tree_paths_from_path(upper_tree_path, use_tree_str); + return Some(use_tree); } None })?; @@ -786,11 +804,11 @@ } fn add_change_vis(vis: Option<ast::Visibility>, node_or_token_opt: Option<syntax::SyntaxElement>) { - if vis.is_none() { - if let Some(node_or_token) = node_or_token_opt { - let pub_crate_vis = make::visibility_pub_crate().clone_for_update(); - ted::insert(ted::Position::before(node_or_token), pub_crate_vis.syntax()); - } + if vis.is_none() + && let Some(node_or_token) = node_or_token_opt + { + let pub_crate_vis = make::visibility_pub_crate().clone_for_update(); + ted::insert(ted::Position::before(node_or_token), pub_crate_vis.syntax()); } } @@ -1425,10 +1443,10 @@ struct B {} mod modname { - use super::B; - use super::A; + use super::B; + impl A { pub(crate) fn foo(x: B) {} } @@ -1740,4 +1758,27 @@ "#, ); } + + #[test] + fn test_miss_select_item() { + check_assist( + extract_module, + r#" +mod foo { + mod $0bar { + fn foo(){}$0 + } +} +"#, + r#" +mod foo { + mod modname { + pub(crate) mod bar { + fn foo(){} + } + } +} +"#, + ) + } }
diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index b9c4228..c56d0b3 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -16,8 +16,9 @@ SyntaxKind::*, SyntaxNode, T, ast::{ - self, AstNode, HasAttrs, HasGenericParams, HasName, HasVisibility, edit::IndentLevel, - edit_in_place::Indent, make, + self, AstNode, HasAttrs, HasGenericParams, HasName, HasVisibility, + edit::{AstNodeEdit, IndentLevel}, + make, }, match_ast, ted, }; @@ -110,20 +111,30 @@ let generics = generic_params.as_ref().map(|generics| generics.clone_for_update()); // resolve GenericArg in field_list to actual type - let field_list = field_list.clone_for_update(); - if let Some((target_scope, source_scope)) = + let field_list = if let Some((target_scope, source_scope)) = ctx.sema.scope(enum_ast.syntax()).zip(ctx.sema.scope(field_list.syntax())) { - PathTransform::generic_transformation(&target_scope, &source_scope) - .apply(field_list.syntax()); - } + let field_list = field_list.reset_indent(); + let field_list = + PathTransform::generic_transformation(&target_scope, &source_scope) + .apply(field_list.syntax()); + match_ast! { + match field_list { + ast::RecordFieldList(field_list) => Either::Left(field_list), + ast::TupleFieldList(field_list) => Either::Right(field_list), + _ => unreachable!(), + } + } + } else { + field_list.clone_for_update() + }; let def = create_struct_def(variant_name.clone(), &variant, &field_list, generics, &enum_ast); let enum_ast = variant.parent_enum(); let indent = enum_ast.indent_level(); - def.reindent_to(indent); + let def = def.indent(indent); ted::insert_all( ted::Position::before(enum_ast.syntax()), @@ -204,12 +215,12 @@ ast::GenericParam::LifetimeParam(lt) if matches!(token.kind(), T![lifetime_ident]) => { - if let Some(lt) = lt.lifetime() { - if lt.text().as_str() == token.text() { - *tag = true; - tagged_one = true; - break; - } + if let Some(lt) = lt.lifetime() + && lt.text().as_str() == token.text() + { + *tag = true; + tagged_one = true; + break; } } param if matches!(token.kind(), T![ident]) => { @@ -279,7 +290,7 @@ field_list.clone().into() } }; - field_list.reindent_to(IndentLevel::single()); + let field_list = field_list.indent(IndentLevel::single()); let strukt = make::struct_(enum_vis, name, generics, field_list).clone_for_update();
diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs index d843ac6..7f93506 100644 --- a/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -69,13 +69,14 @@ edit.replace(ty.syntax(), new_ty.syntax()); // Insert new alias - let ty_alias = make::ty_alias("Type", generic_params, None, None, Some((ty, None))) - .clone_for_update(); + let ty_alias = + make::ty_alias(None, "Type", generic_params, None, None, Some((ty, None))) + .clone_for_update(); - if let Some(cap) = ctx.config.snippet_cap { - if let Some(name) = ty_alias.name() { - edit.add_annotation(name.syntax(), builder.make_tabstop_before(cap)); - } + if let Some(cap) = ctx.config.snippet_cap + && let Some(name) = ty_alias.name() + { + edit.add_annotation(name.syntax(), builder.make_tabstop_before(cap)); } let indent = IndentLevel::from_node(node); @@ -111,17 +112,17 @@ match ty { ast::Type::PathType(ty) => { if let Some(path) = ty.path() { - if let Some(name_ref) = path.as_single_name_ref() { - if let Some(param) = known_generics.iter().find(|gp| { + if let Some(name_ref) = path.as_single_name_ref() + && let Some(param) = known_generics.iter().find(|gp| { match gp { ast::GenericParam::ConstParam(cp) => cp.name(), ast::GenericParam::TypeParam(tp) => tp.name(), _ => None, } .is_some_and(|n| n.text() == name_ref.text()) - }) { - generics.push(param); - } + }) + { + generics.push(param); } generics.extend( path.segments() @@ -160,20 +161,18 @@ .and_then(|lt| known_generics.iter().find(find_lifetime(<.text()))), ), ast::Type::ArrayType(ar) => { - if let Some(ast::Expr::PathExpr(p)) = ar.const_arg().and_then(|x| x.expr()) { - if let Some(path) = p.path() { - if let Some(name_ref) = path.as_single_name_ref() { - if let Some(param) = known_generics.iter().find(|gp| { - if let ast::GenericParam::ConstParam(cp) = gp { - cp.name().is_some_and(|n| n.text() == name_ref.text()) - } else { - false - } - }) { - generics.push(param); - } + if let Some(ast::Expr::PathExpr(p)) = ar.const_arg().and_then(|x| x.expr()) + && let Some(path) = p.path() + && let Some(name_ref) = path.as_single_name_ref() + && let Some(param) = known_generics.iter().find(|gp| { + if let ast::GenericParam::ConstParam(cp) = gp { + cp.name().is_some_and(|n| n.text() == name_ref.text()) + } else { + false } - } + }) + { + generics.push(param); } } _ => (),
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index db2d316..bd88e8b 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -200,7 +200,7 @@ } ExtractionKind::Constant => { let ast_ty = make.ty(&ty_string); - ast::Item::Const(make.item_const(None, pat_name, ast_ty, initializer)) + ast::Item::Const(make.item_const(None, None, pat_name, ast_ty, initializer)) .into() } ExtractionKind::Static => { @@ -404,11 +404,10 @@ } if let Some(expr) = node.parent().and_then(ast::StmtList::cast).and_then(|it| it.tail_expr()) + && expr.syntax() == &node { - if expr.syntax() == &node { - cov_mark::hit!(test_extract_var_last_expr); - return Some(Anchor::Before(node)); - } + cov_mark::hit!(test_extract_var_last_expr); + return Some(Anchor::Before(node)); } if let Some(parent) = node.parent() { @@ -427,10 +426,10 @@ } if let Some(stmt) = ast::Stmt::cast(node.clone()) { - if let ast::Stmt::ExprStmt(stmt) = stmt { - if stmt.expr().as_ref() == Some(to_extract) { - return Some(Anchor::Replace(stmt)); - } + if let ast::Stmt::ExprStmt(stmt) = stmt + && stmt.expr().as_ref() == Some(to_extract) + { + return Some(Anchor::Replace(stmt)); } return Some(Anchor::Before(node)); }
diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs index 3badc17..55de537 100644 --- a/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/crates/ide-assists/src/handlers/fix_visibility.rs
@@ -136,10 +136,6 @@ target_name = Some(t.name(db)); offset_target_and_file_id(db, t)? } - hir::ModuleDef::TraitAlias(t) => { - target_name = Some(t.name(db)); - offset_target_and_file_id(db, t)? - } hir::ModuleDef::TypeAlias(t) => { target_name = Some(t.name(db)); offset_target_and_file_id(db, t)?
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index ca66cb6..d8a2e03 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -2,9 +2,11 @@ use ide_db::{FxHashSet, path_transform::PathTransform}; use syntax::{ ast::{ - self, AstNode, HasGenericParams, HasName, HasVisibility as _, edit_in_place::Indent, make, + self, AstNode, HasGenericParams, HasName, HasVisibility as _, + edit::{AstNodeEdit, IndentLevel}, + make, }, - ted, + syntax_editor::Position, }; use crate::{ @@ -114,9 +116,13 @@ let source_scope = ctx.sema.scope(v.syntax()); let target_scope = ctx.sema.scope(strukt.syntax()); if let (Some(s), Some(t)) = (source_scope, target_scope) { - PathTransform::generic_transformation(&t, &s).apply(v.syntax()); + ast::Fn::cast( + PathTransform::generic_transformation(&t, &s).apply(v.syntax()), + ) + .unwrap_or(v) + } else { + v } - v } None => return, }; @@ -149,6 +155,7 @@ let ret_type = method_source.ret_type(); let f = make::fn_( + None, vis, fn_name, type_params, @@ -161,54 +168,67 @@ is_unsafe, is_gen, ) - .clone_for_update(); + .indent(IndentLevel(1)); + let item = ast::AssocItem::Fn(f.clone()); - // Get the impl to update, or create one if we need to. - let impl_def = match impl_def { - Some(impl_def) => edit.make_mut(impl_def), + let mut editor = edit.make_editor(strukt.syntax()); + let fn_: Option<ast::AssocItem> = match impl_def { + Some(impl_def) => match impl_def.assoc_item_list() { + Some(assoc_item_list) => { + let item = item.indent(IndentLevel::from_node(impl_def.syntax())); + assoc_item_list.add_items(&mut editor, vec![item.clone()]); + Some(item) + } + None => { + let assoc_item_list = make::assoc_item_list(Some(vec![item])); + editor.insert( + Position::last_child_of(impl_def.syntax()), + assoc_item_list.syntax(), + ); + assoc_item_list.assoc_items().next() + } + }, 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 where_clause = strukt.where_clause(); + let assoc_item_list = make::assoc_item_list(Some(vec![item])); let impl_def = make::impl_( + None, ty_params, ty_args, make::ty_path(make::ext::ident_path(name)), where_clause, - None, + Some(assoc_item_list), ) .clone_for_update(); // Fixup impl_def indentation let indent = strukt.indent_level(); - impl_def.reindent_to(indent); + let impl_def = impl_def.indent(indent); // Insert the impl block. let strukt = edit.make_mut(strukt.clone()); - ted::insert_all( - ted::Position::after(strukt.syntax()), + editor.insert_all( + Position::after(strukt.syntax()), vec![ make::tokens::whitespace(&format!("\n\n{indent}")).into(), impl_def.syntax().clone().into(), ], ); - - impl_def + impl_def.assoc_item_list().and_then(|list| list.assoc_items().next()) } }; - // Fixup function indentation. - // FIXME: Should really be handled by `AssocItemList::add_item` - f.reindent_to(impl_def.indent_level() + 1); - - let assoc_items = impl_def.get_or_create_assoc_item_list(); - assoc_items.add_item(f.clone().into()); - - if let Some(cap) = ctx.config.snippet_cap { - edit.add_tabstop_before(cap, f) + if let Some(cap) = ctx.config.snippet_cap + && let Some(fn_) = fn_ + { + let tabstop = edit.make_tabstop_before(cap); + editor.add_annotation(fn_.syntax(), tabstop); } + edit.add_file_edits(ctx.vfs_file_id(), editor); }, )?; }
diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 2bdc85e..e87dde5 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
@@ -20,7 +20,6 @@ HasGenericParams, HasName, HasTypeBounds, HasVisibility as astHasVisibility, Path, WherePred, edit::{self, AstNodeEdit}, - edit_in_place::AttrsOwnerEdit, make, }, ted::{self, Position}, @@ -255,7 +254,6 @@ delegee: &Delegee, edition: Edition, ) -> Option<ast::Impl> { - let delegate: ast::Impl; let db = ctx.db(); let ast_strukt = &strukt.strukt; let strukt_ty = make::ty_path(make::ext::ident_path(&strukt.name.to_string())); @@ -266,7 +264,8 @@ let bound_def = ctx.sema.source(delegee.to_owned())?.value; let bound_params = bound_def.generic_param_list(); - delegate = make::impl_trait( + let delegate = make::impl_trait( + None, delegee.is_unsafe(db), bound_params.clone(), bound_params.map(|params| params.to_generic_args()), @@ -304,7 +303,7 @@ let target_scope = ctx.sema.scope(strukt.strukt.syntax())?; let source_scope = ctx.sema.scope(bound_def.syntax())?; let transform = PathTransform::generic_transformation(&target_scope, &source_scope); - transform.apply(delegate.syntax()); + ast::Impl::cast(transform.apply(delegate.syntax())) } Delegee::Impls(trait_, old_impl) => { let old_impl = ctx.sema.source(old_impl.to_owned())?.value; @@ -358,20 +357,29 @@ // 2.3) Instantiate generics with `transform_impl`, this step also // remove unused params. - let mut trait_gen_args = old_impl.trait_()?.generic_arg_list(); - if let Some(trait_args) = &mut trait_gen_args { - *trait_args = trait_args.clone_for_update(); - transform_impl(ctx, ast_strukt, &old_impl, &transform_args, trait_args.syntax())?; - } + let trait_gen_args = old_impl.trait_()?.generic_arg_list().and_then(|trait_args| { + let trait_args = &mut trait_args.clone_for_update(); + if let Some(new_args) = transform_impl( + ctx, + ast_strukt, + &old_impl, + &transform_args, + trait_args.clone_subtree(), + ) { + *trait_args = new_args.clone_subtree(); + Some(new_args) + } else { + None + } + }); let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args()); - let path_type = make::ty(&trait_.name(db).display_no_db(edition).to_smolstr()).clone_for_update(); - transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type.syntax())?; - + let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?; // 3) Generate delegate trait impl - delegate = make::impl_trait( + let delegate = make::impl_trait( + None, trait_.is_unsafe(db), trait_gen_params, trait_gen_args, @@ -385,7 +393,6 @@ None, ) .clone_for_update(); - // Goto link : https://doc.rust-lang.org/reference/paths.html#qualified-paths let qualified_path_type = make::path_from_text(&format!("<{} as {}>", field_ty, delegate.trait_()?)); @@ -398,7 +405,7 @@ .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) { let item = item.clone_for_update(); - transform_impl(ctx, ast_strukt, &old_impl, &transform_args, item.syntax())?; + let item = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, item)?; let assoc = process_assoc_item(item, qualified_path_type.clone(), field_name)?; delegate_assoc_items.add_item(assoc); @@ -408,19 +415,18 @@ if let Some(wc) = delegate.where_clause() { remove_useless_where_clauses(&delegate.trait_()?, &delegate.self_ty()?, wc); } + Some(delegate) } } - - Some(delegate) } -fn transform_impl( +fn transform_impl<N: ast::AstNode>( ctx: &AssistContext<'_>, strukt: &ast::Struct, old_impl: &ast::Impl, args: &Option<GenericArgList>, - syntax: &syntax::SyntaxNode, -) -> Option<()> { + syntax: N, +) -> Option<N> { let source_scope = ctx.sema.scope(old_impl.self_ty()?.syntax())?; let target_scope = ctx.sema.scope(strukt.syntax())?; let hir_old_impl = ctx.sema.to_impl_def(old_impl)?; @@ -437,8 +443,7 @@ }, ); - transform.apply(syntax); - Some(()) + N::cast(transform.apply(syntax.syntax())) } fn remove_instantiated_params( @@ -570,9 +575,7 @@ let scope = ctx.sema.scope(item.syntax())?; let transform = PathTransform::adt_transformation(&scope, &scope, hir_adt, args.clone()); - transform.apply(item.syntax()); - - Some(item) + N::cast(transform.apply(item.syntax())) } fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> Option<()> { @@ -650,8 +653,7 @@ qual_path_ty: ast::Path, base_name: &str, ) -> Option<ast::AssocItem> { - let attrs = item.attrs(); - let assoc = match item { + match item { AssocItem::Const(c) => const_assoc_item(c, qual_path_ty), AssocItem::Fn(f) => func_assoc_item(f, qual_path_ty, base_name), AssocItem::MacroCall(_) => { @@ -660,18 +662,7 @@ None } AssocItem::TypeAlias(ta) => ty_assoc_item(ta, qual_path_ty), - }; - if let Some(assoc) = &assoc { - attrs.for_each(|attr| { - assoc.add_attr(attr.clone()); - // fix indentations - if let Some(tok) = attr.syntax().next_sibling_or_token() { - let pos = Position::after(tok); - ted::insert(pos, make::tokens::whitespace(" ")); - } - }) } - assoc } fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option<AssocItem> { @@ -685,6 +676,7 @@ // make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap()); let qualified_path = qualified_path(qual_path_ty, path_expr_segment); let inner = make::item_const( + item.attrs(), item.visibility(), item.name()?, item.ty()?, @@ -753,6 +745,7 @@ let body = make::block_expr(vec![], Some(call.into())).clone_for_update(); let func = make::fn_( + item.attrs(), item.visibility(), item.name()?, item.generic_param_list(), @@ -777,13 +770,14 @@ let ident = item.name()?.to_string(); let alias = make::ty_alias( + item.attrs(), ident.as_str(), item.generic_param_list(), None, item.where_clause(), Some((ty, None)), ) - .clone_for_update(); + .indent(edit::IndentLevel(1)); Some(AssocItem::TypeAlias(alias)) } @@ -1812,6 +1806,63 @@ } #[test] + fn test_ty_alias_attrs() { + check_assist( + generate_delegate_trait, + r#" +struct A; + +trait T { + #[cfg(test)] + type t; + #[cfg(not(test))] + type t; +} + +impl T for A { + #[cfg(test)] + type t = u32; + #[cfg(not(test))] + type t = bool; +} + +struct B { + a$0: A, +} +"#, + r#" +struct A; + +trait T { + #[cfg(test)] + type t; + #[cfg(not(test))] + type t; +} + +impl T for A { + #[cfg(test)] + type t = u32; + #[cfg(not(test))] + type t = bool; +} + +struct B { + a: A, +} + +impl T for B { + #[cfg(test)] + type t = <A as T>::t; + + #[cfg(not(test))] + type t = <A as T>::t; +} +"#, + ); + } + + #[test] fn assoc_items_attributes_mutably_cloned() { check_assist( generate_delegate_trait,
diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs index 73a69c8..06fef4a 100644 --- a/crates/ide-assists/src/handlers/generate_derive.rs +++ b/crates/ide-assists/src/handlers/generate_derive.rs
@@ -1,6 +1,8 @@ use syntax::{ + SyntaxKind::{ATTR, COMMENT, WHITESPACE}, T, - ast::{self, AstNode, HasAttrs, edit_in_place::AttrsOwnerEdit, make}, + ast::{self, AstNode, HasAttrs, edit::IndentLevel, make}, + syntax_editor::{Element, Position}, }; use crate::{AssistContext, AssistId, Assists}; @@ -48,8 +50,20 @@ )) .clone_for_update(); - let nominal = edit.make_mut(nominal); - nominal.add_attr(derive.clone()); + let mut editor = edit.make_editor(nominal.syntax()); + let indent = IndentLevel::from_node(nominal.syntax()); + let after_attrs_and_comments = nominal + .syntax() + .children_with_tokens() + .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) + .map_or(Position::first_child_of(nominal.syntax()), Position::before); + editor.insert_all( + after_attrs_and_comments, + vec![ + derive.syntax().syntax_element(), + make::tokens::whitespace(&format!("\n{indent}")).syntax_element(), + ], + ); let delimiter = derive .meta() @@ -58,8 +72,9 @@ .expect("failed to get token tree out of Meta") .r_paren_token() .expect("make::attr_outer was expected to have a R_PAREN"); - - edit.add_tabstop_before_token(cap, delimiter); + let tabstop_before = edit.make_tabstop_before(cap); + editor.add_annotation(delimiter, tabstop_before); + edit.add_file_edits(ctx.vfs_file_id(), editor); } Some(_) => { // Just move the cursor.
diff --git a/crates/ide-assists/src/handlers/generate_documentation_template.rs b/crates/ide-assists/src/handlers/generate_documentation_template.rs index d4d1b34..77232df 100644 --- a/crates/ide-assists/src/handlers/generate_documentation_template.rs +++ b/crates/ide-assists/src/handlers/generate_documentation_template.rs
@@ -148,11 +148,11 @@ let self_name = self_name(ast_func); format_to!(example, "use {use_path};\n\n"); - if let Some(self_name) = &self_name { - if let Some(mut_) = is_ref_mut_self(ast_func) { - let mut_ = if mut_ { "mut " } else { "" }; - format_to!(example, "let {mut_}{self_name} = ;\n"); - } + if let Some(self_name) = &self_name + && let Some(mut_) = is_ref_mut_self(ast_func) + { + let mut_ = if mut_ { "mut " } else { "" }; + format_to!(example, "let {mut_}{self_name} = ;\n"); } for param_name in &ref_mut_params { format_to!(example, "let mut {param_name} = ;\n"); @@ -170,10 +170,10 @@ format_to!(example, "{function_call};\n"); } // Check the mutated values - if let Some(self_name) = &self_name { - if is_ref_mut_self(ast_func) == Some(true) { - format_to!(example, "assert_eq!({self_name}, );"); - } + if let Some(self_name) = &self_name + && is_ref_mut_self(ast_func) == Some(true) + { + format_to!(example, "assert_eq!({self_name}, );"); } for param_name in &ref_mut_params { format_to!(example, "assert_eq!({param_name}, );"); @@ -313,12 +313,28 @@ /// `None` if function without a body; some bool to guess if function can panic fn can_panic(ast_func: &ast::Fn) -> Option<bool> { let body = ast_func.body()?.to_string(); - let can_panic = body.contains("panic!(") - // FIXME it would be better to not match `debug_assert*!` macro invocations - || body.contains("assert!(") - || body.contains(".unwrap()") - || body.contains(".expect("); - Some(can_panic) + let mut iter = body.chars(); + let assert_postfix = |s| { + ["!(", "_eq!(", "_ne!(", "_matches!("].iter().any(|postfix| str::starts_with(s, postfix)) + }; + + while !iter.as_str().is_empty() { + let s = iter.as_str(); + iter.next(); + if s.strip_prefix("debug_assert").is_some_and(assert_postfix) { + iter.nth(10); + continue; + } + if s.strip_prefix("assert").is_some_and(assert_postfix) + || s.starts_with("panic!(") + || s.starts_with(".unwrap()") + || s.starts_with(".expect(") + { + return Some(true); + } + } + + Some(false) } /// Helper function to get the name that should be given to `self` arguments @@ -678,6 +694,24 @@ } #[test] + fn guesses_debug_assert_macro_cannot_panic() { + check_assist( + generate_documentation_template, + r#" +pub fn $0debug_panics_if_not(a: bool) { + debug_assert!(a == true); +} +"#, + r#" +/// . +pub fn debug_panics_if_not(a: bool) { + debug_assert!(a == true); +} +"#, + ); + } + + #[test] fn guesses_assert_macro_can_panic() { check_assist( generate_documentation_template, @@ -700,6 +734,28 @@ } #[test] + fn guesses_assert_eq_macro_can_panic() { + check_assist( + generate_documentation_template, + r#" +pub fn $0panics_if_not(a: bool) { + assert_eq!(a, true); +} +"#, + r#" +/// . +/// +/// # Panics +/// +/// Panics if . +pub fn panics_if_not(a: bool) { + assert_eq!(a, true); +} +"#, + ); + } + + #[test] fn guesses_unwrap_can_panic() { check_assist( generate_documentation_template,
diff --git a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index b63baa6..0b7eca2 100644 --- a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
@@ -94,6 +94,7 @@ // Insert new alias let ty_alias = make::ty_alias( + None, &alias_name, generic_params, None, @@ -111,10 +112,10 @@ ], ); - if let Some(cap) = ctx.config.snippet_cap { - if let Some(name) = ty_alias.name() { - edit.add_annotation(name.syntax(), builder.make_placeholder_snippet(cap)); - } + if let Some(cap) = ctx.config.snippet_cap + && let Some(name) = ty_alias.name() + { + edit.add_annotation(name.syntax(), builder.make_placeholder_snippet(cap)); } builder.add_file_edits(ctx.vfs_file_id(), edit);
diff --git a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index af949a0..cb83c67 100644 --- a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
@@ -1,3 +1,4 @@ +use hir::next_solver::{DbInterner, TypingMode}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use syntax::ast::{self, AstNode, HasName}; @@ -80,17 +81,20 @@ sema: &'_ hir::Semantics<'_, RootDatabase>, variant: &ast::Variant, ) -> Option<()> { + let db = sema.db; let variant = sema.to_def(variant)?; - let enum_ = variant.parent_enum(sema.db); - let krate = enum_.module(sema.db).krate(); - + let krate = variant.module(db).krate(); let from_trait = FamousDefs(sema, krate).core_convert_From()?; + let interner = DbInterner::new_with(db, Some(krate.base()), None); + use hir::next_solver::infer::DbInternerInferExt; + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let enum_type = enum_.ty(sema.db); - - let wrapped_type = variant.fields(sema.db).first()?.ty(sema.db); - - if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) { Some(()) } else { None } + let variant = variant.instantiate_infer(&infcx); + let enum_ = variant.parent_enum(sema.db); + let field_ty = variant.fields(sema.db).first()?.ty(sema.db); + let enum_ty = enum_.ty(sema.db); + tracing::debug!(?enum_, ?field_ty, ?enum_ty); + enum_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] @@ -119,15 +123,19 @@ ); } + // FIXME(next-solver): it would be nice to not be *required* to resolve the + // path in order to properly generate assists #[test] fn test_generate_from_impl_for_enum_complicated_path() { check_assist( generate_from_impl_for_enum, r#" //- minicore: from +mod foo { pub mod bar { pub mod baz { pub struct Boo; } } } enum A { $0One(foo::bar::baz::Boo) } "#, r#" +mod foo { pub mod bar { pub mod baz { pub struct Boo; } } } enum A { One(foo::bar::baz::Boo) } impl From<foo::bar::baz::Boo> for A {
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 78ae815..88ed6f9 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -70,10 +70,10 @@ let TargetInfo { target_module, adt_info, target, file } = fn_target_info(ctx, path, &call, fn_name)?; - if let Some(m) = target_module { - if !is_editable_crate(m.krate(), ctx.db()) { - return None; - } + if let Some(m) = target_module + && !is_editable_crate(m.krate(), ctx.db()) + { + return None; } let function_builder = @@ -189,7 +189,7 @@ ))); // FIXME: adt may have generic params. - let impl_ = make::impl_(None, None, name, None, None).clone_for_update(); + 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()); @@ -316,7 +316,7 @@ 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.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( @@ -365,6 +365,7 @@ Visibility::Pub => Some(make::visibility_pub()), }; let fn_def = make::fn_( + None, visibility, self.fn_name, self.generic_param_list, @@ -743,17 +744,30 @@ let where_preds: Vec<ast::WherePred> = where_preds.into_iter().map(|it| it.node.clone_for_update()).collect(); - // 4. Rewrite paths - if let Some(param) = generic_params.first() { - let source_scope = ctx.sema.scope(param.syntax())?; - let target_scope = ctx.sema.scope(&target.parent())?; - if source_scope.module() != target_scope.module() { + let (generic_params, where_preds): (Vec<ast::GenericParam>, Vec<ast::WherePred>) = + if let Some(param) = generic_params.first() + && let source_scope = ctx.sema.scope(param.syntax())? + && let target_scope = ctx.sema.scope(&target.parent())? + && source_scope.module() != target_scope.module() + { + // 4. Rewrite paths let transform = PathTransform::generic_transformation(&target_scope, &source_scope); let generic_params = generic_params.iter().map(|it| it.syntax()); let where_preds = where_preds.iter().map(|it| it.syntax()); - transform.apply_all(generic_params.chain(where_preds)); - } - } + 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 where_clause = @@ -3117,4 +3131,32 @@ "#, ) } + + #[test] + fn no_generate_method_by_keyword() { + check_assist_not_applicable( + generate_function, + r#" +fn main() { + s.super$0(); +} + "#, + ); + check_assist_not_applicable( + generate_function, + r#" +fn main() { + s.Self$0(); +} + "#, + ); + check_assist_not_applicable( + generate_function, + r#" +fn main() { + s.self$0(); +} + "#, + ); + } }
diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index 20ee925..e42d0ed 100644 --- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -263,6 +263,7 @@ let body = make::block_expr([], Some(body)); make::fn_( + None, strukt.visibility(), fn_name, None, @@ -299,6 +300,7 @@ // Make the setter fn make::fn_( + None, strukt.visibility(), fn_name, None, @@ -433,12 +435,11 @@ new_fn.indent(1.into()); // Insert a tabstop only for last method we generate - if i == record_fields_count - 1 { - if let Some(cap) = ctx.config.snippet_cap { - if let Some(name) = new_fn.name() { - builder.add_tabstop_before(cap, name); - } - } + if i == record_fields_count - 1 + && let Some(cap) = ctx.config.snippet_cap + && let Some(name) = new_fn.name() + { + builder.add_tabstop_before(cap, name); } assoc_item_list.add_item(new_fn.clone().into());
diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs index 14601ca..77eb8ef 100644 --- a/crates/ide-assists/src/handlers/generate_impl.rs +++ b/crates/ide-assists/src/handlers/generate_impl.rs
@@ -1,12 +1,17 @@ use syntax::{ - ast::{self, AstNode, HasName, edit_in_place::Indent, make}, + ast::{self, AstNode, HasGenericParams, HasName, edit_in_place::Indent, make}, syntax_editor::{Position, SyntaxEditor}, }; -use crate::{AssistContext, AssistId, Assists, utils}; +use crate::{ + AssistContext, AssistId, Assists, + utils::{self, DefaultMethods, IgnoreAssocItems}, +}; -fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) { +fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &impl Indent) { let indent = nominal.indent_level(); + + impl_.indent(indent); editor.insert_all( Position::after(nominal.syntax()), vec![ @@ -53,11 +58,11 @@ let mut editor = edit.make_editor(nominal.syntax()); // Add a tabstop after the left curly brace - if let Some(cap) = ctx.config.snippet_cap { - if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) { - let tabstop = edit.make_tabstop_after(cap); - editor.add_annotation(l_curly, tabstop); - } + if let Some(cap) = ctx.config.snippet_cap + && let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) + { + let tabstop = edit.make_tabstop_after(cap); + editor.add_annotation(l_curly, tabstop); } insert_impl(&mut editor, &impl_, &nominal); @@ -120,6 +125,126 @@ ) } +// Assist: generate_impl_trait +// +// Adds this trait impl for a type. +// +// ``` +// trait $0Foo { +// fn foo(&self) -> i32; +// } +// ``` +// -> +// ``` +// trait Foo { +// fn foo(&self) -> i32; +// } +// +// impl Foo for ${1:_} { +// fn foo(&self) -> i32 { +// $0todo!() +// } +// } +// ``` +pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let name = ctx.find_node_at_offset::<ast::Name>()?; + let trait_ = ast::Trait::cast(name.syntax().parent()?)?; + let target_scope = ctx.sema.scope(trait_.syntax())?; + let hir_trait = ctx.sema.to_def(&trait_)?; + + let target = trait_.syntax().text_range(); + acc.add( + AssistId::generate("generate_impl_trait"), + format!("Generate `{name}` impl for type"), + target, + |edit| { + let mut editor = edit.make_editor(trait_.syntax()); + + let holder_arg = ast::GenericArg::TypeArg(make::type_arg(make::ty_placeholder())); + let missing_items = utils::filter_assoc_items( + &ctx.sema, + &hir_trait.items(ctx.db()), + DefaultMethods::No, + IgnoreAssocItems::DocHiddenAttrPresent, + ); + + let trait_gen_args = trait_.generic_param_list().map(|list| { + make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone())) + }); + + let make_impl_ = |body| { + make::impl_trait( + None, + trait_.unsafe_token().is_some(), + None, + trait_gen_args.clone(), + None, + None, + false, + make::ty(&name.text()), + make::ty_placeholder(), + None, + None, + body, + ) + .clone_for_update() + }; + + let impl_ = if missing_items.is_empty() { + make_impl_(None) + } else { + let impl_ = make_impl_(None); + let assoc_items = utils::add_trait_assoc_items_to_impl( + &ctx.sema, + ctx.config, + &missing_items, + hir_trait, + &impl_, + &target_scope, + ); + let assoc_item_list = make::assoc_item_list(Some(assoc_items)); + make_impl_(Some(assoc_item_list)) + }; + + if let Some(cap) = ctx.config.snippet_cap { + if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) { + for generic in generics.generic_args() { + let placeholder = edit.make_placeholder_snippet(cap); + editor.add_annotation(generic.syntax(), placeholder); + } + } + + if let Some(ty) = impl_.self_ty() { + let placeholder = edit.make_placeholder_snippet(cap); + editor.add_annotation(ty.syntax(), placeholder); + } + + if let Some(expr) = + impl_.assoc_item_list().and_then(|it| it.assoc_items().find_map(extract_expr)) + { + let tabstop = edit.make_tabstop_before(cap); + editor.add_annotation(expr.syntax(), tabstop); + } else if let Some(l_curly) = + impl_.assoc_item_list().and_then(|it| it.l_curly_token()) + { + let tabstop = edit.make_tabstop_after(cap); + editor.add_annotation(l_curly, tabstop); + } + } + + insert_impl(&mut editor, &impl_, &trait_); + edit.add_file_edits(ctx.vfs_file_id(), editor); + }, + ) +} + +fn extract_expr(item: ast::AssocItem) -> Option<ast::Expr> { + let ast::AssocItem::Fn(f) = item else { + return None; + }; + f.body()?.tail_expr() +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_target}; @@ -492,4 +617,209 @@ "#, ); } + + #[test] + fn test_add_impl_trait() { + check_assist( + generate_impl_trait, + r#" + trait $0Foo { + fn foo(&self) -> i32; + + fn bar(&self) -> i32 { + self.foo() + } + } + "#, + r#" + trait Foo { + fn foo(&self) -> i32; + + fn bar(&self) -> i32 { + self.foo() + } + } + + impl Foo for ${1:_} { + fn foo(&self) -> i32 { + $0todo!() + } + } + "#, + ); + } + + #[test] + fn test_add_impl_trait_use_generic() { + check_assist( + generate_impl_trait, + r#" + trait $0Foo<T> { + fn foo(&self) -> T; + + fn bar(&self) -> T { + self.foo() + } + } + "#, + r#" + trait Foo<T> { + fn foo(&self) -> T; + + fn bar(&self) -> T { + self.foo() + } + } + + impl Foo<${1:_}> for ${2:_} { + fn foo(&self) -> _ { + $0todo!() + } + } + "#, + ); + check_assist( + generate_impl_trait, + r#" + trait $0Foo<T, U> { + fn foo(&self) -> T; + + fn bar(&self) -> T { + self.foo() + } + } + "#, + r#" + trait Foo<T, U> { + fn foo(&self) -> T; + + fn bar(&self) -> T { + self.foo() + } + } + + impl Foo<${1:_}, ${2:_}> for ${3:_} { + fn foo(&self) -> _ { + $0todo!() + } + } + "#, + ); + } + + #[test] + fn test_add_impl_trait_docs() { + check_assist( + generate_impl_trait, + r#" + /// foo + trait $0Foo { + /// foo method + fn foo(&self) -> i32; + + fn bar(&self) -> i32 { + self.foo() + } + } + "#, + r#" + /// foo + trait Foo { + /// foo method + fn foo(&self) -> i32; + + fn bar(&self) -> i32 { + self.foo() + } + } + + impl Foo for ${1:_} { + fn foo(&self) -> i32 { + $0todo!() + } + } + "#, + ); + } + + #[test] + fn test_add_impl_trait_assoc_types() { + check_assist( + generate_impl_trait, + r#" + trait $0Foo { + type Output; + + fn foo(&self) -> Self::Output; + } + "#, + r#" + trait Foo { + type Output; + + fn foo(&self) -> Self::Output; + } + + impl Foo for ${1:_} { + type Output; + + fn foo(&self) -> Self::Output { + $0todo!() + } + } + "#, + ); + } + + #[test] + fn test_add_impl_trait_indent() { + check_assist( + generate_impl_trait, + r#" + mod foo { + mod bar { + trait $0Foo { + type Output; + + fn foo(&self) -> Self::Output; + } + } + } + "#, + r#" + mod foo { + mod bar { + trait Foo { + type Output; + + fn foo(&self) -> Self::Output; + } + + impl Foo for ${1:_} { + type Output; + + fn foo(&self) -> Self::Output { + $0todo!() + } + } + } + } + "#, + ); + } + + #[test] + fn test_add_impl_trait_empty() { + check_assist( + generate_impl_trait, + r#" + trait $0Foo {} + "#, + r#" + trait Foo {} + + impl Foo for ${1:_} {$0} + "#, + ); + } }
diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index 9c4bcdd..ae1ae24 100644 --- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
@@ -104,7 +104,14 @@ format!("Generate `{trait_new}` impl from this `{trait_name}` trait"), target, |edit| { - edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}")); + edit.insert( + target.start(), + if ctx.config.snippet_cap.is_some() { + format!("$0{impl_def}\n\n{indent}") + } else { + format!("{impl_def}\n\n{indent}") + }, + ); }, ) } @@ -161,7 +168,10 @@ #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; + use crate::{ + AssistConfig, + tests::{TEST_CONFIG, check_assist, check_assist_not_applicable, check_assist_with_config}, + }; use super::*; @@ -405,4 +415,41 @@ "#, ); } + + #[test] + fn no_snippets() { + check_assist_with_config( + generate_mut_trait_impl, + AssistConfig { snippet_cap: None, ..TEST_CONFIG }, + r#" +//- minicore: index +pub enum Axis { X = 0, Y = 1, Z = 2 } + +impl<T> core::ops::Index$0<Axis> for [T; 3] { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + &self[index as usize] + } +} +"#, + r#" +pub enum Axis { X = 0, Y = 1, Z = 2 } + +impl<T> core::ops::IndexMut<Axis> for [T; 3] { + fn index_mut(&mut self, index: Axis) -> &mut Self::Output { + &mut self[index as usize] + } +} + +impl<T> core::ops::Index<Axis> for [T; 3] { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + &self[index as usize] + } +} +"#, + ); + } }
diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs index 5bda122..ac4a54a 100644 --- a/crates/ide-assists/src/handlers/generate_new.rs +++ b/crates/ide-assists/src/handlers/generate_new.rs
@@ -134,6 +134,7 @@ let ret_type = make::ret_type(make::ty_path(make::ext::ident_path("Self"))); let fn_ = make::fn_( + None, strukt.visibility(), make::name("new"), None, @@ -168,7 +169,7 @@ ); fn_.syntax().clone() } else { - let items = vec![either::Either::Right(ast::AssocItem::Fn(fn_))]; + let items = vec![ast::AssocItem::Fn(fn_)]; let list = make::assoc_item_list(Some(items)); editor.insert(Position::after(impl_def.syntax()), list.syntax()); list.syntax().clone() @@ -176,7 +177,7 @@ } else { // Generate a new impl to add the method to let indent_level = strukt.indent_level(); - let body = vec![either::Either::Right(ast::AssocItem::Fn(fn_))]; + let body = vec![ast::AssocItem::Fn(fn_)]; let list = make::assoc_item_list(Some(body)); let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list));
diff --git a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 4e95ceb..deef4a9 100644 --- a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
@@ -1,4 +1,5 @@ use ast::make; +use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; use ide_db::{ RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast, @@ -6,13 +7,12 @@ }; use syntax::{ TokenText, - ast::{self, AstNode, HasGenericParams, HasName, edit, edit_in_place::Indent}, + ast::{self, AstNode, HasAttrs, HasGenericParams, HasName, edit, edit_in_place::Indent}, }; use crate::{ AssistId, assist_context::{AssistContext, Assists}, - utils::add_cfg_attrs_to, }; // Assist: generate_single_field_struct_from @@ -48,6 +48,7 @@ let strukt_name = ctx.find_node_at_offset::<ast::Name>()?; let adt = ast::Adt::cast(strukt_name.syntax().parent()?)?; let ast::Adt::Struct(strukt) = adt else { + tracing::debug!(?adt); return None; }; @@ -58,10 +59,12 @@ let constructors = make_constructors(ctx, module, &types); if constructors.iter().filter(|expr| expr.is_none()).count() != 1 { + tracing::debug!(?constructors); return None; } let main_field_i = constructors.iter().position(Option::is_none)?; if from_impl_exists(&strukt, main_field_i, &ctx.sema).is_some() { + tracing::debug!(?strukt, ?main_field_i); return None; } @@ -90,6 +93,7 @@ let fn_ = make::fn_( None, + None, make::name("from"), None, None, @@ -110,8 +114,12 @@ .clone_for_update(); fn_.indent(1.into()); + let cfg_attrs = strukt + .attrs() + .filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); let impl_ = make::impl_trait( + cfg_attrs, false, None, trait_gen_args, @@ -128,8 +136,6 @@ impl_.get_or_create_assoc_item_list().add_item(fn_.into()); - add_cfg_attrs_to(&strukt, &impl_); - impl_.reindent_to(indent); builder.insert(strukt.syntax().text_range().end(), format!("\n\n{indent}{impl_}")); @@ -198,6 +204,7 @@ }) } +#[tracing::instrument(ret)] fn from_impl_exists( strukt: &ast::Struct, main_field_i: usize, @@ -207,9 +214,15 @@ let strukt = sema.to_def(strukt)?; let krate = strukt.krate(db); let from_trait = FamousDefs(sema, krate).core_convert_From()?; - let ty = strukt.fields(db).get(main_field_i)?.ty(db); + let interner = DbInterner::new_with(db, Some(krate.base()), None); + use hir::next_solver::infer::DbInternerInferExt; + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - strukt.ty(db).impls_trait(db, from_trait, &[ty]).then_some(()) + let strukt = strukt.instantiate_infer(&infcx); + let field_ty = strukt.fields(db).get(main_field_i)?.ty(db); + let struct_ty = strukt.ty(db); + tracing::debug!(?strukt, ?field_ty, ?struct_ty); + struct_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) } #[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 92a4bd3..56500cf 100644 --- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
@@ -2,12 +2,8 @@ use ide_db::assists::AssistId; use syntax::{ AstNode, SyntaxKind, T, - ast::{ - self, HasGenericParams, HasName, HasVisibility, - edit_in_place::{HasVisibilityEdit, Indent}, - make, - }, - ted::{self, Position}, + ast::{self, HasGenericParams, HasName, HasVisibility, edit_in_place::Indent, make}, + syntax_editor::{Position, SyntaxEditor}, }; // NOTES : @@ -88,8 +84,8 @@ return None; } - let assoc_items = impl_ast.assoc_item_list()?; - let first_element = assoc_items.assoc_items().next(); + let impl_assoc_items = impl_ast.assoc_item_list()?; + let first_element = impl_assoc_items.assoc_items().next(); first_element.as_ref()?; let impl_name = impl_ast.self_ty()?; @@ -99,20 +95,16 @@ "Generate trait from impl", impl_ast.syntax().text_range(), |builder| { - let impl_ast = builder.make_mut(impl_ast); - let trait_items = assoc_items.clone_for_update(); - let impl_items = builder.make_mut(assoc_items); - let impl_name = builder.make_mut(impl_name); + let trait_items: ast::AssocItemList = { + let trait_items = impl_assoc_items.clone_subtree(); + let mut trait_items_editor = SyntaxEditor::new(trait_items.syntax().clone()); - trait_items.assoc_items().for_each(|item| { - strip_body(&item); - remove_items_visibility(&item); - }); - - impl_items.assoc_items().for_each(|item| { - remove_items_visibility(&item); - }); - + trait_items.assoc_items().for_each(|item| { + strip_body(&mut trait_items_editor, &item); + remove_items_visibility(&mut trait_items_editor, &item); + }); + ast::AssocItemList::cast(trait_items_editor.finish().new_root().clone()).unwrap() + }; let trait_ast = make::trait_( false, "NewTrait", @@ -130,6 +122,7 @@ trait_name_ref.syntax().clone().into(), make::tokens::single_space().into(), make::token(T![for]).into(), + make::tokens::single_space().into(), ]; if let Some(params) = impl_ast.generic_param_list() { @@ -137,10 +130,15 @@ elements.insert(1, gen_args.syntax().clone().into()); } - ted::insert_all(Position::before(impl_name.syntax()), elements); + let mut editor = builder.make_editor(impl_ast.syntax()); + impl_assoc_items.assoc_items().for_each(|item| { + remove_items_visibility(&mut editor, &item); + }); + + editor.insert_all(Position::before(impl_name.syntax()), elements); // Insert trait before TraitImpl - ted::insert_all_raw( + editor.insert_all( Position::before(impl_ast.syntax()), vec![ trait_ast.syntax().clone().into(), @@ -150,11 +148,12 @@ // Link the trait name & trait ref names together as a placeholder snippet group if let Some(cap) = ctx.config.snippet_cap { - builder.add_placeholder_snippet_group( - cap, - vec![trait_name.syntax().clone(), trait_name_ref.syntax().clone()], - ); + let placeholder = builder.make_placeholder_snippet(cap); + editor.add_annotation(trait_name.syntax(), placeholder); + editor.add_annotation(trait_name_ref.syntax(), placeholder); } + + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ); @@ -162,31 +161,33 @@ } /// `E0449` Trait items always share the visibility of their trait -fn remove_items_visibility(item: &ast::AssocItem) { +fn remove_items_visibility(editor: &mut SyntaxEditor, item: &ast::AssocItem) { if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) { if let Some(vis) = has_vis.visibility() && let Some(token) = vis.syntax().next_sibling_or_token() && token.kind() == SyntaxKind::WHITESPACE { - ted::remove(token); + editor.delete(token); } - has_vis.set_visibility(None); + if let Some(vis) = has_vis.visibility() { + editor.delete(vis.syntax()); + } } } -fn strip_body(item: &ast::AssocItem) { - if let ast::AssocItem::Fn(f) = item { - if let Some(body) = f.body() { - // In contrast to function bodies, we want to see no ws before a semicolon. - // So let's remove them if we see any. - if let Some(prev) = body.syntax().prev_sibling_or_token() { - if prev.kind() == SyntaxKind::WHITESPACE { - ted::remove(prev); - } - } - - ted::replace(body.syntax(), make::tokens::semicolon()); +fn strip_body(editor: &mut SyntaxEditor, item: &ast::AssocItem) { + if let ast::AssocItem::Fn(f) = item + && let Some(body) = f.body() + { + // In contrast to function bodies, we want to see no ws before a semicolon. + // So let's remove them if we see any. + if let Some(prev) = body.syntax().prev_sibling_or_token() + && prev.kind() == SyntaxKind::WHITESPACE + { + editor.delete(prev); } + + editor.replace(body.syntax(), make::tokens::semicolon()); }; }
diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index b7b8bc6..5367350 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs
@@ -393,19 +393,17 @@ // `FileReference` incorrect 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() { - if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) { - if let Some(t) = imp.self_ty() { - while let Some(self_tok) = 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(); - ted::replace(self_tok, replace_with); - } - } + while let Some(self_tok) = 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(); + ted::replace(self_tok, replace_with); } } @@ -415,10 +413,10 @@ for stmt in fn_body.statements() { if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) { for has_token in let_stmt.syntax().children_with_tokens() { - if let Some(node) = has_token.as_node() { - if let Some(ident_pat) = ast::IdentPat::cast(node.to_owned()) { - func_let_vars.insert(ident_pat.syntax().text().to_string()); - } + if let Some(node) = has_token.as_node() + && let Some(ident_pat) = ast::IdentPat::cast(node.to_owned()) + { + func_let_vars.insert(ident_pat.syntax().text().to_string()); } } } @@ -534,11 +532,15 @@ } } - if let Some(generic_arg_list) = generic_arg_list.clone() { - if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) - { + 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( PathTransform::function_call(target, source, function, generic_arg_list) - .apply(body.syntax()); + .apply(body.syntax()), + ) { + body = new_body; } }
diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs index 4511072..ae8d130 100644 --- a/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/crates/ide-assists/src/handlers/inline_type_alias.rs
@@ -9,10 +9,11 @@ search::FileReference, }; use itertools::Itertools; +use syntax::ast::syntax_factory::SyntaxFactory; +use syntax::syntax_editor::SyntaxEditor; use syntax::{ AstNode, NodeOrToken, SyntaxNode, - ast::{self, HasGenericParams, HasName, make}, - ted, + ast::{self, HasGenericParams, HasName}, }; use crate::{ @@ -68,37 +69,41 @@ let mut definition_deleted = false; let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| { - builder.edit_file(file_id); + let source = ctx.sema.parse(file_id); + let mut 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) }); - path_type_uses .iter() .flat_map(ast_to_remove_for_path_in_use_stmt) - .for_each(|x| builder.delete(x.syntax().text_range())); + .for_each(|x| editor.delete(x.syntax())); + for (target, replacement) in path_types.into_iter().filter_map(|path_type| { - let replacement = inline(&ast_alias, &path_type)?.to_text(&concrete_type); - let target = path_type.syntax().text_range(); + let replacement = + inline(&ast_alias, &path_type)?.replace_generic(&concrete_type); + let target = path_type.syntax().clone(); Some((target, replacement)) }) { - builder.replace(target, replacement); + editor.replace(target, replacement); } - if file_id == ctx.vfs_file_id() { - builder.delete(ast_alias.syntax().text_range()); + if file_id.file_id(ctx.db()) == ctx.vfs_file_id() { + editor.delete(ast_alias.syntax()); definition_deleted = true; } + builder.add_file_edits(file_id.file_id(ctx.db()), editor); }; for (file_id, refs) in usages.into_iter() { - inline_refs_for_file(file_id.file_id(ctx.db()), refs); + inline_refs_for_file(file_id, refs); } if !definition_deleted { - builder.edit_file(ctx.vfs_file_id()); - builder.delete(ast_alias.syntax().text_range()); + let mut editor = builder.make_editor(ast_alias.syntax()); + editor.delete(ast_alias.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor) } }, ) @@ -146,23 +151,26 @@ } } - let target = alias_instance.syntax().text_range(); - acc.add( AssistId::refactor_inline("inline_type_alias"), "Inline type alias", - target, - |builder| builder.replace(target, replacement.to_text(&concrete_type)), + alias_instance.syntax().text_range(), + |builder| { + let mut editor = builder.make_editor(alias_instance.syntax()); + let replace = replacement.replace_generic(&concrete_type); + editor.replace(alias_instance.syntax(), replace); + builder.add_file_edits(ctx.vfs_file_id(), editor); + }, ) } impl Replacement { - fn to_text(&self, concrete_type: &ast::Type) -> String { + fn replace_generic(&self, concrete_type: &ast::Type) -> SyntaxNode { match self { Replacement::Generic { lifetime_map, const_and_type_map } => { create_replacement(lifetime_map, const_and_type_map, concrete_type) } - Replacement::Plain => concrete_type.to_string(), + Replacement::Plain => concrete_type.syntax().clone_subtree().clone_for_update(), } } } @@ -199,8 +207,8 @@ alias_generics: &ast::GenericParamList, ) -> Option<Self> { let mut inner = FxHashMap::default(); - - let wildcard_lifetime = make::lifetime("'_"); + let make = SyntaxFactory::without_mappings(); + let wildcard_lifetime = make.lifetime("'_"); let lifetimes = alias_generics .lifetime_params() .filter_map(|lp| lp.lifetime()) @@ -299,15 +307,14 @@ lifetime_map: &LifetimeMap, const_and_type_map: &ConstAndTypeMap, concrete_type: &ast::Type, -) -> String { - let updated_concrete_type = concrete_type.clone_for_update(); - let mut replacements = Vec::new(); - let mut removals = Vec::new(); +) -> SyntaxNode { + let updated_concrete_type = concrete_type.syntax().clone_subtree(); + let mut editor = SyntaxEditor::new(updated_concrete_type.clone()); - for syntax in updated_concrete_type.syntax().descendants() { - let syntax_string = syntax.to_string(); - let syntax_str = syntax_string.as_str(); + let mut replacements: Vec<(SyntaxNode, SyntaxNode)> = Vec::new(); + let mut removals: Vec<NodeOrToken<SyntaxNode, _>> = Vec::new(); + for syntax in updated_concrete_type.descendants() { if let Some(old_lifetime) = ast::Lifetime::cast(syntax.clone()) { if let Some(new_lifetime) = lifetime_map.0.get(&old_lifetime.to_string()) { if new_lifetime.text() == "'_" { @@ -322,12 +329,16 @@ replacements.push((syntax.clone(), new_lifetime.syntax().clone_for_update())); } - } else if let Some(replacement_syntax) = const_and_type_map.0.get(syntax_str) { + } else if let Some(name_ref) = ast::NameRef::cast(syntax.clone()) { + let Some(replacement_syntax) = const_and_type_map.0.get(&name_ref.to_string()) else { + continue; + }; let new_string = replacement_syntax.to_string(); let new = if new_string == "_" { - make::wildcard_pat().syntax().clone_for_update() + let make = SyntaxFactory::without_mappings(); + make.wildcard_pat().syntax().clone() } else { - replacement_syntax.clone_for_update() + replacement_syntax.clone() }; replacements.push((syntax.clone(), new)); @@ -335,14 +346,13 @@ } for (old, new) in replacements { - ted::replace(old, new); + editor.replace(old, new); } for syntax in removals { - ted::remove(syntax); + editor.delete(syntax); } - - updated_concrete_type.to_string() + editor.finish().new_root().clone() } fn get_type_alias(ctx: &AssistContext<'_>, path: &ast::PathType) -> Option<ast::TypeAlias> { @@ -377,12 +387,15 @@ } fn replacement_value(&self) -> Option<SyntaxNode> { - Some(match self { - ConstOrTypeGeneric::ConstArg(ca) => ca.expr()?.syntax().clone(), - ConstOrTypeGeneric::TypeArg(ta) => ta.syntax().clone(), - ConstOrTypeGeneric::ConstParam(cp) => cp.default_val()?.syntax().clone(), - ConstOrTypeGeneric::TypeParam(tp) => tp.default_type()?.syntax().clone(), - }) + Some( + match self { + ConstOrTypeGeneric::ConstArg(ca) => ca.expr()?.syntax().clone(), + ConstOrTypeGeneric::TypeArg(ta) => ta.syntax().clone(), + ConstOrTypeGeneric::ConstParam(cp) => cp.default_val()?.syntax().clone(), + ConstOrTypeGeneric::TypeParam(tp) => tp.default_type()?.syntax().clone(), + } + .clone_for_update(), + ) } }
diff --git a/crates/ide-assists/src/handlers/move_bounds.rs b/crates/ide-assists/src/handlers/move_bounds.rs index a9df6f6..e5425ab 100644 --- a/crates/ide-assists/src/handlers/move_bounds.rs +++ b/crates/ide-assists/src/handlers/move_bounds.rs
@@ -55,7 +55,6 @@ match parent { ast::Fn(it) => it.get_or_create_where_clause(), ast::Trait(it) => it.get_or_create_where_clause(), - ast::TraitAlias(it) => it.get_or_create_where_clause(), ast::Impl(it) => it.get_or_create_where_clause(), ast::Enum(it) => it.get_or_create_where_clause(), ast::Struct(it) => it.get_or_create_where_clause(),
diff --git a/crates/ide-assists/src/handlers/move_const_to_impl.rs b/crates/ide-assists/src/handlers/move_const_to_impl.rs index 0c1dc9e..a645c8b 100644 --- a/crates/ide-assists/src/handlers/move_const_to_impl.rs +++ b/crates/ide-assists/src/handlers/move_const_to_impl.rs
@@ -43,10 +43,10 @@ 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. - if let Some(body) = const_.body() { - if body.syntax().text_range().contains(ctx.offset()) { - return None; - } + if let Some(body) = const_.body() + && body.syntax().text_range().contains(ctx.offset()) + { + return None; } let parent_fn = const_.syntax().ancestors().find_map(ast::Fn::cast)?;
diff --git a/crates/ide-assists/src/handlers/promote_local_to_const.rs b/crates/ide-assists/src/handlers/promote_local_to_const.rs index 603be4d..547d368 100644 --- a/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -88,7 +88,7 @@ } } - let item = make.item_const(None, make.name(&name), make.ty(&ty), initializer); + let item = make.item_const(None, None, make.name(&name), make.ty(&ty), initializer); if let Some((cap, name)) = ctx.config.snippet_cap.zip(item.name()) { let tabstop = edit.make_tabstop_before(cap);
diff --git a/crates/ide-assists/src/handlers/pull_assignment_up.rs b/crates/ide-assists/src/handlers/pull_assignment_up.rs index 1b0c313..21debf6 100644 --- a/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide-assists/src/handlers/pull_assignment_up.rs
@@ -62,10 +62,10 @@ return None; }; - if let Some(parent) = tgt.syntax().parent() { - if matches!(parent.kind(), syntax::SyntaxKind::BIN_EXPR | syntax::SyntaxKind::LET_STMT) { - return None; - } + if let Some(parent) = tgt.syntax().parent() + && matches!(parent.kind(), syntax::SyntaxKind::BIN_EXPR | syntax::SyntaxKind::LET_STMT) + { + return None; } let target = tgt.syntax().text_range(); @@ -90,10 +90,10 @@ let mut editor = SyntaxEditor::new(edit_tgt); for (stmt, rhs) in assignments { let mut stmt = stmt.syntax().clone(); - if let Some(parent) = stmt.parent() { - if ast::ExprStmt::cast(parent.clone()).is_some() { - stmt = parent.clone(); - } + if let Some(parent) = stmt.parent() + && ast::ExprStmt::cast(parent.clone()).is_some() + { + stmt = parent.clone(); } editor.replace(stmt, rhs.syntax()); }
diff --git a/crates/ide-assists/src/handlers/raw_string.rs b/crates/ide-assists/src/handlers/raw_string.rs index 94b49c5..2cbb24a 100644 --- a/crates/ide-assists/src/handlers/raw_string.rs +++ b/crates/ide-assists/src/handlers/raw_string.rs
@@ -80,15 +80,15 @@ // parse inside string to escape `"` let escaped = value.escape_default().to_string(); let suffix = string_suffix(token.text()).unwrap_or_default(); - if let Some(offsets) = token.quote_offsets() { - if token.text()[offsets.contents - token.syntax().text_range().start()] == escaped { - let end_quote = offsets.quotes.1; - let end_quote = - TextRange::new(end_quote.start(), end_quote.end() - TextSize::of(suffix)); - edit.replace(offsets.quotes.0, "\""); - edit.replace(end_quote, "\""); - return; - } + if let Some(offsets) = token.quote_offsets() + && token.text()[offsets.contents - token.syntax().text_range().start()] == escaped + { + let end_quote = offsets.quotes.1; + let end_quote = + TextRange::new(end_quote.start(), end_quote.end() - TextSize::of(suffix)); + edit.replace(offsets.quotes.0, "\""); + edit.replace(end_quote, "\""); + return; } edit.replace(token.syntax().text_range(), format!("\"{escaped}\"{suffix}"));
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index 9356d02..414f674 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -112,6 +112,16 @@ } } } + // dbg!(2, 'x', &x, x, ...); + exprs if ast::ExprStmt::can_cast(parent.kind()) && exprs.iter().all(pure_expr) => { + let mut replace = vec![parent.clone().into()]; + if let Some(prev_sibling) = parent.prev_sibling_or_token() + && prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE + { + replace.push(prev_sibling); + } + (replace, None) + } // dbg!(expr0) [expr] => { // dbg!(expr, &parent); @@ -163,6 +173,20 @@ }) } +fn pure_expr(expr: &ast::Expr) -> bool { + match_ast! { + match (expr.syntax()) { + ast::Literal(_) => true, + ast::RefExpr(it) => { + matches!(it.expr(), Some(ast::Expr::PathExpr(p)) + if p.path().and_then(|p| p.as_single_name_ref()).is_some()) + }, + ast::PathExpr(it) => it.path().and_then(|it| it.as_single_name_ref()).is_some(), + _ => false, + } + } +} + fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr { if let ast::Expr::MacroExpr(mac) = &expanded { // Special-case when `expanded` itself is `dbg!()` since we cannot replace the whole tree @@ -232,6 +256,32 @@ } #[test] + fn test_remove_simple_dbg_statement() { + check_assist( + remove_dbg, + r#" +fn foo() { + let n = 2; + $0dbg!(3); + dbg!(2.6); + dbg!(1, 2.5); + dbg!('x'); + dbg!(&n); + dbg!(n); + // needless comment + dbg!("foo");$0 +} +"#, + r#" +fn foo() { + let n = 2; + // needless comment +} +"#, + ); + } + + #[test] fn test_remove_dbg_not_applicable() { check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}"); check_assist_not_applicable(remove_dbg, "fn main() {$0dbg(5, 6, 7)}");
diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 806c8fb..175f261 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -5,12 +5,12 @@ SyntaxKind::WHITESPACE, T, ast::{self, AstNode, HasName, make}, - ted::{self, Position}, + syntax_editor::{Position, SyntaxEditor}, }; use crate::{ AssistConfig, AssistId, - assist_context::{AssistContext, Assists, SourceChangeBuilder}, + assist_context::{AssistContext, Assists}, utils::{ DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, generate_trait_impl, @@ -126,98 +126,56 @@ let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`"); acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| { - let insert_after = ted::Position::after(builder.make_mut(adt.clone()).syntax()); + 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_with_items = impl_def_from_trait( + let impl_def = impl_def_from_trait( &ctx.sema, ctx.config, adt, &annotated_name, trait_, replace_trait_path, + impl_is_unsafe, ); - update_attribute(builder, old_derives, old_tree, old_trait_path, attr); + + let mut editor = builder.make_editor(attr.syntax()); + update_attribute(&mut editor, old_derives, old_tree, old_trait_path, attr); let trait_path = make::ty_path(replace_trait_path.clone()); - match (ctx.config.snippet_cap, impl_def_with_items) { - (None, None) => { - let impl_def = generate_trait_impl(adt, trait_path); - if impl_is_unsafe { - ted::insert( - Position::first_child_of(impl_def.syntax()), - make::token(T![unsafe]), - ); - } + let (impl_def, first_assoc_item) = if let Some(impl_def) = impl_def { + ( + impl_def.clone(), + impl_def.assoc_item_list().and_then(|list| list.assoc_items().next()), + ) + } else { + (generate_trait_impl(impl_is_unsafe, adt, trait_path), None) + }; - ted::insert_all( - insert_after, - vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], - ); - } - (None, Some((impl_def, _))) => { - if impl_is_unsafe { - ted::insert( - Position::first_child_of(impl_def.syntax()), - make::token(T![unsafe]), - ); - } - ted::insert_all( - insert_after, - vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], - ); - } - (Some(cap), None) => { - let impl_def = generate_trait_impl(adt, trait_path); - - if impl_is_unsafe { - ted::insert( - Position::first_child_of(impl_def.syntax()), - make::token(T![unsafe]), - ); - } - - if let Some(l_curly) = impl_def.assoc_item_list().and_then(|it| it.l_curly_token()) + if let Some(cap) = ctx.config.snippet_cap { + if let Some(first_assoc_item) = first_assoc_item { + if let ast::AssocItem::Fn(ref func) = first_assoc_item + && let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) + && m.syntax().text() == "todo!()" { - builder.add_tabstop_after_token(cap, l_curly); - } - - ted::insert_all( - insert_after, - vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], - ); - } - (Some(cap), Some((impl_def, first_assoc_item))) => { - let mut added_snippet = false; - - if impl_is_unsafe { - ted::insert( - Position::first_child_of(impl_def.syntax()), - make::token(T![unsafe]), - ); - } - - if let ast::AssocItem::Fn(ref func) = first_assoc_item { - if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { - if m.syntax().text() == "todo!()" { - // Make the `todo!()` a placeholder - builder.add_placeholder_snippet(cap, m); - added_snippet = true; - } - } - } - - if !added_snippet { + // Make the `todo!()` a placeholder + builder.add_placeholder_snippet(cap, m); + } else { // If we haven't already added a snippet, add a tabstop before the generated function builder.add_tabstop_before(cap, first_assoc_item); } - - ted::insert_all( - insert_after, - vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], - ); + } else if let Some(l_curly) = + impl_def.assoc_item_list().and_then(|it| it.l_curly_token()) + { + builder.add_tabstop_after_token(cap, l_curly); } - }; + } + + editor.insert_all( + insert_after, + vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], + ); + builder.add_file_edits(ctx.vfs_file_id(), editor); }) } @@ -228,7 +186,8 @@ annotated_name: &ast::Name, trait_: Option<hir::Trait>, trait_path: &ast::Path, -) -> Option<(ast::Impl, ast::AssocItem)> { + impl_is_unsafe: bool, +) -> Option<ast::Impl> { let trait_ = trait_?; let target_scope = sema.scope(annotated_name.syntax())?; @@ -245,21 +204,39 @@ if trait_items.is_empty() { return None; } - let impl_def = generate_trait_impl(adt, make::ty_path(trait_path.clone())); + let impl_def = generate_trait_impl(impl_is_unsafe, adt, make::ty_path(trait_path.clone())); - let first_assoc_item = + let assoc_items = add_trait_assoc_items_to_impl(sema, config, &trait_items, trait_, &impl_def, &target_scope); + let assoc_item_list = if let Some((first, other)) = + assoc_items.split_first().map(|(first, other)| (first.clone_subtree(), other)) + { + let first_item = if let ast::AssocItem::Fn(ref func) = first + && let Some(body) = gen_trait_fn_body(func, trait_path, adt, None) + && let Some(func_body) = func.body() + { + let mut editor = SyntaxEditor::new(first.syntax().clone()); + editor.replace(func_body.syntax(), body.syntax()); + ast::AssocItem::cast(editor.finish().new_root().clone()) + } else { + Some(first.clone()) + }; + let items = first_item.into_iter().chain(other.iter().cloned()).collect(); + make::assoc_item_list(Some(items)) + } else { + make::assoc_item_list(None) + } + .clone_for_update(); - // Generate a default `impl` function body for the derived trait. - if let ast::AssocItem::Fn(ref func) = first_assoc_item { - let _ = gen_trait_fn_body(func, trait_path, adt, None); - }; - - Some((impl_def, first_assoc_item)) + let impl_def = impl_def.clone_subtree(); + let mut editor = SyntaxEditor::new(impl_def.syntax().clone()); + editor.replace(impl_def.assoc_item_list()?.syntax(), assoc_item_list.syntax()); + let impl_def = ast::Impl::cast(editor.finish().new_root().clone())?; + Some(impl_def) } fn update_attribute( - builder: &mut SourceChangeBuilder, + editor: &mut SyntaxEditor, old_derives: &[ast::Path], old_tree: &ast::TokenTree, old_trait_path: &ast::Path, @@ -272,8 +249,6 @@ let has_more_derives = !new_derives.is_empty(); if has_more_derives { - let old_tree = builder.make_mut(old_tree.clone()); - // Make the paths into flat lists of tokens in a vec let tt = new_derives.iter().map(|path| path.syntax().clone()).map(|node| { node.descendants_with_tokens() @@ -288,18 +263,17 @@ let tt = tt.collect::<Vec<_>>(); let new_tree = make::token_tree(T!['('], tt).clone_for_update(); - ted::replace(old_tree.syntax(), new_tree.syntax()); + editor.replace(old_tree.syntax(), new_tree.syntax()); } else { // Remove the attr and any trailing whitespace - let attr = builder.make_mut(attr.clone()); if let Some(line_break) = attr.syntax().next_sibling_or_token().filter(|t| t.kind() == WHITESPACE) { - ted::remove(line_break) + editor.delete(line_break) } - ted::remove(attr.syntax()) + editor.delete(attr.syntax()) } }
diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 3cd7b58..df70578 100644 --- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
@@ -7,11 +7,8 @@ }; use syntax::{ AstNode, - ast::{ - self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType, - make::impl_trait_type, - }, - match_ast, ted, + ast::{self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType, make}, + match_ast, }; use crate::{AssistContext, AssistId, Assists}; @@ -74,26 +71,31 @@ "Replace named generic with impl trait", target, |edit| { - let type_param = edit.make_mut(type_param); - let fn_ = edit.make_mut(fn_); - - let path_types_to_replace = path_types_to_replace - .into_iter() - .map(|param| edit.make_mut(param)) - .collect::<Vec<_>>(); + let mut editor = edit.make_editor(type_param.syntax()); // remove trait from generic param list if let Some(generic_params) = fn_.generic_param_list() { - generic_params.remove_generic_param(ast::GenericParam::TypeParam(type_param)); - if generic_params.generic_params().count() == 0 { - ted::remove(generic_params.syntax()); + let params: Vec<ast::GenericParam> = generic_params + .clone() + .generic_params() + .filter(|it| it.syntax() != type_param.syntax()) + .collect(); + if params.is_empty() { + editor.delete(generic_params.syntax()); + } else { + let new_generic_param_list = make::generic_param_list(params); + editor.replace( + generic_params.syntax(), + new_generic_param_list.syntax().clone_for_update(), + ); } } - let new_bounds = impl_trait_type(type_bound_list); + let new_bounds = make::impl_trait_type(type_bound_list); for path_type in path_types_to_replace.iter().rev() { - ted::replace(path_type.syntax(), new_bounds.clone_for_update().syntax()); + editor.replace(path_type.syntax(), new_bounds.clone_for_update().syntax()); } + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) }
diff --git a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index fa005a4..9f74213 100644 --- a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs
@@ -102,10 +102,10 @@ fn drop_generic_args(path: &ast::Path) -> ast::Path { let path = path.clone_for_update(); - if let Some(segment) = path.segment() { - if let Some(generic_args) = segment.generic_arg_list() { - ted::remove(generic_args.syntax()); - } + if let Some(segment) = path.segment() + && let Some(generic_args) = segment.generic_arg_list() + { + ted::remove(generic_args.syntax()); } path }
diff --git a/crates/ide-assists/src/handlers/unmerge_imports.rs b/crates/ide-assists/src/handlers/unmerge_imports.rs index c066f41..accb5c2 100644 --- a/crates/ide-assists/src/handlers/unmerge_imports.rs +++ b/crates/ide-assists/src/handlers/unmerge_imports.rs
@@ -1,9 +1,6 @@ use syntax::{ AstNode, SyntaxKind, - ast::{ - self, HasAttrs, HasVisibility, edit::IndentLevel, edit_in_place::AttrsOwnerEdit, make, - syntax_factory::SyntaxFactory, - }, + ast::{self, HasAttrs, HasVisibility, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, syntax_editor::{Element, Position, Removable}, }; @@ -46,13 +43,10 @@ acc.add(AssistId::refactor_rewrite("unmerge_imports"), label, target, |builder| { let make = SyntaxFactory::with_mappings(); let new_use = make.use_( + use_.attrs(), use_.visibility(), make.use_tree(path, tree.use_tree_list(), tree.rename(), tree.star_token().is_some()), ); - // Add any attributes that are present on the use tree - use_.attrs().for_each(|attr| { - new_use.add_attr(attr.clone_for_update()); - }); let mut editor = builder.make_editor(use_.syntax()); // Remove the use tree from the current use item
diff --git a/crates/ide-assists/src/handlers/unnecessary_async.rs b/crates/ide-assists/src/handlers/unnecessary_async.rs index ac10a82..b938577 100644 --- a/crates/ide-assists/src/handlers/unnecessary_async.rs +++ b/crates/ide-assists/src/handlers/unnecessary_async.rs
@@ -41,10 +41,10 @@ return None; } // Do nothing if the method is a member of trait. - if let Some(impl_) = function.syntax().ancestors().nth(2).and_then(ast::Impl::cast) { - if impl_.trait_().is_some() { - return None; - } + if let Some(impl_) = function.syntax().ancestors().nth(2).and_then(ast::Impl::cast) + && impl_.trait_().is_some() + { + return None; } // Remove the `async` keyword plus whitespace after it, if any.
diff --git a/crates/ide-assists/src/handlers/unwrap_return_type.rs b/crates/ide-assists/src/handlers/unwrap_return_type.rs index cf38262..eea6c85 100644 --- a/crates/ide-assists/src/handlers/unwrap_return_type.rs +++ b/crates/ide-assists/src/handlers/unwrap_return_type.rs
@@ -72,20 +72,20 @@ let mut exprs_to_unwrap = Vec::new(); let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e); walk_expr(&body_expr, &mut |expr| { - if let ast::Expr::ReturnExpr(ret_expr) = expr { - if let Some(ret_expr_arg) = &ret_expr.expr() { - for_each_tail_expr(ret_expr_arg, tail_cb); - } + if let ast::Expr::ReturnExpr(ret_expr) = expr + && let Some(ret_expr_arg) = &ret_expr.expr() + { + for_each_tail_expr(ret_expr_arg, tail_cb); } }); for_each_tail_expr(&body_expr, tail_cb); let is_unit_type = is_unit_type(&happy_type); if is_unit_type { - if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() { - if token.kind() == SyntaxKind::WHITESPACE { - editor.delete(token); - } + if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() + && token.kind() == SyntaxKind::WHITESPACE + { + editor.delete(token); } editor.delete(ret_type.syntax()); @@ -162,10 +162,10 @@ } } - if let Some(cap) = ctx.config.snippet_cap { - if let Some(final_placeholder) = final_placeholder { - editor.add_annotation(final_placeholder.syntax(), builder.make_tabstop_after(cap)); - } + if let Some(cap) = ctx.config.snippet_cap + && let Some(final_placeholder) = final_placeholder + { + editor.add_annotation(final_placeholder.syntax(), builder.make_tabstop_after(cap)); } editor.add_mappings(make.finish_with_mappings());
diff --git a/crates/ide-assists/src/handlers/unwrap_tuple.rs b/crates/ide-assists/src/handlers/unwrap_tuple.rs index ecfecbb..46f3e85 100644 --- a/crates/ide-assists/src/handlers/unwrap_tuple.rs +++ b/crates/ide-assists/src/handlers/unwrap_tuple.rs
@@ -47,10 +47,10 @@ if tuple_pat.fields().count() != tuple_init.fields().count() { return None; } - if let Some(tys) = &tuple_ty { - if tuple_pat.fields().count() != tys.fields().count() { - return None; - } + if let Some(tys) = &tuple_ty + && tuple_pat.fields().count() != tys.fields().count() + { + return None; } let parent = let_kw.parent()?;
diff --git a/crates/ide-assists/src/handlers/wrap_return_type.rs b/crates/ide-assists/src/handlers/wrap_return_type.rs index d7189aa..0f089c9 100644 --- a/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/crates/ide-assists/src/handlers/wrap_return_type.rs
@@ -101,24 +101,24 @@ let mut exprs_to_wrap = Vec::new(); let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); walk_expr(&body_expr, &mut |expr| { - if let Expr::ReturnExpr(ret_expr) = expr { - if let Some(ret_expr_arg) = &ret_expr.expr() { - for_each_tail_expr(ret_expr_arg, tail_cb); - } + if let Expr::ReturnExpr(ret_expr) = expr + && let Some(ret_expr_arg) = &ret_expr.expr() + { + for_each_tail_expr(ret_expr_arg, tail_cb); } }); for_each_tail_expr(&body_expr, tail_cb); for ret_expr_arg in exprs_to_wrap { - if let Some(ty) = ctx.sema.type_of_expr(&ret_expr_arg) { - if ty.adjusted().could_unify_with(ctx.db(), &semantic_new_return_ty) { - // The type is already correct, don't wrap it. - // We deliberately don't use `could_unify_with_deeply()`, because as long as the outer - // enum matches it's okay for us, as we don't trigger the assist if the return type - // is already `Option`/`Result`, so mismatched exact type is more likely a mistake - // than something intended. - continue; - } + if let Some(ty) = ctx.sema.type_of_expr(&ret_expr_arg) + && ty.adjusted().could_unify_with(ctx.db(), &semantic_new_return_ty) + { + // The type is already correct, don't wrap it. + // We deliberately don't use `could_unify_with_deeply()`, because as long as the outer + // enum matches it's okay for us, as we don't trigger the assist if the return type + // is already `Option`/`Result`, so mismatched exact type is more likely a mistake + // than something intended. + continue; } let happy_wrapped = make.expr_call( @@ -147,13 +147,13 @@ ast::GenericArg::LifetimeArg(_) => false, _ => true, }); - if let Some(error_type_arg) = error_type_arg { - if let Some(cap) = ctx.config.snippet_cap { - editor.add_annotation( - error_type_arg.syntax(), - builder.make_placeholder_snippet(cap), - ); - } + if let Some(error_type_arg) = error_type_arg + && let Some(cap) = ctx.config.snippet_cap + { + editor.add_annotation( + error_type_arg.syntax(), + builder.make_placeholder_snippet(cap), + ); } }
diff --git a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 5183566..7d5740b 100644 --- a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
@@ -200,13 +200,12 @@ ], ); - if let Some(snippet_cap) = ctx.config.snippet_cap { - if let Some(first_meta) = + if let Some(snippet_cap) = ctx.config.snippet_cap + && let Some(first_meta) = cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token()) - { - let tabstop = edit.make_tabstop_after(snippet_cap); - editor.add_annotation(first_meta, tabstop); - } + { + let tabstop = edit.make_tabstop_after(snippet_cap); + editor.add_annotation(first_meta, tabstop); } editor.add_mappings(make.finish_with_mappings()); @@ -256,13 +255,12 @@ editor.replace(attr.syntax(), cfg_attr.syntax()); - if let Some(snippet_cap) = ctx.config.snippet_cap { - if let Some(first_meta) = + if let Some(snippet_cap) = ctx.config.snippet_cap + && let Some(first_meta) = cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token()) - { - let tabstop = edit.make_tabstop_after(snippet_cap); - editor.add_annotation(first_meta, tabstop); - } + { + let tabstop = edit.make_tabstop_after(snippet_cap); + editor.add_annotation(first_meta, tabstop); } editor.add_mappings(make.finish_with_mappings());
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index cde0d87..5008f97 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs
@@ -67,7 +67,7 @@ pub mod utils; use hir::Semantics; -use ide_db::{EditionedFileId, RootDatabase}; +use ide_db::{EditionedFileId, RootDatabase, base_db::salsa}; use syntax::{Edition, TextRange}; pub(crate) use crate::assist_context::{AssistContext, Assists}; @@ -93,8 +93,11 @@ .unwrap_or_else(|| EditionedFileId::new(db, range.file_id, Edition::CURRENT)); let ctx = AssistContext::new(sema, config, hir::FileRange { file_id, range: range.range }); let mut acc = Assists::new(&ctx, resolve); - handlers::all().iter().for_each(|handler| { - handler(&mut acc, &ctx); + // the handlers may invoke trait solving related things which accesses salsa structs outside queries + salsa::attach(db, || { + handlers::all().iter().for_each(|handler| { + handler(&mut acc, &ctx); + }); }); acc.finish() } @@ -302,6 +305,7 @@ generate_function::generate_function, generate_impl::generate_impl, generate_impl::generate_trait_impl, + generate_impl::generate_impl_trait, generate_is_empty_from_len::generate_is_empty_from_len, generate_mut_trait_impl::generate_mut_trait_impl, generate_new::generate_new,
diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index cda2ad4..f4daabf 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs
@@ -1,11 +1,11 @@ mod generated; use expect_test::expect; -use hir::Semantics; +use hir::{Semantics, setup_tracing}; use ide_db::{ EditionedFileId, FileRange, RootDatabase, SnippetCap, assists::ExprFillDefaultMode, - base_db::SourceDatabase, + base_db::{SourceDatabase, salsa}, imports::insert_use::{ImportGranularity, InsertUseConfig}, source_change::FileSystemEdit, }; @@ -305,6 +305,7 @@ expected: ExpectedResult<'_>, assist_label: Option<&str>, ) { + let _tracing = setup_tracing(); let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before); db.enable_proc_attr_macros(); let text_without_caret = db.file_text(file_with_caret_id.file_id(&db)).text(&db).to_string(); @@ -318,7 +319,9 @@ _ => AssistResolveStrategy::All, }; let mut acc = Assists::new(&ctx, resolve); - handler(&mut acc, &ctx); + salsa::attach(&db, || { + handler(&mut acc, &ctx); + }); let mut res = acc.finish(); let assist = match assist_label {
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index fc1c692..91348be 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs
@@ -1881,6 +1881,29 @@ } #[test] +fn doctest_generate_impl_trait() { + check_doc_test( + "generate_impl_trait", + r#####" +trait $0Foo { + fn foo(&self) -> i32; +} +"#####, + r#####" +trait Foo { + fn foo(&self) -> i32; +} + +impl Foo for ${1:_} { + fn foo(&self) -> i32 { + $0todo!() + } +} +"#####, + ) +} + +#[test] fn doctest_generate_is_empty_from_len() { check_doc_test( "generate_is_empty_from_len",
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 31649a7..20e0302 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs
@@ -23,11 +23,11 @@ ast::{ self, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, edit::{AstNodeEdit, IndentLevel}, - edit_in_place::{AttrsOwnerEdit, Indent, Removable}, + edit_in_place::AttrsOwnerEdit, make, syntax_factory::SyntaxFactory, }, - ted, + syntax_editor::{Removable, SyntaxEditor}, }; use crate::{ @@ -130,10 +130,10 @@ if ignore_items == IgnoreAssocItems::DocHiddenAttrPresent && assoc_item.attrs(sema.db).has_doc_hidden() { - if let hir::AssocItem::Function(f) = assoc_item { - if !f.has_body(sema.db) { - return true; - } + if let hir::AssocItem::Function(f) = assoc_item + && !f.has_body(sema.db) + { + return true; } return false; } @@ -178,6 +178,7 @@ /// [`filter_assoc_items()`]), clones each item for update and applies path transformation to it, /// then inserts into `impl_`. Returns the modified `impl_` and the first associated item that got /// inserted. +#[must_use] pub fn add_trait_assoc_items_to_impl( sema: &Semantics<'_, RootDatabase>, config: &AssistConfig, @@ -185,71 +186,72 @@ trait_: hir::Trait, impl_: &ast::Impl, target_scope: &hir::SemanticsScope<'_>, -) -> ast::AssocItem { +) -> Vec<ast::AssocItem> { let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1; - let items = original_items.iter().map(|InFile { file_id, value: original_item }| { - let cloned_item = { - if let Some(macro_file) = file_id.macro_file() { - let span_map = sema.db.expansion_span_map(macro_file); - let item_prettified = prettify_macro_expansion( - sema.db, - original_item.syntax().clone(), - &span_map, - target_scope.krate().into(), - ); - if let Some(formatted) = ast::AssocItem::cast(item_prettified) { - return formatted; - } else { - stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`"); + original_items + .iter() + .map(|InFile { file_id, value: original_item }| { + let mut cloned_item = { + if let Some(macro_file) = file_id.macro_file() { + let span_map = sema.db.expansion_span_map(macro_file); + let item_prettified = prettify_macro_expansion( + sema.db, + original_item.syntax().clone(), + &span_map, + target_scope.krate().into(), + ); + if let Some(formatted) = ast::AssocItem::cast(item_prettified) { + return formatted; + } else { + stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`"); + } } + original_item } - original_item.clone_for_update() - }; + .reset_indent(); - if let Some(source_scope) = sema.scope(original_item.syntax()) { - // FIXME: Paths in nested macros are not handled well. See - // `add_missing_impl_members::paths_in_nested_macro_should_get_transformed` test. - let transform = - PathTransform::trait_impl(target_scope, &source_scope, trait_, impl_.clone()); - transform.apply(cloned_item.syntax()); - } - cloned_item.remove_attrs_and_docs(); - cloned_item.reindent_to(new_indent_level); - cloned_item - }); - - let assoc_item_list = impl_.get_or_create_assoc_item_list(); - - let mut first_item = None; - for item in items { - first_item.get_or_insert_with(|| item.clone()); - match &item { + if let Some(source_scope) = sema.scope(original_item.syntax()) { + // FIXME: Paths in nested macros are not handled well. See + // `add_missing_impl_members::paths_in_nested_macro_should_get_transformed` test. + let transform = + 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 + }) + .filter_map(|item| match item { ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { - let body = AstNodeEdit::indent( - &make::block_expr( - None, - Some(match config.expr_fill_default { - ExprFillDefaultMode::Todo => make::ext::expr_todo(), - ExprFillDefaultMode::Underscore => make::ext::expr_underscore(), - ExprFillDefaultMode::Default => make::ext::expr_todo(), - }), - ), - new_indent_level, + let fn_ = fn_.clone_subtree(); + let new_body = &make::block_expr( + None, + Some(match config.expr_fill_default { + ExprFillDefaultMode::Todo => make::ext::expr_todo(), + ExprFillDefaultMode::Underscore => make::ext::expr_underscore(), + ExprFillDefaultMode::Default => make::ext::expr_todo(), + }), ); - ted::replace(fn_.get_or_create_body().syntax(), body.syntax()) + let new_body = AstNodeEdit::indent(new_body, IndentLevel::single()); + let mut fn_editor = SyntaxEditor::new(fn_.syntax().clone()); + fn_.replace_or_insert_body(&mut fn_editor, new_body); + let new_fn_ = fn_editor.finish().new_root().clone(); + ast::AssocItem::cast(new_fn_) } ast::AssocItem::TypeAlias(type_alias) => { + let type_alias = type_alias.clone_subtree(); if let Some(type_bound_list) = type_alias.type_bound_list() { - type_bound_list.remove() + let mut type_alias_editor = SyntaxEditor::new(type_alias.syntax().clone()); + type_bound_list.remove(&mut type_alias_editor); + let type_alias = type_alias_editor.finish().new_root().clone(); + ast::AssocItem::cast(type_alias) + } else { + Some(ast::AssocItem::TypeAlias(type_alias)) } } - _ => {} - } - - assoc_item_list.add_item(item) - } - - first_item.unwrap() + item => Some(item), + }) + .map(|item| AstNodeEdit::indent(&item, new_indent_level)) + .collect() } pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize { @@ -334,7 +336,7 @@ fn invert_special_case_legacy(expr: &ast::Expr) -> Option<ast::Expr> { match expr { ast::Expr::BinExpr(bin) => { - let bin = bin.clone_for_update(); + let bin = bin.clone_subtree(); let op_token = bin.op_token()?; let rev_token = match op_token.kind() { T![==] => T![!=], @@ -350,8 +352,9 @@ ); } }; - ted::replace(op_token, make::token(rev_token)); - Some(bin.into()) + let mut bin_editor = SyntaxEditor::new(bin.syntax().clone()); + bin_editor.replace(op_token, make::token(rev_token)); + ast::Expr::cast(bin_editor.finish().new_root().clone()) } ast::Expr::MethodCallExpr(mce) => { let receiver = mce.receiver()?; @@ -516,10 +519,10 @@ if !(same_ty && not_trait_impl) { None } else { Some(impl_blk) } }); - if let Some(ref impl_blk) = block { - if has_any_fn(impl_blk, names) { - return None; - } + if let Some(ref impl_blk) = block + && has_any_fn(impl_blk, names) + { + return None; } Some(block) @@ -528,12 +531,11 @@ fn has_any_fn(imp: &ast::Impl, names: &[String]) -> bool { if let Some(il) = imp.assoc_item_list() { for item in il.assoc_items() { - if let ast::AssocItem::Fn(f) = item { - if let Some(name) = f.name() { - if names.iter().any(|n| n.eq_ignore_ascii_case(&name.text())) { - return true; - } - } + if let ast::AssocItem::Fn(f) = item + && let Some(name) = f.name() + && names.iter().any(|n| n.eq_ignore_ascii_case(&name.text())) + { + return true; } } } @@ -668,19 +670,19 @@ adt: &ast::Adt, body: Option<ast::AssocItemList>, ) -> ast::Impl { - generate_impl_inner(adt, None, true, body) + generate_impl_inner(false, adt, None, true, body) } pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl { - generate_impl_inner(adt, None, true, None) + generate_impl_inner(false, adt, None, true, None) } /// Generates the corresponding `impl <trait> for Type {}` including type /// and lifetime parameters, with `<trait>` appended to `impl`'s generic parameters' bounds. /// /// This is useful for traits like `PartialEq`, since `impl<T> PartialEq for U<T>` often requires `T: PartialEq`. -pub(crate) fn generate_trait_impl(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl { - generate_impl_inner(adt, Some(trait_), true, None) +pub(crate) fn generate_trait_impl(is_unsafe: bool, adt: &ast::Adt, trait_: ast::Type) -> ast::Impl { + generate_impl_inner(is_unsafe, adt, Some(trait_), true, None) } /// Generates the corresponding `impl <trait> for Type {}` including type @@ -688,10 +690,11 @@ /// /// This is useful for traits like `From<T>`, since `impl<T> From<T> for U<T>` doesn't require `T: From<T>`. pub(crate) fn generate_trait_impl_intransitive(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl { - generate_impl_inner(adt, Some(trait_), false, None) + generate_impl_inner(false, adt, Some(trait_), false, None) } fn generate_impl_inner( + is_unsafe: bool, adt: &ast::Adt, trait_: Option<ast::Type>, trait_is_transitive: bool, @@ -733,9 +736,12 @@ generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text())); - let impl_ = match trait_ { + let cfg_attrs = + adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); + match trait_ { Some(trait_) => make::impl_trait( - false, + cfg_attrs, + is_unsafe, None, None, generic_params, @@ -747,26 +753,9 @@ adt.where_clause(), body, ), - None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), body), + None => make::impl_(cfg_attrs, generic_params, generic_args, ty, adt.where_clause(), body), } - .clone_for_update(); - - // Copy any cfg attrs from the original adt - add_cfg_attrs_to(adt, &impl_); - - impl_ -} - -pub(crate) fn add_cfg_attrs_to<T, U>(from: &T, to: &U) -where - T: HasAttrs, - U: AttrsOwnerEdit, -{ - let cfg_attrs = - from.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); - for attr in cfg_attrs { - to.add_attr(attr.clone_for_update()); - } + .clone_for_update() } pub(crate) fn add_method_to_adt( @@ -1022,12 +1011,12 @@ pub(crate) fn convert_param_list_to_arg_list(list: ast::ParamList) -> ast::ArgList { let mut args = vec![]; for param in list.params() { - if let Some(ast::Pat::IdentPat(pat)) = param.pat() { - if let Some(name) = pat.name() { - let name = name.to_string(); - let expr = make::expr_path(make::ext::ident_path(&name)); - args.push(expr); - } + if let Some(ast::Pat::IdentPat(pat)) = param.pat() + && let Some(name) = pat.name() + { + let name = name.to_string(); + let expr = make::expr_path(make::ext::ident_path(&name)); + args.push(expr); } } make::arg_list(args) @@ -1139,12 +1128,11 @@ }; match expr { ast::Expr::CallExpr(call) => { - if let Some(ast::Expr::PathExpr(path_expr)) = call.expr() { - if let Some(PathResolution::Def(ModuleDef::Function(func))) = + if let Some(ast::Expr::PathExpr(path_expr)) = call.expr() + && let Some(PathResolution::Def(ModuleDef::Function(func))) = path_expr.path().and_then(|path| sema.resolve_path(&path)) - { - is_const &= func.is_const(sema.db); - } + { + is_const &= func.is_const(sema.db); } } ast::Expr::MethodCallExpr(call) => {
diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index 026209e..87e90e8 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -1,10 +1,7 @@ //! This module contains functions to generate default trait impl function bodies where possible. use hir::TraitRef; -use syntax::{ - ast::{self, AstNode, BinaryOp, CmpOp, HasName, LogicOp, edit::AstNodeEdit, make}, - ted, -}; +use syntax::ast::{self, AstNode, BinaryOp, CmpOp, HasName, LogicOp, edit::AstNodeEdit, make}; /// Generate custom trait bodies without default implementation where possible. /// @@ -18,21 +15,33 @@ trait_path: &ast::Path, adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>, -) -> Option<()> { +) -> Option<ast::BlockExpr> { + let _ = func.body()?; match trait_path.segment()?.name_ref()?.text().as_str() { - "Clone" => gen_clone_impl(adt, func), - "Debug" => gen_debug_impl(adt, func), - "Default" => gen_default_impl(adt, func), - "Hash" => gen_hash_impl(adt, func), - "PartialEq" => gen_partial_eq(adt, func, trait_ref), - "PartialOrd" => gen_partial_ord(adt, func, trait_ref), + "Clone" => { + stdx::always!(func.name().is_some_and(|name| name.text() == "clone")); + gen_clone_impl(adt) + } + "Debug" => gen_debug_impl(adt), + "Default" => gen_default_impl(adt), + "Hash" => { + stdx::always!(func.name().is_some_and(|name| name.text() == "hash")); + gen_hash_impl(adt) + } + "PartialEq" => { + stdx::always!(func.name().is_some_and(|name| name.text() == "eq")); + gen_partial_eq(adt, trait_ref) + } + "PartialOrd" => { + stdx::always!(func.name().is_some_and(|name| name.text() == "partial_cmp")); + gen_partial_ord(adt, trait_ref) + } _ => None, } } /// Generate a `Clone` impl based on the fields and members of the target type. -fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - stdx::always!(func.name().is_some_and(|name| name.text() == "clone")); +fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { fn gen_clone_call(target: ast::Expr) -> ast::Expr { let method = make::name_ref("clone"); make::expr_method_call(target, method, make::arg_list(None)).into() @@ -139,12 +148,11 @@ } }; let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } /// Generate a `Debug` impl based on the fields and members of the target type. -fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { +fn gen_debug_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { let annotated_name = adt.name()?; match adt { // `Debug` cannot be derived for unions, so no default impl can be provided. @@ -248,8 +256,7 @@ let body = make::block_expr(None, Some(match_expr.into())); let body = body.indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } ast::Adt::Struct(strukt) => { @@ -296,14 +303,13 @@ let method = make::name_ref("finish"); let expr = make::expr_method_call(expr, method, make::arg_list(None)).into(); let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } } } /// Generate a `Debug` impl based on the fields and members of the target type. -fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { +fn gen_default_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { fn gen_default_call() -> Option<ast::Expr> { let fn_name = make::ext::path_from_idents(["Default", "default"])?; Some(make::expr_call(make::expr_path(fn_name), make::arg_list(None)).into()) @@ -342,15 +348,13 @@ } }; let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } } } /// Generate a `Hash` impl based on the fields and members of the target type. -fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - stdx::always!(func.name().is_some_and(|name| name.text() == "hash")); +fn gen_hash_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> { fn gen_hash_call(target: ast::Expr) -> ast::Stmt { let method = make::name_ref("hash"); let arg = make::expr_path(make::ext::ident_path("state")); @@ -400,13 +404,11 @@ }, }; - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } /// Generate a `PartialEq` impl based on the fields and members of the target type. -fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef<'_>>) -> Option<()> { - stdx::always!(func.name().is_some_and(|name| name.text() == "eq")); +fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast::BlockExpr> { fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> { match expr { Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)), @@ -595,12 +597,10 @@ }, }; - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } -fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef<'_>>) -> Option<()> { - stdx::always!(func.name().is_some_and(|name| name.text() == "partial_cmp")); +fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast::BlockExpr> { fn gen_partial_eq_match(match_target: ast::Expr) -> Option<ast::Stmt> { let mut arms = vec![]; @@ -686,8 +686,7 @@ }, }; - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } fn make_discriminant() -> Option<ast::Expr> {
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 65072d9..11d2622 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs
@@ -111,10 +111,11 @@ ctx: &CompletionContext<'_>, super_chain_len: Option<usize>, ) { - if let Some(len) = super_chain_len { - if len > 0 && len < ctx.depth_from_crate_root { - self.add_keyword(ctx, "super::"); - } + if let Some(len) = super_chain_len + && len > 0 + && len < ctx.depth_from_crate_root + { + self.add_keyword(ctx, "super::"); } } @@ -643,10 +644,10 @@ let variants = enum_.variants(ctx.db); - if let Some(impl_) = impl_.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { - if impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_)) { - variants.iter().for_each(|variant| process_variant(*variant)); - } + if let Some(impl_) = impl_.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) + && impl_.self_ty(ctx.db).as_adt() == Some(hir::Adt::Enum(enum_)) + { + variants.iter().for_each(|variant| process_variant(*variant)); } for variant in variants {
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 5340d65..129964f 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs
@@ -258,12 +258,11 @@ fn on_trait_method(&mut self, func: hir::Function) -> ControlFlow<()> { // This needs to come before the `seen_methods` test, so that if we see the same method twice, // once as inherent and once not, we will include it. - if let ItemContainer::Trait(trait_) = func.container(self.ctx.db) { - if self.ctx.exclude_traits.contains(&trait_) - || trait_.complete(self.ctx.db) == Complete::IgnoreMethods - { - return ControlFlow::Continue(()); - } + if let ItemContainer::Trait(trait_) = func.container(self.ctx.db) + && (self.ctx.exclude_traits.contains(&trait_) + || trait_.complete(self.ctx.db) == Complete::IgnoreMethods) + { + return ControlFlow::Continue(()); } if func.self_param(self.ctx.db).is_some() @@ -1385,14 +1384,15 @@ fn skip_iter() { check_no_kw( r#" - //- minicore: iterator + //- minicore: iterator, clone, builtin_impls fn foo() { [].$0 } "#, expect![[r#" - me clone() (as Clone) fn(&self) -> Self - me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter + me clone() (as Clone) fn(&self) -> Self + me fmt(…) (use core::fmt::Debug) fn(&self, &mut Formatter<'_>) -> Result<(), Error> + me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter "#]], ); check_no_kw(
diff --git a/crates/ide-completion/src/completions/fn_param.rs b/crates/ide-completion/src/completions/fn_param.rs index 809e71c..fb78386 100644 --- a/crates/ide-completion/src/completions/fn_param.rs +++ b/crates/ide-completion/src/completions/fn_param.rs
@@ -128,10 +128,10 @@ { let module = scope.module().into(); scope.process_all_names(&mut |name, def| { - if let hir::ScopeDef::Local(local) = def { - if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module, true) { - cb(name, ty); - } + if let hir::ScopeDef::Local(local) = def + && let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module, true) + { + cb(name, ty); } }); }
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index 975c2f0..cdd77e7 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -228,24 +228,22 @@ .set_documentation(func.docs(ctx.db)) .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); - if let Some(source) = ctx.sema.source(func) { - if let Some(transformed_fn) = + if let Some(source) = ctx.sema.source(func) + && let Some(transformed_fn) = get_transformed_fn(ctx, source.value, impl_def, async_sugaring) - { - let function_decl = - function_declaration(ctx, &transformed_fn, source.file_id.macro_file()); - match ctx.config.snippet_cap { - Some(cap) => { - let snippet = format!("{function_decl} {{\n $0\n}}"); - item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet)); - } - None => { - let header = format!("{function_decl} {{"); - item.text_edit(TextEdit::replace(replacement_range, header)); - } - }; - item.add_to(acc, ctx.db); - } + { + let function_decl = function_declaration(ctx, &transformed_fn, source.file_id.macro_file()); + match ctx.config.snippet_cap { + Some(cap) => { + let snippet = format!("{function_decl} {{\n $0\n}}"); + item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet)); + } + None => { + let header = format!("{function_decl} {{"); + item.text_edit(TextEdit::replace(replacement_range, header)); + } + }; + item.add_to(acc, ctx.db); } } @@ -276,7 +274,7 @@ let assoc_item = assoc_item.clone_for_update(); // FIXME: Paths in nested macros are not handled well. See // `macro_generated_assoc_item2` test. - transform.apply(assoc_item.syntax()); + let assoc_item = ast::AssocItem::cast(transform.apply(assoc_item.syntax()))?; assoc_item.remove_attrs_and_docs(); Some(assoc_item) } @@ -301,7 +299,7 @@ let fn_ = fn_.clone_for_update(); // FIXME: Paths in nested macros are not handled well. See // `macro_generated_assoc_item2` test. - transform.apply(fn_.syntax()); + let fn_ = ast::Fn::cast(transform.apply(fn_.syntax()))?; fn_.remove_attrs_and_docs(); match async_ { AsyncSugaring::Desugar => { @@ -447,36 +445,36 @@ ) { let const_name = const_.name(ctx.db).map(|n| n.display_no_db(ctx.edition).to_smolstr()); - if let Some(const_name) = const_name { - if let Some(source) = ctx.sema.source(const_) { - let assoc_item = ast::AssocItem::Const(source.value); - if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { - let transformed_const = match transformed_item { - ast::AssocItem::Const(const_) => const_, - _ => unreachable!(), - }; + if let Some(const_name) = const_name + && let Some(source) = ctx.sema.source(const_) + { + let assoc_item = ast::AssocItem::Const(source.value); + if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { + let transformed_const = match transformed_item { + ast::AssocItem::Const(const_) => const_, + _ => unreachable!(), + }; - let label = - make_const_compl_syntax(ctx, &transformed_const, source.file_id.macro_file()); - let replacement = format!("{label} "); + let label = + make_const_compl_syntax(ctx, &transformed_const, source.file_id.macro_file()); + let replacement = format!("{label} "); - let mut item = - CompletionItem::new(SymbolKind::Const, replacement_range, label, ctx.edition); - item.lookup_by(format_smolstr!("const {const_name}")) - .set_documentation(const_.docs(ctx.db)) - .set_relevance(CompletionRelevance { - exact_name_match: true, - ..Default::default() - }); - match ctx.config.snippet_cap { - Some(cap) => item.snippet_edit( - cap, - TextEdit::replace(replacement_range, format!("{replacement}$0;")), - ), - None => item.text_edit(TextEdit::replace(replacement_range, replacement)), - }; - item.add_to(acc, ctx.db); - } + let mut item = + CompletionItem::new(SymbolKind::Const, replacement_range, label, ctx.edition); + item.lookup_by(format_smolstr!("const {const_name}")) + .set_documentation(const_.docs(ctx.db)) + .set_relevance(CompletionRelevance { + exact_name_match: true, + ..Default::default() + }); + match ctx.config.snippet_cap { + Some(cap) => item.snippet_edit( + cap, + TextEdit::replace(replacement_range, format!("{replacement}$0;")), + ), + None => item.text_edit(TextEdit::replace(replacement_range, replacement)), + }; + item.add_to(acc, ctx.db); } } }
diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs index 013747e..3333300 100644 --- a/crates/ide-completion/src/completions/mod_.rs +++ b/crates/ide-completion/src/completions/mod_.rs
@@ -26,18 +26,17 @@ let mut current_module = ctx.module; // For `mod $0`, `ctx.module` is its parent, but for `mod f$0`, it's `mod f` itself, but we're // interested in its parent. - if ctx.original_token.kind() == SyntaxKind::IDENT { - if let Some(module) = + if ctx.original_token.kind() == SyntaxKind::IDENT + && let Some(module) = ctx.original_token.parent_ancestors().nth(1).and_then(ast::Module::cast) - { - match ctx.sema.to_def(&module) { - Some(module) if module == current_module => { - if let Some(parent) = current_module.parent(ctx.db) { - current_module = parent; - } + { + match ctx.sema.to_def(&module) { + Some(module) if module == current_module => { + if let Some(parent) = current_module.parent(ctx.db) { + current_module = parent; } - _ => {} } + _ => {} } }
diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index 62fae1c..815ce51 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs
@@ -64,18 +64,17 @@ if let Some(hir::Adt::Enum(e)) = ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) + && (refutable || single_variant_enum(e)) { - if refutable || single_variant_enum(e) { - super::enum_variants_with_paths( - acc, - ctx, - e, - &pattern_ctx.impl_, - |acc, ctx, variant, path| { - acc.add_qualified_variant_pat(ctx, pattern_ctx, variant, path); - }, - ); - } + super::enum_variants_with_paths( + acc, + ctx, + e, + &pattern_ctx.impl_, + |acc, ctx, variant, path| { + acc.add_qualified_variant_pat(ctx, pattern_ctx, variant, path); + }, + ); } // FIXME: ideally, we should look at the type we are matching against and
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index d002385..0058611 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs
@@ -65,26 +65,19 @@ let cfg = ctx.config.import_path_config(ctx.is_nightly); - if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { - if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { - if let Some(drop_fn) = ctx.famous_defs().core_mem_drop() { - if let Some(path) = - ctx.module.find_path(ctx.db, ItemInNs::Values(drop_fn.into()), cfg) - { - cov_mark::hit!(postfix_drop_completion); - let mut item = postfix_snippet( - "drop", - "fn drop(&mut self)", - &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); - } - } - } + if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() + && receiver_ty.impls_trait(ctx.db, drop_trait, &[]) + && let Some(drop_fn) = ctx.famous_defs().core_mem_drop() + && let Some(path) = ctx.module.find_path(ctx.db, ItemInNs::Values(drop_fn.into()), cfg) + { + cov_mark::hit!(postfix_drop_completion); + let mut item = postfix_snippet( + "drop", + "fn drop(&mut self)", + &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); @@ -117,56 +110,50 @@ let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references()); let mut is_in_cond = false; - if let Some(parent) = dot_receiver_including_refs.syntax().parent() { - if let Some(second_ancestor) = parent.parent() { - let sec_ancestor_kind = second_ancestor.kind(); - if let Some(expr) = <Either<ast::IfExpr, ast::WhileExpr>>::cast(second_ancestor) { - is_in_cond = match expr { - Either::Left(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent), - Either::Right(it) => { - it.condition().is_some_and(|cond| *cond.syntax() == parent) - } - } + if let Some(parent) = dot_receiver_including_refs.syntax().parent() + && let Some(second_ancestor) = parent.parent() + { + let sec_ancestor_kind = second_ancestor.kind(); + if let Some(expr) = <Either<ast::IfExpr, ast::WhileExpr>>::cast(second_ancestor) { + is_in_cond = match expr { + Either::Left(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent), + Either::Right(it) => it.condition().is_some_and(|cond| *cond.syntax() == parent), } - match &try_enum { - Some(try_enum) if is_in_cond => match try_enum { - TryEnum::Result => { - postfix_snippet( - "let", - "let Ok(_)", - &format!("let Ok($0) = {receiver_text}"), - ) + } + match &try_enum { + Some(try_enum) if is_in_cond => match try_enum { + TryEnum::Result => { + postfix_snippet("let", "let Ok(_)", &format!("let Ok($0) = {receiver_text}")) .add_to(acc, ctx.db); - postfix_snippet( - "letm", - "let Ok(mut _)", - &format!("let Ok(mut $0) = {receiver_text}"), - ) - .add_to(acc, ctx.db); - } - TryEnum::Option => { - postfix_snippet( - "let", - "let Some(_)", - &format!("let Some($0) = {receiver_text}"), - ) - .add_to(acc, ctx.db); - postfix_snippet( - "letm", - "let Some(mut _)", - &format!("let Some(mut $0) = {receiver_text}"), - ) - .add_to(acc, ctx.db); - } - }, - _ if matches!(sec_ancestor_kind, STMT_LIST | EXPR_STMT) => { - postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) - .add_to(acc, ctx.db); - postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) - .add_to(acc, ctx.db); + postfix_snippet( + "letm", + "let Ok(mut _)", + &format!("let Ok(mut $0) = {receiver_text}"), + ) + .add_to(acc, ctx.db); } - _ => (), + TryEnum::Option => { + postfix_snippet( + "let", + "let Some(_)", + &format!("let Some($0) = {receiver_text}"), + ) + .add_to(acc, ctx.db); + postfix_snippet( + "letm", + "let Some(mut _)", + &format!("let Some(mut $0) = {receiver_text}"), + ) + .add_to(acc, ctx.db); + } + }, + _ if matches!(sec_ancestor_kind, STMT_LIST | EXPR_STMT) => { + postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) + .add_to(acc, ctx.db); + postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) + .add_to(acc, ctx.db); } + _ => (), } } @@ -258,25 +245,25 @@ ) .add_to(acc, ctx.db); postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db); - } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() { - if receiver_ty.impls_trait(ctx.db, trait_, &[]) { - postfix_snippet( - "for", - "for ele in expr {}", - &format!("for ele in {receiver_text} {{\n $0\n}}"), - ) - .add_to(acc, ctx.db); - } + } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() + && receiver_ty.impls_trait(ctx.db, trait_, &[]) + { + postfix_snippet( + "for", + "for ele in expr {}", + &format!("for ele in {receiver_text} {{\n $0\n}}"), + ) + .add_to(acc, ctx.db); } } let mut block_should_be_wrapped = true; if dot_receiver.syntax().kind() == BLOCK_EXPR { block_should_be_wrapped = false; - if let Some(parent) = dot_receiver.syntax().parent() { - if matches!(parent.kind(), IF_EXPR | WHILE_EXPR | LOOP_EXPR | FOR_EXPR) { - block_should_be_wrapped = true; - } + if let Some(parent) = dot_receiver.syntax().parent() + && matches!(parent.kind(), IF_EXPR | WHILE_EXPR | LOOP_EXPR | FOR_EXPR) + { + block_should_be_wrapped = true; } }; { @@ -292,10 +279,10 @@ postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db); } - if let ast::Expr::Literal(literal) = dot_receiver_including_refs.clone() { - if let Some(literal_text) = ast::String::cast(literal.token()) { - add_format_like_completions(acc, ctx, &dot_receiver_including_refs, cap, &literal_text); - } + if let ast::Expr::Literal(literal) = dot_receiver_including_refs.clone() + && let Some(literal_text) = ast::String::cast(literal.token()) + { + add_format_like_completions(acc, ctx, &dot_receiver_including_refs, cap, &literal_text); } postfix_snippet(
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 7c38c7d..fc27cbd 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs
@@ -37,9 +37,7 @@ true } // Type things are fine - ScopeDef::ModuleDef( - BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TraitAlias(_) | TypeAlias(_), - ) + ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_)) | ScopeDef::AdtSelfType(_) | ScopeDef::Unknown | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(),
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs index d2ab193..f39b641 100644 --- a/crates/ide-completion/src/completions/use_.rs +++ b/crates/ide-completion/src/completions/use_.rs
@@ -54,12 +54,10 @@ for (name, def) in module_scope { if let (Some(attrs), Some(defining_crate)) = (def.attrs(ctx.db), def.krate(ctx.db)) + && (!ctx.check_stability(Some(&attrs)) + || ctx.is_doc_hidden(&attrs, defining_crate)) { - if !ctx.check_stability(Some(&attrs)) - || ctx.is_doc_hidden(&attrs, defining_crate) - { - continue; - } + continue; } let is_name_already_imported = already_imported_names.contains(name.as_str());
diff --git a/crates/ide-completion/src/completions/vis.rs b/crates/ide-completion/src/completions/vis.rs index 38761f7..28d906d 100644 --- a/crates/ide-completion/src/completions/vis.rs +++ b/crates/ide-completion/src/completions/vis.rs
@@ -20,11 +20,11 @@ // Try completing next child module of the path that is still a parent of the current module let next_towards_current = ctx.module.path_to_root(ctx.db).into_iter().take_while(|it| it != module).last(); - if let Some(next) = next_towards_current { - if let Some(name) = next.name(ctx.db) { - cov_mark::hit!(visibility_qualified); - acc.add_module(ctx, path_ctx, next, name, vec![]); - } + if let Some(next) = next_towards_current + && let Some(name) = next.name(ctx.db) + { + cov_mark::hit!(visibility_qualified); + acc.add_module(ctx, path_ctx, next, name, vec![]); } acc.add_super_keyword(ctx, *super_chain_len);
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index bb6b13b..265462d 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs
@@ -526,7 +526,6 @@ hir::ModuleDef::Const(it) => self.is_visible(it), hir::ModuleDef::Static(it) => self.is_visible(it), hir::ModuleDef::Trait(it) => self.is_visible(it), - hir::ModuleDef::TraitAlias(it) => self.is_visible(it), hir::ModuleDef::TypeAlias(it) => self.is_visible(it), hir::ModuleDef::Macro(it) => self.is_visible(it), hir::ModuleDef::BuiltinType(_) => Visible::Yes,
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 5907723..6eb1727 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs
@@ -287,24 +287,22 @@ &spec_attr, fake_ident_token.clone(), ), - ) { - if let Some((fake_mapped_token, _)) = - fake_mapped_tokens.into_iter().min_by_key(|(_, rank)| *rank) - { - return Some(ExpansionResult { - original_file: original_file.value, - speculative_file, - original_offset, - speculative_offset: fake_ident_token.text_range().start(), - fake_ident_token, - derive_ctx: Some(( - actual_expansion, - fake_expansion, - fake_mapped_token.text_range().start(), - orig_attr, - )), - }); - } + ) && let Some((fake_mapped_token, _)) = + fake_mapped_tokens.into_iter().min_by_key(|(_, rank)| *rank) + { + return Some(ExpansionResult { + original_file: original_file.value, + speculative_file, + original_offset, + speculative_offset: fake_ident_token.text_range().start(), + fake_ident_token, + derive_ctx: Some(( + actual_expansion, + fake_expansion, + fake_mapped_token.text_range().start(), + orig_attr, + )), + }); } if let Some(spec_adt) = @@ -535,14 +533,13 @@ NameRefKind::Path(PathCompletionCtx { kind: PathKind::Expr { .. }, path, .. }, ..), .. } = &nameref_ctx + && is_in_token_of_for_loop(path) { - if is_in_token_of_for_loop(path) { - // for pat $0 - // there is nothing to complete here except `in` keyword - // don't bother populating the context - // Ideally this special casing wouldn't be needed, but the parser recovers - return None; - } + // for pat $0 + // there is nothing to complete here except `in` keyword + // don't bother populating the context + // Ideally this special casing wouldn't be needed, but the parser recovers + return None; } qual_ctx = qualifier_ctx; @@ -562,6 +559,7 @@ token: &SyntaxToken, name_like: &ast::NameLike, ) -> (Option<Type<'db>>, Option<NameOrNameRef>) { + let token = prev_assign_token_at_trivia(token.clone()); let mut node = match token.parent() { Some(it) => it, None => return (None, None), @@ -632,6 +630,17 @@ .map(TypeInfo::original); (ty, None) }, + ast::BinExpr(it) => { + if let Some(ast::BinaryOp::Assignment { op: None }) = it.op_kind() { + let ty = it.lhs() + .and_then(|lhs| sema.type_of_expr(&lhs)) + .or_else(|| it.rhs().and_then(|rhs| sema.type_of_expr(&rhs))) + .map(TypeInfo::original); + (ty, None) + } else { + (None, None) + } + }, ast::ArgList(_) => { cov_mark::hit!(expected_type_fn_param); ActiveParameter::at_token( @@ -951,29 +960,26 @@ let inbetween_body_and_decl_check = |node: SyntaxNode| { if let Some(NodeOrToken::Node(n)) = syntax::algo::non_trivia_sibling(node.into(), syntax::Direction::Prev) + && let Some(item) = ast::Item::cast(n) { - if let Some(item) = ast::Item::cast(n) { - let is_inbetween = match &item { - ast::Item::Const(it) => it.body().is_none() && it.semicolon_token().is_none(), - ast::Item::Enum(it) => it.variant_list().is_none(), - ast::Item::ExternBlock(it) => it.extern_item_list().is_none(), - ast::Item::Fn(it) => it.body().is_none() && it.semicolon_token().is_none(), - ast::Item::Impl(it) => it.assoc_item_list().is_none(), - ast::Item::Module(it) => { - it.item_list().is_none() && it.semicolon_token().is_none() - } - ast::Item::Static(it) => it.body().is_none(), - ast::Item::Struct(it) => { - it.field_list().is_none() && it.semicolon_token().is_none() - } - ast::Item::Trait(it) => it.assoc_item_list().is_none(), - ast::Item::TypeAlias(it) => it.ty().is_none() && it.semicolon_token().is_none(), - ast::Item::Union(it) => it.record_field_list().is_none(), - _ => false, - }; - if is_inbetween { - return Some(item); + let is_inbetween = match &item { + ast::Item::Const(it) => it.body().is_none() && it.semicolon_token().is_none(), + ast::Item::Enum(it) => it.variant_list().is_none(), + ast::Item::ExternBlock(it) => it.extern_item_list().is_none(), + ast::Item::Fn(it) => it.body().is_none() && it.semicolon_token().is_none(), + ast::Item::Impl(it) => it.assoc_item_list().is_none(), + ast::Item::Module(it) => it.item_list().is_none() && it.semicolon_token().is_none(), + ast::Item::Static(it) => it.body().is_none(), + ast::Item::Struct(it) => { + it.field_list().is_none() && it.semicolon_token().is_none() } + ast::Item::Trait(it) => it.assoc_item_list().is_none(), + ast::Item::TypeAlias(it) => it.ty().is_none() && it.semicolon_token().is_none(), + ast::Item::Union(it) => it.record_field_list().is_none(), + _ => false, + }; + if is_inbetween { + return Some(item); } } None @@ -1031,9 +1037,6 @@ sema.source(trait_)?.value.generic_param_list() } } - hir::ModuleDef::TraitAlias(trait_) => { - sema.source(trait_)?.value.generic_param_list() - } hir::ModuleDef::TypeAlias(ty_) => { sema.source(ty_)?.value.generic_param_list() } @@ -1504,10 +1507,10 @@ } }; } - } else if let Some(segment) = path.segment() { - if segment.coloncolon_token().is_some() { - path_ctx.qualified = Qualified::Absolute; - } + } else if let Some(segment) = path.segment() + && segment.coloncolon_token().is_some() + { + path_ctx.qualified = Qualified::Absolute; } let mut qualifier_ctx = QualifierCtx::default(); @@ -1532,38 +1535,30 @@ if let Some(top) = top_node { if let Some(NodeOrToken::Node(error_node)) = syntax::algo::non_trivia_sibling(top.clone().into(), syntax::Direction::Prev) + && error_node.kind() == SyntaxKind::ERROR { - if error_node.kind() == SyntaxKind::ERROR { - for token in - error_node.children_with_tokens().filter_map(NodeOrToken::into_token) - { - match token.kind() { - SyntaxKind::UNSAFE_KW => qualifier_ctx.unsafe_tok = Some(token), - SyntaxKind::ASYNC_KW => qualifier_ctx.async_tok = Some(token), - SyntaxKind::SAFE_KW => qualifier_ctx.safe_tok = Some(token), - _ => {} - } + for token in error_node.children_with_tokens().filter_map(NodeOrToken::into_token) { + match token.kind() { + SyntaxKind::UNSAFE_KW => qualifier_ctx.unsafe_tok = Some(token), + SyntaxKind::ASYNC_KW => qualifier_ctx.async_tok = Some(token), + SyntaxKind::SAFE_KW => qualifier_ctx.safe_tok = Some(token), + _ => {} } - qualifier_ctx.vis_node = error_node.children().find_map(ast::Visibility::cast); } + qualifier_ctx.vis_node = error_node.children().find_map(ast::Visibility::cast); } - if let PathKind::Item { .. } = path_ctx.kind { - if qualifier_ctx.none() { - if let Some(t) = top.first_token() { - if let Some(prev) = t - .prev_token() - .and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev)) - { - if ![T![;], T!['}'], T!['{']].contains(&prev.kind()) { - // This was inferred to be an item position path, but it seems - // to be part of some other broken node which leaked into an item - // list - return None; - } - } - } - } + if let PathKind::Item { .. } = path_ctx.kind + && qualifier_ctx.none() + && let Some(t) = top.first_token() + && let Some(prev) = + t.prev_token().and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev)) + && ![T![;], T!['}'], T!['{']].contains(&prev.kind()) + { + // This was inferred to be an item position path, but it seems + // to be part of some other broken node which leaked into an item + // list + return None; } } } @@ -1872,3 +1867,23 @@ } None } + +fn prev_assign_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { + while token.kind().is_trivia() + && let Some(prev) = token.prev_token() + && let T![=] + | T![+=] + | T![/=] + | T![*=] + | T![%=] + | T![>>=] + | T![<<=] + | T![-=] + | T![|=] + | T![&=] + | T![^=] = prev.kind() + { + token = prev + } + token +}
diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs index 75c2096..7a8c70f 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs
@@ -434,3 +434,53 @@ expect!["ty: u32, name: ?"], ); } + +#[test] +fn expected_type_assign() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let x: &mut State = &mut State::Stop; + x = $0; +} +"#, + expect![[r#"ty: &'_ mut State, name: ?"#]], + ); +} + +#[test] +fn expected_type_deref_assign() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let x: &mut State = &mut State::Stop; + match x { + State::Stop => { + *x = $0; + }, + } +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); +} + +#[test] +fn expected_type_deref_assign_at_block_end() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let x: &mut State = &mut State::Stop; + match x { + State::Stop => { + *x = $0 + }, + } +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); +}
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index dcaac39..5fb9dc9 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs
@@ -399,7 +399,6 @@ SymbolKind::Struct => "st", SymbolKind::ToolModule => "tm", SymbolKind::Trait => "tt", - SymbolKind::TraitAlias => "tr", SymbolKind::TypeAlias => "ta", SymbolKind::TypeParam => "tp", SymbolKind::Union => "un", @@ -636,10 +635,10 @@ } pub(crate) fn set_detail(&mut self, detail: Option<impl Into<String>>) -> &mut Builder { self.detail = detail.map(Into::into); - if let Some(detail) = &self.detail { - if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { - self.detail = Some(detail.split('\n').next().unwrap().to_owned()); - } + if let Some(detail) = &self.detail + && never!(detail.contains('\n'), "multiline detail:\n{}", detail) + { + self.detail = Some(detail.split('\n').next().unwrap().to_owned()); } self }
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 1fdd4cd..1a4c97e 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs
@@ -10,6 +10,7 @@ #[cfg(test)] mod tests; +use base_db::salsa; use ide_db::{ FilePosition, FxHashSet, RootDatabase, imports::insert_use::{self, ImportScope}, @@ -208,9 +209,9 @@ // when the user types a bare `_` (that is it does not belong to an identifier) // the user might just wanted to type a `_` for type inference or pattern discarding // so try to suppress completions in those cases - if trigger_character == Some('_') && ctx.original_token.kind() == syntax::SyntaxKind::UNDERSCORE - { - if let CompletionAnalysis::NameRef(NameRefContext { + if trigger_character == Some('_') + && ctx.original_token.kind() == syntax::SyntaxKind::UNDERSCORE + && let CompletionAnalysis::NameRef(NameRefContext { kind: NameRefKind::Path( path_ctx @ PathCompletionCtx { @@ -220,17 +221,15 @@ ), .. }) = analysis - { - if path_ctx.is_trivial_path() { - return None; - } - } + && path_ctx.is_trivial_path() + { + return None; } { let acc = &mut completions; - match analysis { + salsa::attach(db, || match analysis { CompletionAnalysis::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx), CompletionAnalysis::NameRef(name_ref_ctx) => { completions::complete_name_ref(acc, ctx, name_ref_ctx) @@ -258,7 +257,7 @@ ); } CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (), - } + }) } Some(completions.into())
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index c6b8af3..7d23f9d 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs
@@ -164,19 +164,18 @@ let expected_fn_type = ctx.completion.expected_type.as_ref().is_some_and(|ty| ty.is_fn() || ty.is_closure()); - if !expected_fn_type { - if let Some(receiver) = &dot_access.receiver { - if let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone()) { - builder.insert(receiver.syntax().text_range().start(), "(".to_owned()); - builder.insert(ctx.source_range().end(), ")".to_owned()); + if !expected_fn_type + && let Some(receiver) = &dot_access.receiver + && let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone()) + { + builder.insert(receiver.syntax().text_range().start(), "(".to_owned()); + builder.insert(ctx.source_range().end(), ")".to_owned()); - let is_parens_needed = - !matches!(dot_access.kind, DotAccessKind::Method { has_parens: true }); + let is_parens_needed = + !matches!(dot_access.kind, DotAccessKind::Method { has_parens: true }); - if is_parens_needed { - builder.insert(ctx.source_range().end(), "()".to_owned()); - } - } + if is_parens_needed { + builder.insert(ctx.source_range().end(), "()".to_owned()); } } @@ -184,12 +183,11 @@ } else { item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name)); } - if let Some(receiver) = &dot_access.receiver { - if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) { - if let Some(ref_mode) = compute_ref_match(ctx.completion, ty) { - item.ref_match(ref_mode, original.syntax().text_range().start()); - } - } + if let Some(receiver) = &dot_access.receiver + && let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) + && let Some(ref_mode) = compute_ref_match(ctx.completion, ty) + { + item.ref_match(ref_mode, original.syntax().text_range().start()); } item.doc_aliases(ctx.doc_aliases); item.build(db) @@ -437,26 +435,21 @@ path_ctx, PathCompletionCtx { kind: PathKind::Type { .. }, has_type_args: false, .. } ) && config.callable.is_some(); - if type_path_no_ty_args { - if let Some(cap) = cap { - let has_non_default_type_params = match resolution { - ScopeDef::ModuleDef(hir::ModuleDef::Adt(it)) => it.has_non_default_type_params(db), - ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(it)) => { - it.has_non_default_type_params(db) - } - _ => false, - }; - - if has_non_default_type_params { - cov_mark::hit!(inserts_angle_brackets_for_generics); - item.lookup_by(name.clone()) - .label(SmolStr::from_iter([&name, "<…>"])) - .trigger_call_info() - .insert_snippet( - cap, - format!("{}<$0>", local_name.display(db, completion.edition)), - ); + if type_path_no_ty_args && let Some(cap) = cap { + let has_non_default_type_params = match resolution { + ScopeDef::ModuleDef(hir::ModuleDef::Adt(it)) => it.has_non_default_type_params(db), + ScopeDef::ModuleDef(hir::ModuleDef::TypeAlias(it)) => { + it.has_non_default_type_params(db) } + _ => false, + }; + + if has_non_default_type_params { + cov_mark::hit!(inserts_angle_brackets_for_generics); + item.lookup_by(name.clone()) + .label(SmolStr::from_iter([&name, "<…>"])) + .trigger_call_info() + .insert_snippet(cap, format!("{}<$0>", local_name.display(db, completion.edition))); } } @@ -493,10 +486,7 @@ | ScopeDef::Label(_) | ScopeDef::Unknown | ScopeDef::ModuleDef( - ModuleDef::Trait(_) - | ModuleDef::TraitAlias(_) - | ModuleDef::Module(_) - | ModuleDef::TypeAlias(_), + ModuleDef::Trait(_) | ModuleDef::Module(_) | ModuleDef::TypeAlias(_), ) => (), }; @@ -549,9 +539,6 @@ ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), - ScopeDef::ModuleDef(TraitAlias(..)) => { - CompletionItemKind::SymbolKind(SymbolKind::TraitAlias) - } ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias), ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { @@ -634,23 +621,24 @@ if expected_type.could_unify_with(ctx.db, completion_ty) { return None; } - if let Some(expected_without_ref) = &expected_without_ref { - if completion_ty.autoderef(ctx.db).any(|ty| ty == *expected_without_ref) { - cov_mark::hit!(suggest_ref); - let mutability = if expected_type.is_mutable_reference() { - hir::Mutability::Mut - } else { - hir::Mutability::Shared - }; - return Some(CompletionItemRefMode::Reference(mutability)); - } + if let Some(expected_without_ref) = &expected_without_ref + && completion_ty.autoderef(ctx.db).any(|ty| ty == *expected_without_ref) + { + cov_mark::hit!(suggest_ref); + let mutability = if expected_type.is_mutable_reference() { + hir::Mutability::Mut + } else { + hir::Mutability::Shared + }; + return Some(CompletionItemRefMode::Reference(mutability)); } - if let Some(completion_without_ref) = completion_without_ref { - if completion_without_ref == *expected_type && completion_without_ref.is_copy(ctx.db) { - cov_mark::hit!(suggest_deref); - return Some(CompletionItemRefMode::Dereference); - } + if let Some(completion_without_ref) = completion_without_ref + && completion_without_ref == *expected_type + && completion_without_ref.is_copy(ctx.db) + { + cov_mark::hit!(suggest_deref); + return Some(CompletionItemRefMode::Dereference); } None @@ -664,10 +652,10 @@ ) { if let Some(original_path) = &path_ctx.original_path { // At least one char was typed by the user already, in that case look for the original path - if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) { - if let Some(ref_mode) = compute_ref_match(completion, ty) { - item.ref_match(ref_mode, original_path.syntax().text_range().start()); - } + if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) + && let Some(ref_mode) = compute_ref_match(completion, ty) + { + item.ref_match(ref_mode, original_path.syntax().text_range().start()); } } else { // completion requested on an empty identifier, there is no path here yet.
diff --git a/crates/ide-completion/src/render/const_.rs b/crates/ide-completion/src/render/const_.rs index f11b302..707a8ae 100644 --- a/crates/ide-completion/src/render/const_.rs +++ b/crates/ide-completion/src/render/const_.rs
@@ -25,10 +25,10 @@ .detail(detail) .set_relevance(ctx.completion_relevance()); - if let Some(actm) = const_.as_assoc_item(db) { - if let Some(trt) = actm.container_or_implemented_trait(db) { - item.trait_name(trt.name(db).display_no_db(ctx.completion.edition).to_smolstr()); - } + if let Some(actm) = const_.as_assoc_item(db) + && let Some(trt) = actm.container_or_implemented_trait(db) + { + item.trait_name(trt.name(db).display_no_db(ctx.completion.edition).to_smolstr()); } item.insert_text(escaped_name);
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 7669aec..c466019 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs
@@ -132,10 +132,10 @@ super::path_ref_match(completion, path_ctx, &ret_type, &mut item); } FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => { - if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) { - if let Some(ref_mode) = compute_ref_match(completion, &ret_type) { - item.ref_match(ref_mode, original_expr.syntax().text_range().start()); - } + if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) + && let Some(ref_mode) = compute_ref_match(completion, &ret_type) + { + item.ref_match(ref_mode, original_expr.syntax().text_range().start()); } } _ => (), @@ -169,12 +169,10 @@ item.add_import(import_to_add); } None => { - if let Some(actm) = assoc_item { - if let Some(trt) = actm.container_or_implemented_trait(db) { - item.trait_name( - trt.name(db).display_no_db(ctx.completion.edition).to_smolstr(), - ); - } + if let Some(actm) = assoc_item + && let Some(trt) = actm.container_or_implemented_trait(db) + { + item.trait_name(trt.name(db).display_no_db(ctx.completion.edition).to_smolstr()); } } } @@ -378,15 +376,13 @@ ctx.config.callable.as_ref()?; // Don't add parentheses if the expected type is a function reference with the same signature. - if let Some(expected) = ctx.expected_type.as_ref().filter(|e| e.is_fn()) { - if let Some(expected) = expected.as_callable(ctx.db) { - if let Some(completed) = func.ty(ctx.db).as_callable(ctx.db) { - if expected.sig() == completed.sig() { - cov_mark::hit!(no_call_parens_if_fn_ptr_needed); - return None; - } - } - } + if let Some(expected) = ctx.expected_type.as_ref().filter(|e| e.is_fn()) + && let Some(expected) = expected.as_callable(ctx.db) + && let Some(completed) = func.ty(ctx.db).as_callable(ctx.db) + && expected.sig() == completed.sig() + { + cov_mark::hit!(no_call_parens_if_fn_ptr_needed); + return None; } let self_param = if has_dot_receiver || matches!(func_kind, FuncKind::Method(_, Some(_))) {
diff --git a/crates/ide-completion/src/render/type_alias.rs b/crates/ide-completion/src/render/type_alias.rs index d57feee..3fc0f36 100644 --- a/crates/ide-completion/src/render/type_alias.rs +++ b/crates/ide-completion/src/render/type_alias.rs
@@ -51,10 +51,10 @@ .detail(detail) .set_relevance(ctx.completion_relevance()); - if let Some(actm) = type_alias.as_assoc_item(db) { - if let Some(trt) = actm.container_or_implemented_trait(db) { - item.trait_name(trt.name(db).display_no_db(ctx.completion.edition).to_smolstr()); - } + if let Some(actm) = type_alias.as_assoc_item(db) + && let Some(trt) = actm.container_or_implemented_trait(db) + { + item.trait_name(trt.name(db).display_no_db(ctx.completion.edition).to_smolstr()); } item.insert_text(escaped_name);
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index fdc3d9a..4b3b271 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs
@@ -26,7 +26,7 @@ use base_db::SourceDatabase; use expect_test::Expect; -use hir::PrefixKind; +use hir::{PrefixKind, setup_tracing}; use ide_db::{ FilePosition, RootDatabase, SnippetCap, imports::insert_use::{ImportGranularity, InsertUseConfig}, @@ -120,6 +120,8 @@ include_keywords: bool, trigger_character: Option<char>, ) -> Vec<CompletionItem> { + let _tracing = setup_tracing(); + // filter out all but one built-in type completion for smaller test outputs let items = get_all_items(config, ra_fixture, trigger_character); items
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 27c91bc..bb10086 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -1,3 +1,4 @@ +use base_db::salsa; use expect_test::{Expect, expect}; use crate::{ @@ -19,25 +20,29 @@ let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap(); let mut acc = crate::completions::Completions::default(); - if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) = - &analysis - { - crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx); - } - if let CompletionAnalysis::NameRef(name_ref_ctx) = &analysis { - match &name_ref_ctx.kind { - NameRefKind::Path(path) => { - crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path); - } - NameRefKind::DotAccess(dot_access) => { - crate::completions::flyimport::import_on_the_fly_dot(&mut acc, &ctx, dot_access); - } - NameRefKind::Pattern(pattern) => { - crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pattern); - } - _ => (), + salsa::attach(ctx.db, || { + if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) = + &analysis + { + crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx); } - } + if let CompletionAnalysis::NameRef(name_ref_ctx) = &analysis { + match &name_ref_ctx.kind { + NameRefKind::Path(path) => { + crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path); + } + NameRefKind::DotAccess(dot_access) => { + crate::completions::flyimport::import_on_the_fly_dot( + &mut acc, &ctx, dot_access, + ); + } + NameRefKind::Pattern(pattern) => { + crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pattern); + } + _ => (), + } + } + }); expect.assert_eq(&super::render_completion_list(Vec::from(acc))); }
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 1482031..84ddff8 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs
@@ -677,6 +677,7 @@ expect![[r#" fn foo() (as Foo) fn() -> Self ex Bar + ex Bar::foo() ex bar() "#]], ); @@ -706,6 +707,7 @@ fn bar() fn() fn foo() (as Foo) fn() -> Self ex Bar + ex Bar::foo() ex bar() "#]], ); @@ -734,6 +736,7 @@ expect![[r#" fn foo() (as Foo) fn() -> Self ex Bar + ex Bar::foo() ex bar() "#]], );
diff --git a/crates/ide-db/src/active_parameter.rs b/crates/ide-db/src/active_parameter.rs index 9edfc11..4fb7d14 100644 --- a/crates/ide-db/src/active_parameter.rs +++ b/crates/ide-db/src/active_parameter.rs
@@ -125,7 +125,6 @@ hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(), - hir::PathResolution::Def(hir::ModuleDef::TraitAlias(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => { variant = Some(it);
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index a4a140e..61a21cc 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs
@@ -16,7 +16,7 @@ ExternCrateDecl, Field, Function, GenericDef, GenericParam, GenericSubstitution, HasContainer, HasVisibility, HirDisplay, Impl, InlineAsmOperand, ItemContainer, Label, Local, Macro, Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, Trait, - TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, + TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use span::Edition; use stdx::{format_to, impl_from}; @@ -40,7 +40,6 @@ Const(Const), Static(Static), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), SelfType(Impl), GenericParam(GenericParam), @@ -83,7 +82,6 @@ Definition::Const(it) => it.module(db), Definition::Static(it) => it.module(db), Definition::Trait(it) => it.module(db), - Definition::TraitAlias(it) => it.module(db), Definition::TypeAlias(it) => it.module(db), Definition::Variant(it) => it.module(db), Definition::SelfType(it) => it.module(db), @@ -122,7 +120,6 @@ Definition::Const(it) => container_to_definition(it.container(db)), Definition::Static(it) => container_to_definition(it.container(db)), Definition::Trait(it) => container_to_definition(it.container(db)), - Definition::TraitAlias(it) => container_to_definition(it.container(db)), Definition::TypeAlias(it) => container_to_definition(it.container(db)), Definition::Variant(it) => Some(Adt::Enum(it.parent_enum(db)).into()), Definition::SelfType(it) => Some(it.module(db).into()), @@ -151,7 +148,6 @@ Definition::Const(it) => it.visibility(db), Definition::Static(it) => it.visibility(db), Definition::Trait(it) => it.visibility(db), - Definition::TraitAlias(it) => it.visibility(db), Definition::TypeAlias(it) => it.visibility(db), Definition::Variant(it) => it.visibility(db), Definition::ExternCrateDecl(it) => it.visibility(db), @@ -185,7 +181,6 @@ Definition::Const(it) => it.name(db)?, Definition::Static(it) => it.name(db), Definition::Trait(it) => it.name(db), - Definition::TraitAlias(it) => it.name(db), Definition::TypeAlias(it) => it.name(db), Definition::BuiltinType(it) => it.name(), Definition::TupleField(it) => it.name(), @@ -230,7 +225,6 @@ Definition::Const(it) => it.docs_with_rangemap(db), Definition::Static(it) => it.docs_with_rangemap(db), Definition::Trait(it) => it.docs_with_rangemap(db), - Definition::TraitAlias(it) => it.docs_with_rangemap(db), Definition::TypeAlias(it) => { it.docs_with_rangemap(db).or_else(|| { // docs are missing, try to fall back to the docs of the aliased item. @@ -321,7 +315,6 @@ Definition::Const(it) => it.display(db, display_target).to_string(), Definition::Static(it) => it.display(db, display_target).to_string(), Definition::Trait(it) => it.display(db, display_target).to_string(), - Definition::TraitAlias(it) => it.display(db, display_target).to_string(), Definition::TypeAlias(it) => it.display(db, display_target).to_string(), Definition::BuiltinType(it) => { it.name().display(db, display_target.edition).to_string() @@ -589,7 +582,6 @@ ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?), ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?), ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?), - ast::Item::TraitAlias(it) => Definition::TraitAlias(sema.to_def(&it)?), ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?), ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)), ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)), @@ -610,18 +602,16 @@ let local = sema.to_def(&ident_pat)?; let pat_parent = ident_pat.syntax().parent(); - if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) { - if record_pat_field.name_ref().is_none() { - if let Some((field, _, adt_subst)) = - sema.resolve_record_pat_field_with_subst(&record_pat_field) - { - return Some(NameClass::PatFieldShorthand { - local_def: local, - field_ref: field, - adt_subst, - }); - } - } + if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) + && record_pat_field.name_ref().is_none() + && let Some((field, _, adt_subst)) = + sema.resolve_record_pat_field_with_subst(&record_pat_field) + { + return Some(NameClass::PatFieldShorthand { + local_def: local, + field_ref: field, + adt_subst, + }); } Some(NameClass::Definition(Definition::Local(local))) } @@ -755,30 +745,27 @@ let parent = name_ref.syntax().parent()?; - if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) { - if let Some((field, local, _, adt_subst)) = + if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) + && let Some((field, local, _, adt_subst)) = sema.resolve_record_field_with_substitution(&record_field) - { - let res = match local { - None => NameRefClass::Definition(Definition::Field(field), Some(adt_subst)), - Some(local) => NameRefClass::FieldShorthand { - field_ref: field, - local_ref: local, - adt_subst, - }, - }; - return Some(res); - } + { + let res = match local { + None => NameRefClass::Definition(Definition::Field(field), Some(adt_subst)), + Some(local) => { + NameRefClass::FieldShorthand { field_ref: field, local_ref: local, adt_subst } + } + }; + return Some(res); } if let Some(path) = ast::PathSegment::cast(parent.clone()).map(|it| it.parent_path()) { - if path.parent_path().is_none() { - if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { - // Only use this to resolve to macro calls for last segments as qualifiers resolve - // to modules below. - if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { - return Some(NameRefClass::Definition(Definition::Macro(macro_def), None)); - } + if path.parent_path().is_none() + && let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) + { + // Only use this to resolve to macro calls for last segments as qualifiers resolve + // to modules below. + if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { + return Some(NameRefClass::Definition(Definition::Macro(macro_def), None)); } } return sema @@ -820,8 +807,8 @@ // ^^^^^ let containing_path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; let resolved = sema.resolve_path(&containing_path)?; - if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved { - if let Some(ty) = tr + if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved + && let Some(ty) = tr .items_with_supertraits(sema.db) .iter() .filter_map(|&assoc| match assoc { @@ -833,7 +820,6 @@ // No substitution, this can only occur in type position. return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None)); } - } None }, ast::UseBoundGenericArgs(_) => { @@ -901,7 +887,7 @@ } impl_from!( - Field, Module, Function, Adt, Variant, Const, Static, Trait, TraitAlias, TypeAlias, BuiltinType, Local, + Field, Module, Function, Adt, Variant, Const, Static, Trait, TypeAlias, BuiltinType, Local, GenericParam, Label, Macro, ExternCrateDecl for Definition ); @@ -981,7 +967,6 @@ ModuleDef::Const(it) => Definition::Const(it), ModuleDef::Static(it) => Definition::Static(it), ModuleDef::Trait(it) => Definition::Trait(it), - ModuleDef::TraitAlias(it) => Definition::TraitAlias(it), ModuleDef::TypeAlias(it) => Definition::TypeAlias(it), ModuleDef::Macro(it) => Definition::Macro(it), ModuleDef::BuiltinType(it) => Definition::BuiltinType(it), @@ -1023,7 +1008,6 @@ GenericDef::Function(it) => it.into(), GenericDef::Adt(it) => it.into(), GenericDef::Trait(it) => it.into(), - GenericDef::TraitAlias(it) => it.into(), GenericDef::TypeAlias(it) => it.into(), GenericDef::Impl(it) => it.into(), GenericDef::Const(it) => it.into(), @@ -1039,7 +1023,6 @@ Definition::Function(it) => Ok(it.into()), Definition::Adt(it) => Ok(it.into()), Definition::Trait(it) => Ok(it.into()), - Definition::TraitAlias(it) => Ok(it.into()), Definition::TypeAlias(it) => Ok(it.into()), Definition::SelfType(it) => Ok(it.into()), Definition::Const(it) => Ok(it.into()),
diff --git a/crates/ide-db/src/documentation.rs b/crates/ide-db/src/documentation.rs index 30c355f..cab19aa 100644 --- a/crates/ide-db/src/documentation.rs +++ b/crates/ide-db/src/documentation.rs
@@ -195,8 +195,7 @@ } impl_has_docs![ - Variant, Field, Static, Const, Trait, TraitAlias, TypeAlias, Macro, Function, Adt, Module, - Impl, Crate, + Variant, Field, Static, Const, Trait, TypeAlias, Macro, Function, Adt, Module, Impl, Crate, ]; macro_rules! impl_has_docs_enum {
diff --git a/crates/ide-db/src/generated/lints.rs b/crates/ide-db/src/generated/lints.rs index f9eb44d..14bc380 100644 --- a/crates/ide-db/src/generated/lints.rs +++ b/crates/ide-db/src/generated/lints.rs
@@ -10706,20 +10706,6 @@ deny_since: None, }, Lint { - label: "strict_overflow_ops", - description: r##"# `strict_overflow_ops` - -The tracking issue for this feature is: [#118260] - -[#118260]: https://github.com/rust-lang/rust/issues/118260 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { label: "strict_provenance_atomic_ptr", description: r##"# `strict_provenance_atomic_ptr`
diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index 3404290..1e54058 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs
@@ -70,11 +70,11 @@ }; let mut defs: VecDeque<_> = module.declarations(db).into(); while let Some(def) = defs.pop_front() { - if let ModuleDef::Module(submodule) = def { - if submodule.is_inline(db) { - defs.extend(submodule.declarations(db)); - submodule.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into())); - } + if let ModuleDef::Module(submodule) = def + && submodule.is_inline(db) + { + defs.extend(submodule.declarations(db)); + submodule.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into())); } cb(def.into()); }
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index 9f35988..1012f99 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs
@@ -475,8 +475,6 @@ } // FIXME ModuleDef::Trait(_) => return None, - // FIXME - ModuleDef::TraitAlias(_) => return None, ModuleDef::TypeAlias(alias) => alias.ty(db), ModuleDef::BuiltinType(builtin) => builtin.ty(db), ModuleDef::Adt(adt) => adt.ty(db),
diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs index 813f383..b174adf 100644 --- a/crates/ide-db/src/imports/insert_use.rs +++ b/crates/ide-db/src/imports/insert_use.rs
@@ -97,12 +97,11 @@ .map(ImportScopeKind::Module) .map(|kind| ImportScope { kind, required_cfgs }); } else if let Some(has_attrs) = ast::AnyHasAttrs::cast(syntax) { - if block.is_none() { - if let Some(b) = ast::BlockExpr::cast(has_attrs.syntax().clone()) { - if let Some(b) = sema.original_ast_node(b) { - block = b.stmt_list(); - } - } + if block.is_none() + && let Some(b) = ast::BlockExpr::cast(has_attrs.syntax().clone()) + && let Some(b) = sema.original_ast_node(b) + { + block = b.stmt_list(); } if has_attrs .attrs() @@ -195,7 +194,7 @@ use_tree = use_tree.clone_for_update(); use_tree.wrap_in_tree_list(); } - let use_item = make::use_(None, use_tree).clone_for_update(); + 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()) { @@ -349,26 +348,24 @@ seen_one_style_groups.push((curr_vis.clone(), curr_attrs.clone())); } else if eq_visibility(prev_vis, curr_vis.clone()) && eq_attrs(prev_attrs, curr_attrs.clone()) + && let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) + && let Some((prev_prefix, _)) = common_prefix(&prev_path, &curr_path) { - if let Some((prev_path, curr_path)) = prev.path().zip(curr.path()) { - if let Some((prev_prefix, _)) = common_prefix(&prev_path, &curr_path) { - if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() { - let prefix_c = prev_prefix.qualifiers().count(); - let curr_c = curr_path.qualifiers().count() - prefix_c; - let prev_c = prev_path.qualifiers().count() - prefix_c; - if curr_c == 1 && prev_c == 1 { - // Same prefix, only differing in the last segment and no use tree lists so this has to be of item style. - break ImportGranularityGuess::Item; - } else { - // Same prefix and no use tree list but differs in more than one segment at the end. This might be module style still. - res = ImportGranularityGuess::ModuleOrItem; - } - } else { - // Same prefix with item tree lists, has to be module style as it - // can't be crate style since the trees wouldn't share a prefix then. - break ImportGranularityGuess::Module; - } + if prev.use_tree_list().is_none() && curr.use_tree_list().is_none() { + let prefix_c = prev_prefix.qualifiers().count(); + let curr_c = curr_path.qualifiers().count() - prefix_c; + let prev_c = prev_path.qualifiers().count() - prefix_c; + if curr_c == 1 && prev_c == 1 { + // Same prefix, only differing in the last segment and no use tree lists so this has to be of item style. + break ImportGranularityGuess::Item; + } else { + // Same prefix and no use tree list but differs in more than one segment at the end. This might be module style still. + res = ImportGranularityGuess::ModuleOrItem; } + } else { + // Same prefix with item tree lists, has to be module style as it + // can't be crate style since the trees wouldn't share a prefix then. + break ImportGranularityGuess::Module; } } prev = curr;
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index c94be7e..1f074de 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs
@@ -244,7 +244,7 @@ fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> { let text = db.file_text(file_id).text(db); - Arc::new(LineIndex::new(&text)) + Arc::new(LineIndex::new(text)) } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -273,7 +273,6 @@ Struct, ToolModule, Trait, - TraitAlias, TypeAlias, TypeParam, Union, @@ -306,7 +305,6 @@ hir::ModuleDef::Adt(hir::Adt::Enum(..)) => SymbolKind::Enum, hir::ModuleDef::Adt(hir::Adt::Union(..)) => SymbolKind::Union, hir::ModuleDef::Trait(..) => SymbolKind::Trait, - hir::ModuleDef::TraitAlias(..) => SymbolKind::TraitAlias, hir::ModuleDef::TypeAlias(..) => SymbolKind::TypeAlias, hir::ModuleDef::BuiltinType(..) => SymbolKind::TypeAlias, }
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index 0ab880b..a76a055 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs
@@ -11,16 +11,17 @@ use span::Edition; use syntax::{ NodeOrToken, SyntaxNode, - ast::{self, AstNode, HasGenericArgs, make}, - ted, + ast::{self, AstNode, HasGenericArgs, HasName, make}, + syntax_editor::{self, SyntaxEditor}, }; -#[derive(Default)] +#[derive(Default, Debug)] struct AstSubsts { types_and_consts: Vec<TypeOrConst>, lifetimes: Vec<ast::LifetimeArg>, } +#[derive(Debug)] enum TypeOrConst { Either(ast::TypeArg), // indistinguishable type or const param Const(ast::ConstArg), @@ -128,15 +129,18 @@ } } - pub fn apply(&self, syntax: &SyntaxNode) { + #[must_use] + pub fn apply(&self, syntax: &SyntaxNode) -> SyntaxNode { self.build_ctx().apply(syntax) } - pub fn apply_all<'b>(&self, nodes: impl IntoIterator<Item = &'b SyntaxNode>) { + #[must_use] + pub fn apply_all<'b>( + &self, + nodes: impl IntoIterator<Item = &'b SyntaxNode>, + ) -> Vec<SyntaxNode> { let ctx = self.build_ctx(); - for node in nodes { - ctx.apply(node); - } + nodes.into_iter().map(|node| ctx.apply(&node.clone())).collect() } fn prettify_target_node(&self, node: SyntaxNode) -> SyntaxNode { @@ -189,13 +193,12 @@ } } (Either::Right(k), None) => { - if let Some(default) = k.default(db) { - if let Some(default) = + if let Some(default) = k.default(db) + && let Some(default) = &default.display_source_code(db, source_module.into(), false).ok() - { - type_substs.insert(k, make::ty(default).clone_for_update()); - defaulted_params.push(Either::Left(k)); - } + { + type_substs.insert(k, make::ty(default).clone_for_update()); + defaulted_params.push(Either::Left(k)); } } (Either::Left(k), Some(TypeOrConst::Either(v))) => { @@ -217,11 +220,10 @@ (Either::Left(k), None) => { if let Some(default) = k.default(db, target_module.krate().to_display_target(db)) + && let Some(default) = default.expr() { - if let Some(default) = default.expr() { - const_substs.insert(k, default.syntax().clone_for_update()); - defaulted_params.push(Either::Right(k)); - } + const_substs.insert(k, default.syntax().clone_for_update()); + defaulted_params.push(Either::Right(k)); } } _ => (), // ignore mismatching params @@ -236,7 +238,7 @@ Some((k.name(db).display(db, target_edition).to_string(), v.lifetime()?)) }) .collect(); - let ctx = Ctx { + let mut ctx = Ctx { type_substs, const_substs, lifetime_substs, @@ -272,42 +274,92 @@ } impl Ctx<'_> { - fn apply(&self, item: &SyntaxNode) { + fn apply(&self, item: &SyntaxNode) -> SyntaxNode { // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = preorder_rev(item).filter_map(ast::Path::cast).collect::<Vec<_>>(); - for path in paths { - self.transform_path(path); - } - - preorder_rev(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { + let item = self.transform_path(item).clone_subtree(); + let mut editor = SyntaxEditor::new(item.clone()); + preorder_rev(&item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) { - ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax()); + editor + .replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax()); } }); + + editor.finish().new_root().clone() } - fn transform_default_values(&self, defaulted_params: Vec<DefaultedParam>) { + fn transform_default_values(&mut self, defaulted_params: Vec<DefaultedParam>) { // By now the default values are simply copied from where they are declared // and should be transformed. As any value is allowed to refer to previous // generic (both type and const) parameters, they should be all iterated left-to-right. for param in defaulted_params { - let value = match param { - Either::Left(k) => self.type_substs.get(&k).unwrap().syntax(), - Either::Right(k) => self.const_substs.get(&k).unwrap(), + let value = match ¶m { + Either::Left(k) => self.type_substs.get(k).unwrap().syntax(), + Either::Right(k) => self.const_substs.get(k).unwrap(), }; // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = preorder_rev(value).filter_map(ast::Path::cast).collect::<Vec<_>>(); - for path in paths { - self.transform_path(path); + let new_value = self.transform_path(value); + match param { + Either::Left(k) => { + self.type_substs.insert(k, ast::Type::cast(new_value.clone()).unwrap()); + } + Either::Right(k) => { + self.const_substs.insert(k, new_value.clone()); + } } } } - fn transform_path(&self, path: ast::Path) -> Option<()> { + fn transform_path(&self, path: &SyntaxNode) -> SyntaxNode { + fn find_child_paths_and_ident_pats( + root_path: &SyntaxNode, + ) -> Vec<Either<ast::Path, ast::IdentPat>> { + let mut result: Vec<Either<ast::Path, ast::IdentPat>> = Vec::new(); + for child in root_path.children() { + if let Some(child_path) = ast::Path::cast(child.clone()) { + result.push(either::Left(child_path)); + } else if let Some(child_ident_pat) = ast::IdentPat::cast(child.clone()) { + result.push(either::Right(child_ident_pat)); + } else { + result.extend(find_child_paths_and_ident_pats(&child)); + } + } + result + } + + let root_path = path.clone_subtree(); + + let result = find_child_paths_and_ident_pats(&root_path); + let mut editor = SyntaxEditor::new(root_path.clone()); + for sub_path in result { + let new = self.transform_path(sub_path.syntax()); + editor.replace(sub_path.syntax(), new); + } + + let update_sub_item = editor.finish().new_root().clone().clone_subtree(); + let item = find_child_paths_and_ident_pats(&update_sub_item); + let mut editor = SyntaxEditor::new(update_sub_item); + for sub_path in item { + self.transform_path_or_ident_pat(&mut editor, &sub_path); + } + editor.finish().new_root().clone() + } + fn transform_path_or_ident_pat( + &self, + editor: &mut SyntaxEditor, + item: &Either<ast::Path, ast::IdentPat>, + ) -> Option<()> { + match item { + Either::Left(path) => self.transform_path_(editor, path), + Either::Right(ident_pat) => self.transform_ident_pat(editor, ident_pat), + } + } + + fn transform_path_(&self, editor: &mut SyntaxEditor, path: &ast::Path) -> Option<()> { if path.qualifier().is_some() { return None; } @@ -319,8 +371,7 @@ // don't try to qualify sole `self` either, they are usually locals, but are returned as modules due to namespace clashing return None; } - - let resolution = self.source_scope.speculative_resolve(&path)?; + let resolution = self.source_scope.speculative_resolve(path)?; match resolution { hir::PathResolution::TypeParam(tp) => { @@ -360,12 +411,12 @@ let segment = make::path_segment_ty(subst.clone(), trait_ref); let qualified = make::path_from_segments(std::iter::once(segment), false); - ted::replace(path.syntax(), qualified.clone_for_update().syntax()); + editor.replace(path.syntax(), qualified.clone_for_update().syntax()); } else if let Some(path_ty) = ast::PathType::cast(parent) { let old = path_ty.syntax(); if old.parent().is_some() { - ted::replace(old, subst.clone_subtree().clone_for_update().syntax()); + editor.replace(old, subst.clone_subtree().clone_for_update().syntax()); } else { // Some `path_ty` has no parent, especially ones made for default value // of type parameters. @@ -377,13 +428,13 @@ } let start = path_ty.syntax().first_child().map(NodeOrToken::Node)?; let end = path_ty.syntax().last_child().map(NodeOrToken::Node)?; - ted::replace_all( + editor.replace_all( start..=end, new.syntax().children().map(NodeOrToken::Node).collect::<Vec<_>>(), ); } } else { - ted::replace( + editor.replace( path.syntax(), subst.clone_subtree().clone_for_update().syntax(), ); @@ -391,14 +442,14 @@ } } hir::PathResolution::Def(def) if def.as_assoc_item(self.source_scope.db).is_none() => { - if let hir::ModuleDef::Trait(_) = def { - if matches!(path.segment()?.kind()?, ast::PathSegmentKind::Type { .. }) { - // `speculative_resolve` resolves segments like `<T as - // Trait>` into `Trait`, but just the trait name should - // not be used as the replacement of the original - // segment. - return None; - } + if let hir::ModuleDef::Trait(_) = def + && matches!(path.segment()?.kind()?, ast::PathSegmentKind::Type { .. }) + { + // `speculative_resolve` resolves segments like `<T as + // Trait>` into `Trait`, but just the trait name should + // not be used as the replacement of the original + // segment. + return None; } let cfg = ImportPathConfig { @@ -409,17 +460,26 @@ }; 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).clone_for_update(); - if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) { - if let Some(segment) = res.segment() { - let old = segment.get_or_create_generic_arg_list(); - ted::replace(old.syntax(), args.clone_subtree().syntax().clone_for_update()) + let mut res_editor = SyntaxEditor::new(res.syntax().clone_subtree()); + if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) + && let Some(segment) = res.segment() + { + if let Some(old) = segment.generic_arg_list() { + res_editor + .replace(old.syntax(), args.clone_subtree().syntax().clone_for_update()) + } else { + res_editor.insert( + syntax_editor::Position::last_child_of(segment.syntax()), + args.clone_subtree().syntax().clone_for_update(), + ); } } - ted::replace(path.syntax(), res.syntax()) + let res = res_editor.finish().new_root().clone(); + editor.replace(path.syntax().clone(), res); } hir::PathResolution::ConstParam(cp) => { if let Some(subst) = self.const_substs.get(&cp) { - ted::replace(path.syntax(), subst.clone_subtree().clone_for_update()); + editor.replace(path.syntax(), subst.clone_subtree().clone_for_update()); } } hir::PathResolution::SelfType(imp) => { @@ -438,31 +498,31 @@ .ok()?; let ast_ty = make::ty(ty_str).clone_for_update(); - if let Some(adt) = ty.as_adt() { - if let ast::Type::PathType(path_ty) = &ast_ty { - let cfg = ImportPathConfig { - prefer_no_std: false, - prefer_prelude: true, - prefer_absolute: false, - allow_unstable: true, - }; - let found_path = self.target_module.find_path( - self.source_scope.db, - ModuleDef::from(adt), - cfg, - )?; + if let Some(adt) = ty.as_adt() + && let ast::Type::PathType(path_ty) = &ast_ty + { + let cfg = ImportPathConfig { + prefer_no_std: false, + prefer_prelude: true, + prefer_absolute: false, + allow_unstable: true, + }; + let found_path = self.target_module.find_path( + self.source_scope.db, + ModuleDef::from(adt), + cfg, + )?; - if let Some(qual) = - mod_path_to_ast(&found_path, self.target_edition).qualifier() - { - let res = make::path_concat(qual, path_ty.path()?).clone_for_update(); - ted::replace(path.syntax(), res.syntax()); - return Some(()); - } + if let Some(qual) = + mod_path_to_ast(&found_path, self.target_edition).qualifier() + { + let res = make::path_concat(qual, path_ty.path()?).clone_for_update(); + editor.replace(path.syntax(), res.syntax()); + return Some(()); } } - ted::replace(path.syntax(), ast_ty.syntax()); + editor.replace(path.syntax(), ast_ty.syntax()); } hir::PathResolution::Local(_) | hir::PathResolution::Def(_) @@ -472,6 +532,34 @@ } Some(()) } + + fn transform_ident_pat( + &self, + editor: &mut SyntaxEditor, + ident_pat: &ast::IdentPat, + ) -> Option<()> { + let name = ident_pat.name()?; + + let temp_path = make::path_from_text(&name.text()); + + let resolution = self.source_scope.speculative_resolve(&temp_path)?; + + match resolution { + hir::PathResolution::Def(def) if def.as_assoc_item(self.source_scope.db).is_none() => { + let cfg = ImportPathConfig { + prefer_no_std: false, + prefer_prelude: true, + prefer_absolute: false, + 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).clone_for_update(); + editor.replace(ident_pat.syntax(), res.syntax()); + Some(()) + } + _ => None, + } + } } // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 4e737e2..a8800c1 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs
@@ -163,7 +163,6 @@ Definition::Const(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::Static(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::Trait(it) => name_range(it, sema).and_then(syn_ctx_is_root), - Definition::TraitAlias(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::TypeAlias(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::Local(it) => { name_range(it.primary_source(sema.db), sema).and_then(syn_ctx_is_root) @@ -442,17 +441,17 @@ name: &ast::Name, new_name: &dyn Display, ) -> bool { - if ast::RecordPatField::for_field_name(name).is_some() { - if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { - cov_mark::hit!(rename_record_pat_field_name_split); - // Foo { ref mut field } -> Foo { new_name: ref mut field } - // ^ insert `new_name: ` + if ast::RecordPatField::for_field_name(name).is_some() + && let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) + { + cov_mark::hit!(rename_record_pat_field_name_split); + // Foo { ref mut field } -> Foo { new_name: ref mut field } + // ^ insert `new_name: ` - // FIXME: instead of splitting the shorthand, recursively trigger a rename of the - // other name https://github.com/rust-lang/rust-analyzer/issues/6547 - edit.insert(ident_pat.syntax().text_range().start(), format!("{new_name}: ")); - return true; - } + // FIXME: instead of splitting the shorthand, recursively trigger a rename of the + // other name https://github.com/rust-lang/rust-analyzer/issues/6547 + edit.insert(ident_pat.syntax().text_range().start(), format!("{new_name}: ")); + return true; } false
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 9cf0bcf..f1d076e 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs
@@ -295,10 +295,10 @@ } // def is crate root - if let &Definition::Module(module) = self { - if module.is_crate_root() { - return SearchScope::reverse_dependencies(db, module.krate()); - } + if let &Definition::Module(module) = self + && module.is_crate_root() + { + return SearchScope::reverse_dependencies(db, module.krate()); } let module = match self.module(db) { @@ -352,7 +352,6 @@ hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()), - hir::GenericDef::TraitAlias(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()), @@ -487,9 +486,9 @@ scope.entries.iter().map(|(&file_id, &search_range)| { let text = db.file_text(file_id.file_id(db)).text(db); let search_range = - search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text))); + search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text))); - (text, file_id, search_range) + (text.clone(), file_id, search_range) }) } @@ -683,51 +682,47 @@ } } else if let Some(alias) = usage.ancestors().find_map(ast::TypeAlias::cast) + && let Some(name) = alias.name() + && seen + .insert(InFileWrapper::new(file_id, name.syntax().text_range())) { - if let Some(name) = alias.name() { - if seen.insert(InFileWrapper::new( - file_id, - name.syntax().text_range(), - )) { - if let Some(def) = is_alias(&alias) { - cov_mark::hit!(container_type_alias); - insert_type_alias( - sema.db, - &mut to_process, - name.text().as_str(), - def.into(), - ); - } else { - cov_mark::hit!(same_name_different_def_type_alias); - } - } + if let Some(def) = is_alias(&alias) { + cov_mark::hit!(container_type_alias); + insert_type_alias( + sema.db, + &mut to_process, + name.text().as_str(), + def.into(), + ); + } else { + cov_mark::hit!(same_name_different_def_type_alias); } } // We need to account for `Self`. It can only refer to our type inside an impl. let impl_ = 'impl_: { for ancestor in usage.ancestors() { - if let Some(parent) = ancestor.parent() { - if let Some(parent) = ast::Impl::cast(parent) { - // Only if the GENERIC_PARAM_LIST is directly under impl, otherwise it may be in the self ty. - if matches!( - ancestor.kind(), - SyntaxKind::ASSOC_ITEM_LIST - | SyntaxKind::WHERE_CLAUSE - | SyntaxKind::GENERIC_PARAM_LIST - ) { - break; - } - if parent - .trait_() - .is_some_and(|trait_| *trait_.syntax() == ancestor) - { - break; - } - - // Otherwise, found an impl where its self ty may be our type. - break 'impl_ Some(parent); + if let Some(parent) = ancestor.parent() + && let Some(parent) = ast::Impl::cast(parent) + { + // Only if the GENERIC_PARAM_LIST is directly under impl, otherwise it may be in the self ty. + if matches!( + ancestor.kind(), + SyntaxKind::ASSOC_ITEM_LIST + | SyntaxKind::WHERE_CLAUSE + | SyntaxKind::GENERIC_PARAM_LIST + ) { + break; } + if parent + .trait_() + .is_some_and(|trait_| *trait_.syntax() == ancestor) + { + break; + } + + // Otherwise, found an impl where its self ty may be our type. + break 'impl_ Some(parent); } } None @@ -858,14 +853,7 @@ &finder, name, is_possibly_self.into_iter().map(|position| { - ( - self.sema - .db - .file_text(position.file_id.file_id(self.sema.db)) - .text(self.sema.db), - position.file_id, - position.range, - ) + (position.file_text(self.sema.db).clone(), position.file_id, position.range) }), |path, name_position| { let has_self = path @@ -1071,12 +1059,12 @@ let file_text = sema.db.file_text(file_id.file_id(self.sema.db)); let text = file_text.text(sema.db); let search_range = - search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text))); + search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text))); let tree = LazyCell::new(|| sema.parse(file_id).syntax().clone()); let finder = &Finder::new("self"); - for offset in Self::match_indices(&text, finder, search_range) { + for offset in Self::match_indices(text, finder, search_range) { for name_ref in Self::find_nodes(sema, "self", file_id, &tree, offset) .filter_map(ast::NameRef::cast) { @@ -1356,11 +1344,10 @@ if matches!(expr.op_kind()?, ast::BinaryOp::Assignment { .. }) { // If the variable or field ends on the LHS's end then it's a Write // (covers fields and locals). FIXME: This is not terribly accurate. - if let Some(lhs) = expr.lhs() { - if lhs.syntax().text_range().end() == r.syntax().text_range().end() { + if let Some(lhs) = expr.lhs() + && lhs.syntax().text_range().end() == r.syntax().text_range().end() { return Some(ReferenceCategory::WRITE) } - } } Some(ReferenceCategory::READ) },
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index c15cade..78ade30 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs
@@ -252,10 +252,10 @@ let mut last_batch_start = 0; for idx in 0..symbols.len() { - if let Some(next_symbol) = symbols.get(idx + 1) { - if cmp(&symbols[last_batch_start], next_symbol) == Ordering::Equal { - continue; - } + if let Some(next_symbol) = symbols.get(idx + 1) + && cmp(&symbols[last_batch_start], next_symbol) == Ordering::Equal + { + continue; } let start = last_batch_start; @@ -357,7 +357,6 @@ hir::ModuleDef::Adt(..) | hir::ModuleDef::TypeAlias(..) | hir::ModuleDef::BuiltinType(..) - | hir::ModuleDef::TraitAlias(..) | hir::ModuleDef::Trait(..) ); if non_type_for_type_only_query || !self.matches_assoc_mode(symbol.is_assoc) { @@ -371,10 +370,10 @@ if self.exclude_imports && symbol.is_import { continue; } - if self.mode.check(&self.query, self.case_sensitive, symbol_name) { - if let Some(b) = cb(symbol).break_value() { - return Some(b); - } + if self.mode.check(&self.query, self.case_sensitive, symbol_name) + && let Some(b) = cb(symbol).break_value() + { + return Some(b); } } }
diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index 7e8c921..1d4d8de 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs
@@ -230,11 +230,11 @@ skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); } continue; - } else if let '}' = first_char { - if let Some((_, '}')) = chars.peek() { - // Escaped format specifier, `}}` - read_escaped_format_specifier(&mut chars, &mut callback); - } + } else if let '}' = first_char + && let Some((_, '}')) = chars.peek() + { + // Escaped format specifier, `}}` + read_escaped_format_specifier(&mut chars, &mut callback); } }
diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index bdff64d..cefd8fd 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -79,14 +79,13 @@ continue; } }; - if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast) { - if let_stmt.initializer().map(|it| it.syntax() != &node).unwrap_or(true) - && let_stmt.let_else().map(|it| it.syntax() != &node).unwrap_or(true) - { - // skipping potential const pat expressions in let statements - preorder.skip_subtree(); - continue; - } + if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast) + && let_stmt.initializer().map(|it| it.syntax() != &node).unwrap_or(true) + && let_stmt.let_else().map(|it| it.syntax() != &node).unwrap_or(true) + { + // skipping potential const pat expressions in let statements + preorder.skip_subtree(); + continue; } match ast::Stmt::cast(node.clone()) { @@ -306,10 +305,10 @@ Some(ast::BlockModifier::AsyncGen(_)) => (), None => (), } - if let Some(stmt_list) = b.stmt_list() { - if let Some(e) = stmt_list.tail_expr() { - for_each_tail_expr(&e, cb); - } + if let Some(stmt_list) = b.stmt_list() + && let Some(e) = stmt_list.tail_expr() + { + for_each_tail_expr(&e, cb); } } ast::Expr::IfExpr(if_) => {
diff --git a/crates/ide-db/src/use_trivial_constructor.rs b/crates/ide-db/src/use_trivial_constructor.rs index f63cd92..a91d436a 100644 --- a/crates/ide-db/src/use_trivial_constructor.rs +++ b/crates/ide-db/src/use_trivial_constructor.rs
@@ -16,17 +16,17 @@ ) -> Option<Expr> { match ty.as_adt() { Some(hir::Adt::Enum(x)) => { - if let &[variant] = &*x.variants(db) { - if 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(), - )), - ); + 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)); - } + return Some(make::expr_path(path)); } } Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => {
diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index bf7ddda..742d614 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
@@ -148,37 +148,27 @@ allow_unstable: true, }; - if !scope_has("Serialize") { - if let Some(PathResolution::Def(it)) = serialize_resolved { - if let Some(it) = current_module.find_use_path( - sema.db, - it, - config.insert_use.prefix_kind, - cfg, - ) { - insert_use( - &scope, - mod_path_to_ast(&it, edition), - &config.insert_use, - ); - } - } + if !scope_has("Serialize") + && let Some(PathResolution::Def(it)) = serialize_resolved + && let Some(it) = current_module.find_use_path( + sema.db, + it, + config.insert_use.prefix_kind, + cfg, + ) + { + insert_use(&scope, mod_path_to_ast(&it, edition), &config.insert_use); } - if !scope_has("Deserialize") { - if let Some(PathResolution::Def(it)) = deserialize_resolved { - if let Some(it) = current_module.find_use_path( - sema.db, - it, - config.insert_use.prefix_kind, - cfg, - ) { - insert_use( - &scope, - mod_path_to_ast(&it, edition), - &config.insert_use, - ); - } - } + if !scope_has("Deserialize") + && let Some(PathResolution::Def(it)) = deserialize_resolved + && let Some(it) = current_module.find_use_path( + sema.db, + it, + config.insert_use.prefix_kind, + cfg, + ) + { + insert_use(&scope, mod_path_to_ast(&it, edition), &config.insert_use); } let mut sc = scb.finish(); sc.insert_source_edit(vfs_file_id, edit.finish());
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index 7da799e..893bfca 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -227,12 +227,11 @@ // Look for a ::new() associated function let has_new_func = ty .iterate_assoc_items(ctx.sema.db, krate, |assoc_item| { - if let AssocItem::Function(func) = assoc_item { - if func.name(ctx.sema.db) == sym::new - && func.assoc_fn_params(ctx.sema.db).is_empty() - { - return Some(()); - } + if let AssocItem::Function(func) = assoc_item + && func.name(ctx.sema.db) == sym::new + && func.assoc_fn_params(ctx.sema.db).is_empty() + { + return Some(()); } None
diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 1e80d02..6331090 100644 --- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -157,7 +157,7 @@ fn no_false_positive_dyn_fn() { check_diagnostics( r#" -//- minicore: copy, fn +//- minicore: copy, fn, dispatch_from_dyn fn f(x: &mut &mut dyn Fn()) { x(); } @@ -166,7 +166,7 @@ field: &'a mut dyn Fn(), } -fn f(x: &mut X<'_>) { +fn g(x: &mut X<'_>) { (x.field)(); } "#,
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 8831efa..6e30bf9 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -12,14 +12,14 @@ 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; - if let Some(parent) = node.parent() { - if ast::BinExpr::can_cast(parent.kind()) { - // In case of an assignment, the diagnostic is provided on the variable name. - // We want to expand it to include the whole assignment, but only when this - // is an ordinary assignment, not a destructuring assignment. So, the direct - // parent is an assignment expression. - span = d.span.with_value(SyntaxNodePtr::new(&parent)); - } + if let Some(parent) = node.parent() + && ast::BinExpr::can_cast(parent.kind()) + { + // In case of an assignment, the diagnostic is provided on the variable name. + // We want to expand it to include the whole assignment, but only when this + // is an ordinary assignment, not a destructuring assignment. So, the direct + // parent is an assignment expression. + span = d.span.with_value(SyntaxNodePtr::new(&parent)); }; let fixes = (|| { @@ -73,10 +73,10 @@ let ast = source.syntax(); let Some(mut_token) = token(ast, T![mut]) else { continue }; edit_builder.delete(mut_token.text_range()); - if let Some(token) = mut_token.next_token() { - if token.kind() == SyntaxKind::WHITESPACE { - edit_builder.delete(token.text_range()); - } + if let Some(token) = mut_token.next_token() + && token.kind() == SyntaxKind::WHITESPACE + { + edit_builder.delete(token.text_range()); } } let edit = edit_builder.finish();
diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index d96c658..3a6e480 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -231,13 +231,13 @@ // If there's an existing `mod m;` statement matching the new one, don't emit a fix (it's // probably `#[cfg]`d out). for item in items.clone() { - if let ast::Item::Module(m) = item { - if let Some(name) = m.name() { - if m.item_list().is_none() && name.to_string() == new_mod_name { - cov_mark::hit!(unlinked_file_skip_fix_when_mod_already_exists); - return None; - } - } + if let ast::Item::Module(m) = item + && let Some(name) = m.name() + && m.item_list().is_none() + && name.to_string() == new_mod_name + { + cov_mark::hit!(unlinked_file_skip_fix_when_mod_already_exists); + return None; } }
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 72bd66d..a4eb3d4 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs
@@ -92,7 +92,7 @@ use ide_db::{ EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode}, - base_db::{ReleaseChannel, RootQueryDb as _}, + base_db::{ReleaseChannel, RootQueryDb as _, salsa}, generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup}, imports::insert_use::InsertUseConfig, label::Label, @@ -537,10 +537,12 @@ resolve: &AssistResolveStrategy, file_id: FileId, ) -> Vec<Diagnostic> { - let mut res = syntax_diagnostics(db, config, file_id); - let sema = semantic_diagnostics(db, config, resolve, file_id); - res.extend(sema); - res + salsa::attach(db, || { + let mut res = syntax_diagnostics(db, config, file_id); + let sema = semantic_diagnostics(db, config, resolve, file_id); + res.extend(sema); + res + }) } /// Returns whether to keep this diagnostic (or remove it). @@ -568,10 +570,10 @@ diag.fixes = None; // All Clippy lints report in macros, see https://github.com/rust-lang/rust-clippy/blob/903293b199364/declare_clippy_lint/src/lib.rs#L172. - if let DiagnosticCode::RustcLint(lint) = diag.code { - if !LINTS_TO_REPORT_IN_EXTERNAL_MACROS.contains(lint) { - return false; - } + if let DiagnosticCode::RustcLint(lint) = diag.code + && !LINTS_TO_REPORT_IN_EXTERNAL_MACROS.contains(lint) + { + return false; }; } true @@ -760,35 +762,35 @@ } while let Some(value) = iter.next() { - if let Some(token) = value.as_token() { - if token.kind() == SyntaxKind::IDENT { - let severity = match token.text() { - "allow" | "expect" => Some(Severity::Allow), - "warn" => Some(Severity::Warning), - "forbid" | "deny" => Some(Severity::Error), - "cfg_attr" => { - if let Some(NodeOrToken::Node(value)) = iter.next() { - cfg_attr_lint_attrs(sema, &value, lint_attrs); - } - None + if let Some(token) = value.as_token() + && token.kind() == SyntaxKind::IDENT + { + let severity = match token.text() { + "allow" | "expect" => Some(Severity::Allow), + "warn" => Some(Severity::Warning), + "forbid" | "deny" => Some(Severity::Error), + "cfg_attr" => { + if let Some(NodeOrToken::Node(value)) = iter.next() { + cfg_attr_lint_attrs(sema, &value, lint_attrs); } - _ => None, - }; - if let Some(severity) = severity { - let lints = iter.next(); - if let Some(NodeOrToken::Node(lints)) = lints { - lint_attrs.push((severity, lints)); - } + None + } + _ => None, + }; + if let Some(severity) = severity { + let lints = iter.next(); + if let Some(NodeOrToken::Node(lints)) = lints { + lint_attrs.push((severity, lints)); } } } } - if prev_len != lint_attrs.len() { - if let Some(false) | None = sema.check_cfg_attr(value) { - // Discard the attributes when the condition is false. - lint_attrs.truncate(prev_len); - } + if prev_len != lint_attrs.len() + && let Some(false) | None = sema.check_cfg_attr(value) + { + // Discard the attributes when the condition is false. + lint_attrs.truncate(prev_len); } }
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index 4e4bd47..c3cc5a0 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs
@@ -2,6 +2,7 @@ mod overly_long_real_world_cases; +use hir::setup_tracing; use ide_db::{ LineIndexDatabase, RootDatabase, assists::{AssistResolveStrategy, ExprFillDefaultMode}, @@ -198,6 +199,8 @@ config: DiagnosticsConfig, #[rust_analyzer::rust_fixture] ra_fixture: &str, ) { + let _tracing = setup_tracing(); + let (db, files) = RootDatabase::with_many_files(ra_fixture); let mut annotations = files .iter() @@ -229,7 +232,7 @@ let line_index = db.line_index(file_id); let mut actual = annotations.remove(&file_id).unwrap_or_default(); - let mut expected = extract_annotations(&db.file_text(file_id).text(&db)); + let mut expected = extract_annotations(db.file_text(file_id).text(&db)); expected.sort_by_key(|(range, s)| (range.start(), s.clone())); actual.sort_by_key(|(range, s)| (range.start(), s.clone())); // FIXME: We should panic on duplicates instead, but includes currently cause us to report
diff --git a/crates/ide-ssr/src/lib.rs b/crates/ide-ssr/src/lib.rs index e4b20f3..43ad12c1 100644 --- a/crates/ide-ssr/src/lib.rs +++ b/crates/ide-ssr/src/lib.rs
@@ -186,7 +186,7 @@ replacing::matches_to_edit( self.sema.db, &matches, - &self.sema.db.file_text(file_id).text(self.sema.db), + self.sema.db.file_text(file_id).text(self.sema.db), &self.rules, ), ) @@ -228,7 +228,7 @@ let file = self.sema.parse(file_id); let mut res = Vec::new(); let file_text = self.sema.db.file_text(file_id.file_id(self.sema.db)).text(self.sema.db); - let mut remaining_text = &*file_text; + let mut remaining_text = &**file_text; let mut base = 0; let len = snippet.len() as u32; while let Some(offset) = remaining_text.find(snippet) { @@ -283,17 +283,16 @@ node: node.clone(), }); } - } else if let Some(macro_call) = ast::MacroCall::cast(node.clone()) { - if let Some(expanded) = self.sema.expand_macro_call(¯o_call) { - if let Some(tt) = macro_call.token_tree() { - self.output_debug_for_nodes_at_range( - &expanded.value, - range, - &Some(self.sema.original_range(tt.syntax())), - out, - ); - } - } + } else if let Some(macro_call) = ast::MacroCall::cast(node.clone()) + && let Some(expanded) = self.sema.expand_macro_call(¯o_call) + && let Some(tt) = macro_call.token_tree() + { + self.output_debug_for_nodes_at_range( + &expanded.value, + range, + &Some(self.sema.original_range(tt.syntax())), + out, + ); } self.output_debug_for_nodes_at_range(&node, range, restrict_range, out); }
diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs index b350315..f21132c 100644 --- a/crates/ide-ssr/src/matching.rs +++ b/crates/ide-ssr/src/matching.rs
@@ -156,12 +156,11 @@ /// processing a macro expansion and we want to fail the match if we're working with a node that /// didn't originate from the token tree of the macro call. fn validate_range(&self, range: &FileRange) -> Result<(), MatchFailed> { - if let Some(restrict_range) = &self.restrict_range { - if restrict_range.file_id != range.file_id - || !restrict_range.range.contains_range(range.range) - { - fail_match!("Node originated from a macro"); - } + if let Some(restrict_range) = &self.restrict_range + && (restrict_range.file_id != range.file_id + || !restrict_range.range.contains_range(range.range)) + { + fail_match!("Node originated from a macro"); } Ok(()) } @@ -404,30 +403,27 @@ // Build a map keyed by field name. let mut fields_by_name: FxHashMap<SmolStr, SyntaxNode> = FxHashMap::default(); for child in code.children() { - if let Some(record) = ast::RecordExprField::cast(child.clone()) { - if let Some(name) = record.field_name() { - fields_by_name.insert(name.text().into(), child.clone()); - } + if let Some(record) = ast::RecordExprField::cast(child.clone()) + && let Some(name) = record.field_name() + { + fields_by_name.insert(name.text().into(), child.clone()); } } for p in pattern.children_with_tokens() { - if let SyntaxElement::Node(p) = p { - if let Some(name_element) = p.first_child_or_token() { - if self.get_placeholder(&name_element).is_some() { - // If the pattern is using placeholders for field names then order - // independence doesn't make sense. Fall back to regular ordered - // matching. - return self.attempt_match_node_children(phase, pattern, code); - } - if let Some(ident) = only_ident(name_element) { - let code_record = fields_by_name.remove(ident.text()).ok_or_else(|| { - match_error!( - "Placeholder has record field '{}', but code doesn't", - ident - ) - })?; - self.attempt_match_node(phase, &p, &code_record)?; - } + if let SyntaxElement::Node(p) = p + && let Some(name_element) = p.first_child_or_token() + { + if self.get_placeholder(&name_element).is_some() { + // If the pattern is using placeholders for field names then order + // independence doesn't make sense. Fall back to regular ordered + // matching. + return self.attempt_match_node_children(phase, pattern, code); + } + if let Some(ident) = only_ident(name_element) { + let code_record = fields_by_name.remove(ident.text()).ok_or_else(|| { + match_error!("Placeholder has record field '{}', but code doesn't", ident) + })?; + self.attempt_match_node(phase, &p, &code_record)?; } } } @@ -476,14 +472,13 @@ } } SyntaxElement::Node(n) => { - if let Some(first_token) = n.first_token() { - if Some(first_token.text()) == next_pattern_token.as_deref() { - if let Some(SyntaxElement::Node(p)) = pattern.next() { - // We have a subtree that starts with the next token in our pattern. - self.attempt_match_token_tree(phase, &p, n)?; - break; - } - } + if let Some(first_token) = n.first_token() + && Some(first_token.text()) == next_pattern_token.as_deref() + && let Some(SyntaxElement::Node(p)) = pattern.next() + { + // We have a subtree that starts with the next token in our pattern. + self.attempt_match_token_tree(phase, &p, n)?; + break; } } }; @@ -562,23 +557,22 @@ let deref_count = self.check_expr_type(pattern_type, expr)?; let pattern_receiver = pattern_args.next(); self.attempt_match_opt(phase, pattern_receiver.clone(), code.receiver())?; - if let Phase::Second(match_out) = phase { - if let Some(placeholder_value) = pattern_receiver + if let Phase::Second(match_out) = phase + && let Some(placeholder_value) = pattern_receiver .and_then(|n| self.get_placeholder_for_node(n.syntax())) .and_then(|placeholder| { match_out.placeholder_values.get_mut(&placeholder.ident) }) - { - placeholder_value.autoderef_count = deref_count; - placeholder_value.autoref_kind = self - .sema - .resolve_method_call_as_callable(code) - .and_then(|callable| { - let (self_param, _) = callable.receiver_param(self.sema.db)?; - Some(self.sema.source(self_param)?.value.kind()) - }) - .unwrap_or(ast::SelfParamKind::Owned); - } + { + placeholder_value.autoderef_count = deref_count; + placeholder_value.autoref_kind = self + .sema + .resolve_method_call_as_callable(code) + .and_then(|callable| { + let (self_param, _) = callable.receiver_param(self.sema.db)?; + Some(self.sema.source(self_param)?.value.kind()) + }) + .unwrap_or(ast::SelfParamKind::Owned); } } } else { @@ -698,12 +692,11 @@ } fn record_ignored_comments(&mut self, token: &SyntaxToken) { - if token.kind() == SyntaxKind::COMMENT { - if let Phase::Second(match_out) = self { - if let Some(comment) = ast::Comment::cast(token.clone()) { - match_out.ignored_comments.push(comment); - } - } + if token.kind() == SyntaxKind::COMMENT + && let Phase::Second(match_out) = self + && let Some(comment) = ast::Comment::cast(token.clone()) + { + match_out.ignored_comments.push(comment); } } }
diff --git a/crates/ide-ssr/src/replacing.rs b/crates/ide-ssr/src/replacing.rs index 752edd6..16287a4 100644 --- a/crates/ide-ssr/src/replacing.rs +++ b/crates/ide-ssr/src/replacing.rs
@@ -112,12 +112,12 @@ self.out.push_str(&mod_path.display(self.db, self.edition).to_string()); // Emit everything except for the segment's name-ref, since we already effectively // emitted that as part of `mod_path`. - if let Some(path) = ast::Path::cast(node.clone()) { - if let Some(segment) = path.segment() { - for node_or_token in segment.syntax().children_with_tokens() { - if node_or_token.kind() != SyntaxKind::NAME_REF { - self.render_node_or_token(&node_or_token); - } + if let Some(path) = ast::Path::cast(node.clone()) + && let Some(segment) = path.segment() + { + for node_or_token in segment.syntax().children_with_tokens() { + if node_or_token.kind() != SyntaxKind::NAME_REF { + self.render_node_or_token(&node_or_token); } } } @@ -242,15 +242,15 @@ } fn parse_as_kind(code: &str, kind: SyntaxKind) -> Option<SyntaxNode> { - if ast::Expr::can_cast(kind) { - if let Ok(expr) = fragments::expr(code) { - return Some(expr); - } + if ast::Expr::can_cast(kind) + && let Ok(expr) = fragments::expr(code) + { + return Some(expr); } - if ast::Item::can_cast(kind) { - if let Ok(item) = fragments::item(code) { - return Some(item); - } + if ast::Item::can_cast(kind) + && let Ok(item) = fragments::item(code) + { + return Some(item); } None }
diff --git a/crates/ide-ssr/src/resolving.rs b/crates/ide-ssr/src/resolving.rs index 8f28a1c..1d5f5ad 100644 --- a/crates/ide-ssr/src/resolving.rs +++ b/crates/ide-ssr/src/resolving.rs
@@ -1,7 +1,7 @@ //! This module is responsible for resolving paths within rules. use hir::AsAssocItem; -use ide_db::FxHashMap; +use ide_db::{FxHashMap, base_db::salsa}; use parsing::Placeholder; use syntax::{ SmolStr, SyntaxKind, SyntaxNode, SyntaxToken, @@ -48,16 +48,20 @@ resolution_scope: &ResolutionScope<'db>, index: usize, ) -> Result<ResolvedRule<'db>, SsrError> { - let resolver = - Resolver { resolution_scope, placeholders_by_stand_in: rule.placeholders_by_stand_in }; - let resolved_template = match rule.template { - Some(template) => Some(resolver.resolve_pattern_tree(template)?), - None => None, - }; - Ok(ResolvedRule { - pattern: resolver.resolve_pattern_tree(rule.pattern)?, - template: resolved_template, - index, + salsa::attach(resolution_scope.scope.db, || { + let resolver = Resolver { + resolution_scope, + placeholders_by_stand_in: rule.placeholders_by_stand_in, + }; + let resolved_template = match rule.template { + Some(template) => Some(resolver.resolve_pattern_tree(template)?), + None => None, + }; + Ok(ResolvedRule { + pattern: resolver.resolve_pattern_tree(rule.pattern)?, + template: resolved_template, + index, + }) }) } @@ -83,21 +87,17 @@ let ufcs_function_calls = resolved_paths .iter() .filter_map(|(path_node, resolved)| { - if let Some(grandparent) = path_node.parent().and_then(|parent| parent.parent()) { - if let Some(call_expr) = ast::CallExpr::cast(grandparent.clone()) { - if let hir::PathResolution::Def(hir::ModuleDef::Function(function)) = - resolved.resolution - { - if function.as_assoc_item(self.resolution_scope.scope.db).is_some() { - let qualifier_type = - self.resolution_scope.qualifier_type(path_node); - return Some(( - grandparent, - UfcsCallInfo { call_expr, function, qualifier_type }, - )); - } - } - } + if let Some(grandparent) = path_node.parent().and_then(|parent| parent.parent()) + && let Some(call_expr) = ast::CallExpr::cast(grandparent.clone()) + && let hir::PathResolution::Def(hir::ModuleDef::Function(function)) = + resolved.resolution + && function.as_assoc_item(self.resolution_scope.scope.db).is_some() + { + let qualifier_type = self.resolution_scope.qualifier_type(path_node); + return Some(( + grandparent, + UfcsCallInfo { call_expr, function, qualifier_type }, + )); } None }) @@ -153,12 +153,11 @@ /// Returns whether `path` contains a placeholder, but ignores any placeholders within type /// arguments. fn path_contains_placeholder(&self, path: &ast::Path) -> bool { - if let Some(segment) = path.segment() { - if let Some(name_ref) = segment.name_ref() { - if self.placeholders_by_stand_in.contains_key(name_ref.text().as_str()) { - return true; - } - } + if let Some(segment) = path.segment() + && let Some(name_ref) = segment.name_ref() + && self.placeholders_by_stand_in.contains_key(name_ref.text().as_str()) + { + return true; } if let Some(qualifier) = path.qualifier() { return self.path_contains_placeholder(&qualifier); @@ -252,14 +251,12 @@ fn qualifier_type(&self, path: &SyntaxNode) -> Option<hir::Type<'db>> { use syntax::ast::AstNode; - if let Some(path) = ast::Path::cast(path.clone()) { - if let Some(qualifier) = path.qualifier() { - if let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) = - self.resolve_path(&qualifier) - { - return Some(adt.ty(self.scope.db)); - } - } + if let Some(path) = ast::Path::cast(path.clone()) + && let Some(qualifier) = path.qualifier() + && let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) = + self.resolve_path(&qualifier) + { + return Some(adt.ty(self.scope.db)); } None } @@ -299,11 +296,11 @@ /// Returns whether `path` or any of its qualifiers contains type arguments. fn path_contains_type_arguments(path: Option<ast::Path>) -> bool { if let Some(path) = path { - if let Some(segment) = path.segment() { - if segment.generic_arg_list().is_some() { - cov_mark::hit!(type_arguments_within_path); - return true; - } + if let Some(segment) = path.segment() + && segment.generic_arg_list().is_some() + { + cov_mark::hit!(type_arguments_within_path); + return true; } return path_contains_type_arguments(path.qualifier()); }
diff --git a/crates/ide-ssr/src/search.rs b/crates/ide-ssr/src/search.rs index 99a98fb..72f857c 100644 --- a/crates/ide-ssr/src/search.rs +++ b/crates/ide-ssr/src/search.rs
@@ -187,16 +187,15 @@ self.try_add_match(rule, code, restrict_range, matches_out); // If we've got a macro call, we already tried matching it pre-expansion, which is the only // way to match the whole macro, now try expanding it and matching the expansion. - if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { - if let Some(expanded) = self.sema.expand_macro_call(¯o_call) { - if let Some(tt) = macro_call.token_tree() { - // When matching within a macro expansion, we only want to allow matches of - // nodes that originated entirely from within the token tree of the macro call. - // i.e. we don't want to match something that came from the macro itself. - if let Some(range) = self.sema.original_range_opt(tt.syntax()) { - self.slow_scan_node(&expanded.value, rule, &Some(range), matches_out); - } - } + if let Some(macro_call) = ast::MacroCall::cast(code.clone()) + && let Some(expanded) = self.sema.expand_macro_call(¯o_call) + && let Some(tt) = macro_call.token_tree() + { + // When matching within a macro expansion, we only want to allow matches of + // nodes that originated entirely from within the token tree of the macro call. + // i.e. we don't want to match something that came from the macro itself. + if let Some(range) = self.sema.original_range_opt(tt.syntax()) { + self.slow_scan_node(&expanded.value, rule, &Some(range), matches_out); } } for child in code.children() { @@ -241,10 +240,10 @@ /// Returns whether we support matching within `node` and all of its ancestors. fn is_search_permitted_ancestors(node: &SyntaxNode) -> bool { - if let Some(parent) = node.parent() { - if !is_search_permitted_ancestors(&parent) { - return false; - } + if let Some(parent) = node.parent() + && !is_search_permitted_ancestors(&parent) + { + return false; } is_search_permitted(node) }
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 05196ac..dec1889 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs
@@ -159,10 +159,10 @@ node.value.syntax().text_range(), Some(name), ); - if res.call_site.0.file_id == source_file_id { - if let Some(name_range) = res.call_site.1 { - return Some((res.call_site.0.range, Some(name_range))); - } + if res.call_site.0.file_id == source_file_id + && let Some(name_range) = res.call_site.1 + { + return Some((res.call_site.0.range, Some(name_range))); } }; // otherwise try upmapping the entire node out of attributes
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index a5d9a10..c197d55 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs
@@ -225,7 +225,6 @@ Definition::Const(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Static(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Trait(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), - Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Macro(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Field(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), @@ -390,7 +389,8 @@ let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot); - if let Some(path) = mod_path_of_def(db, target) { + let append_mod = !matches!(def, Definition::Macro(m) if m.is_macro_export(db)); + if append_mod && let Some(path) = mod_path_of_def(db, target) { web_url = join_url(web_url, &path); local_url = join_url(local_url, &path); } @@ -670,11 +670,9 @@ None => String::from("index.html"), }, Definition::Trait(t) => { + // FIXME(trait-alias): url should be traitalias. for aliases format!("trait.{}.html", t.name(db).as_str()) } - Definition::TraitAlias(t) => { - format!("traitalias.{}.html", t.name(db).as_str()) - } Definition::TypeAlias(t) => { format!("type.{}.html", t.name(db).as_str()) }
diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs index 6af156f..b004c07 100644 --- a/crates/ide/src/doc_links/tests.rs +++ b/crates/ide/src/doc_links/tests.rs
@@ -4,6 +4,7 @@ use hir::Semantics; use ide_db::{ FilePosition, FileRange, RootDatabase, + base_db::salsa, defs::Definition, documentation::{DocsRangeMap, Documentation, HasDocs}, }; @@ -63,8 +64,10 @@ .flat_map(|(text_range, link, ns)| { let attr = range.map(text_range); let is_inner_attr = attr.map(|(_file, attr)| attr.is_inner_attr()).unwrap_or(false); - let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) - .unwrap_or_else(|| panic!("Failed to resolve {link}")); + let def = salsa::attach(sema.db, || { + resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) + .unwrap_or_else(|| panic!("Failed to resolve {link}")) + }); def.try_to_nav(sema.db).unwrap().into_iter().zip(iter::repeat(link)) }) .map(|(nav_target, link)| { @@ -415,6 +418,30 @@ } #[test] +fn external_docs_macro_export() { + check_external_docs( + r#" +//- /lib.rs crate:foo +pub mod inner { + #[macro_export] + macro_rules! my_macro { + () => {}; + } +} + +//- /main.rs crate:bar deps:foo +fn main() { + foo::my_m$0acro!(); +} + "#, + Some("/home/user/project"), + Some(expect![[r#"https://docs.rs/foo/*/foo/macro.my_macro.html"#]]), + Some(expect![[r#"file:///home/user/project/doc/foo/macro.my_macro.html"#]]), + Some("/sysroot"), + ); +} + +#[test] fn doc_links_items_simple() { check_doc_links( r#"
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index f31886b..ad84eac 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs
@@ -96,14 +96,14 @@ let (name, expanded, kind) = loop { let node = anc.next()?; - if let Some(item) = ast::Item::cast(node.clone()) { - if let Some(def) = sema.resolve_attr_macro_call(&item) { - break ( - def.name(db).display(db, file_id.edition(db)).to_string(), - expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?, - SyntaxKind::MACRO_ITEMS, - ); - } + if let Some(item) = ast::Item::cast(node.clone()) + && let Some(def) = sema.resolve_attr_macro_call(&item) + { + break ( + def.name(db).display(db, file_id.edition(db)).to_string(), + expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?, + SyntaxKind::MACRO_ITEMS, + ); } if let Some(mac) = ast::MacroCall::cast(node) { let mut name = mac.path()?.segment()?.name_ref()?.to_string();
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index a374f97..2926384 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs
@@ -81,10 +81,10 @@ if token.text_range() != range { return Some(token.text_range()); } - if let Some(comment) = ast::Comment::cast(token.clone()) { - if let Some(range) = extend_comments(comment) { - return Some(range); - } + if let Some(comment) = ast::Comment::cast(token.clone()) + && let Some(range) = extend_comments(comment) + { + return Some(range); } token.parent()? } @@ -92,12 +92,11 @@ }; // if we are in single token_tree, we maybe live in macro or attr - if node.kind() == TOKEN_TREE { - if let Some(macro_call) = node.ancestors().find_map(ast::MacroCall::cast) { - if let Some(range) = extend_tokens_from_range(sema, macro_call, range) { - return Some(range); - } - } + if node.kind() == TOKEN_TREE + && let Some(macro_call) = node.ancestors().find_map(ast::MacroCall::cast) + && let Some(range) = extend_tokens_from_range(sema, macro_call, range) + { + return Some(range); } if node.text_range() != range { @@ -106,10 +105,10 @@ let node = shallowest_node(&node); - if node.parent().is_some_and(|n| list_kinds.contains(&n.kind())) { - if let Some(range) = extend_list_item(&node) { - return Some(range); - } + if node.parent().is_some_and(|n| list_kinds.contains(&n.kind())) + && let Some(range) = extend_list_item(&node) + { + return Some(range); } node.parent().map(|it| it.text_range()) @@ -221,19 +220,20 @@ let prefix = TextRange::new(ws.text_range().start(), offset) - ws.text_range().start(); let ws_suffix = &ws_text[suffix]; let ws_prefix = &ws_text[prefix]; - if ws_text.contains('\n') && !ws_suffix.contains('\n') { - if let Some(node) = ws.next_sibling_or_token() { - let start = match ws_prefix.rfind('\n') { - Some(idx) => ws.text_range().start() + TextSize::from((idx + 1) as u32), - None => node.text_range().start(), - }; - let end = if root.text().char_at(node.text_range().end()) == Some('\n') { - node.text_range().end() + TextSize::of('\n') - } else { - node.text_range().end() - }; - return TextRange::new(start, end); - } + if ws_text.contains('\n') + && !ws_suffix.contains('\n') + && let Some(node) = ws.next_sibling_or_token() + { + let start = match ws_prefix.rfind('\n') { + Some(idx) => ws.text_range().start() + TextSize::from((idx + 1) as u32), + None => node.text_range().start(), + }; + let end = if root.text().char_at(node.text_range().end()) == Some('\n') { + node.text_range().end() + TextSize::of('\n') + } else { + node.text_range().end() + }; + return TextRange::new(start, end); } ws.text_range() }
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs index 6820f99..21254fc 100644 --- a/crates/ide/src/file_structure.rs +++ b/crates/ide/src/file_structure.rs
@@ -23,6 +23,11 @@ Region, } +#[derive(Debug, Clone)] +pub struct FileStructureConfig { + pub exclude_locals: bool, +} + // Feature: File Structure // // Provides a tree of the symbols defined in the file. Can be used to @@ -36,21 +41,24 @@ // | VS Code | <kbd>Ctrl+Shift+O</kbd> | // //  -pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> { +pub(crate) fn file_structure( + file: &SourceFile, + config: &FileStructureConfig, +) -> Vec<StructureNode> { let mut res = Vec::new(); let mut stack = Vec::new(); for event in file.syntax().preorder_with_tokens() { match event { WalkEvent::Enter(NodeOrToken::Node(node)) => { - if let Some(mut symbol) = structure_node(&node) { + if let Some(mut symbol) = structure_node(&node, config) { symbol.parent = stack.last().copied(); stack.push(res.len()); res.push(symbol); } } WalkEvent::Leave(NodeOrToken::Node(node)) => { - if structure_node(&node).is_some() { + if structure_node(&node, config).is_some() { stack.pop().unwrap(); } } @@ -71,7 +79,7 @@ res } -fn structure_node(node: &SyntaxNode) -> Option<StructureNode> { +fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option<StructureNode> { fn decl<N: HasName + HasAttrs>(node: N, kind: StructureNodeKind) -> Option<StructureNode> { decl_with_detail(&node, None, kind) } @@ -154,7 +162,6 @@ ast::Enum(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)), ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)), ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)), - ast::TraitAlias(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::TraitAlias)), ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)), ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)), ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)), @@ -187,6 +194,10 @@ Some(node) }, ast::LetStmt(it) => { + if config.exclude_locals { + return None; + } + let pat = it.pat()?; let mut label = String::new(); @@ -201,7 +212,6 @@ detail: it.ty().map(|ty| ty.to_string()), deprecated: false, }; - Some(node) }, ast::ExternBlock(it) => { @@ -254,9 +264,19 @@ use super::*; + const DEFAULT_CONFIG: FileStructureConfig = FileStructureConfig { exclude_locals: true }; + fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + check_with_config(ra_fixture, &DEFAULT_CONFIG, expect); + } + + fn check_with_config( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + config: &FileStructureConfig, + expect: Expect, + ) { let file = SourceFile::parse(ra_fixture, span::Edition::CURRENT).ok().unwrap(); - let structure = file_structure(&file); + let structure = file_structure(&file, config); expect.assert_debug_eq(&structure) } @@ -532,7 +552,7 @@ navigation_range: 251..256, node_range: 245..262, kind: SymbolKind( - TraitAlias, + Trait, ), detail: None, deprecated: false, @@ -701,13 +721,264 @@ ), deprecated: false, }, + ] + "#]], + ); + } + + #[test] + fn test_file_structure_include_locals() { + check_with_config( + r#" +struct Foo { + x: i32 +} + +mod m { + fn bar1() {} + fn bar2<T>(t: T) -> T {} + fn bar3<A, + B>(a: A, + b: B) -> Vec< + u32 + > {} +} + +enum E { X, Y(i32) } +type T = (); +static S: i32 = 42; +const C: i32 = 42; +trait Tr {} +trait Alias = Tr; + +macro_rules! mc { + () => {} +} + +fn let_statements() { + let x = 42; + let mut y = x; + let Foo { + .. + } = Foo { x }; + _ = (); + let _ = g(); +} +"#, + &FileStructureConfig { exclude_locals: false }, + expect![[r#" + [ + StructureNode { + parent: None, + label: "Foo", + navigation_range: 8..11, + node_range: 1..26, + kind: SymbolKind( + Struct, + ), + detail: None, + deprecated: false, + }, StructureNode { parent: Some( - 27, + 0, ), label: "x", - navigation_range: 684..685, - node_range: 680..691, + navigation_range: 18..19, + node_range: 18..24, + kind: SymbolKind( + Field, + ), + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "m", + navigation_range: 32..33, + node_range: 28..158, + kind: SymbolKind( + Module, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar1", + navigation_range: 43..47, + node_range: 40..52, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn()", + ), + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar2", + navigation_range: 60..64, + node_range: 57..81, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn<T>(t: T) -> T", + ), + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar3", + navigation_range: 89..93, + node_range: 86..156, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn<A, B>(a: A, b: B) -> Vec< u32 >", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "E", + navigation_range: 165..166, + node_range: 160..180, + kind: SymbolKind( + Enum, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 6, + ), + label: "X", + navigation_range: 169..170, + node_range: 169..170, + kind: SymbolKind( + Variant, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 6, + ), + label: "Y", + navigation_range: 172..173, + node_range: 172..178, + kind: SymbolKind( + Variant, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "T", + navigation_range: 186..187, + node_range: 181..193, + kind: SymbolKind( + TypeAlias, + ), + detail: Some( + "()", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "S", + navigation_range: 201..202, + node_range: 194..213, + kind: SymbolKind( + Static, + ), + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "C", + navigation_range: 220..221, + node_range: 214..232, + kind: SymbolKind( + Const, + ), + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "Tr", + navigation_range: 239..241, + node_range: 233..244, + kind: SymbolKind( + Trait, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "Alias", + navigation_range: 251..256, + node_range: 245..262, + kind: SymbolKind( + Trait, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "mc", + navigation_range: 277..279, + node_range: 264..296, + kind: SymbolKind( + Macro, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "let_statements", + navigation_range: 301..315, + node_range: 298..429, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn()", + ), + deprecated: false, + }, + StructureNode { + parent: Some( + 15, + ), + label: "x", + navigation_range: 328..329, + node_range: 324..335, kind: SymbolKind( Local, ), @@ -716,11 +987,11 @@ }, StructureNode { parent: Some( - 27, + 15, ), label: "mut y", - navigation_range: 700..705, - node_range: 696..710, + navigation_range: 344..349, + node_range: 340..354, kind: SymbolKind( Local, ), @@ -729,11 +1000,11 @@ }, StructureNode { parent: Some( - 27, + 15, ), label: "Foo { .. }", - navigation_range: 719..741, - node_range: 715..754, + navigation_range: 363..385, + node_range: 359..398, kind: SymbolKind( Local, ), @@ -742,11 +1013,11 @@ }, StructureNode { parent: Some( - 27, + 15, ), label: "_", - navigation_range: 804..805, - node_range: 800..812, + navigation_range: 419..420, + node_range: 415..427, kind: SymbolKind( Local, ),
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs old mode 100755 new mode 100644 index 698fd14..3969490 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs
@@ -29,7 +29,6 @@ Consts, Statics, TypeAliases, - TraitAliases, ExternCrates, // endregion: item runs } @@ -61,28 +60,29 @@ }; if is_multiline { // for the func with multiline param list - if matches!(element.kind(), FN) { - if let NodeOrToken::Node(node) = &element { - if let Some(fn_node) = ast::Fn::cast(node.clone()) { - if !fn_node - .param_list() - .map(|param_list| param_list.syntax().text().contains_char('\n')) - .unwrap_or(false) - { - continue; - } + if matches!(element.kind(), FN) + && let NodeOrToken::Node(node) = &element + && let Some(fn_node) = ast::Fn::cast(node.clone()) + { + if !fn_node + .param_list() + .map(|param_list| param_list.syntax().text().contains_char('\n')) + .unwrap_or(false) + { + continue; + } - if fn_node.body().is_some() { - res.push(Fold { - range: TextRange::new( - node.text_range().start(), - node.text_range().end(), - ), - kind: FoldKind::Function, - }); - continue; - } - } + if fn_node.body().is_some() { + // Get the actual start of the function (excluding doc comments) + let fn_start = fn_node + .fn_token() + .map(|token| token.text_range().start()) + .unwrap_or(node.text_range().start()); + res.push(Fold { + range: TextRange::new(fn_start, node.text_range().end()), + kind: FoldKind::Function, + }); + continue; } } res.push(Fold { range: element.text_range(), kind }); @@ -118,14 +118,13 @@ match_ast! { match node { ast::Module(module) => { - if module.item_list().is_none() { - if let Some(range) = contiguous_range_for_item_group( + if module.item_list().is_none() + && let Some(range) = contiguous_range_for_item_group( module, &mut visited_nodes, ) { res.push(Fold { range, kind: FoldKind::Modules }) } - } }, ast::Use(use_) => { if let Some(range) = contiguous_range_for_item_group(use_, &mut visited_nodes) { @@ -147,11 +146,6 @@ res.push(Fold { range, kind: FoldKind::TypeAliases }) } }, - ast::TraitAlias(alias) => { - if let Some(range) = contiguous_range_for_item_group(alias, &mut visited_nodes) { - res.push(Fold { range, kind: FoldKind::TraitAliases }) - } - }, ast::ExternCrate(extern_crate) => { if let Some(range) = contiguous_range_for_item_group(extern_crate, &mut visited_nodes) { res.push(Fold { range, kind: FoldKind::ExternCrates }) @@ -210,11 +204,11 @@ for element in first.syntax().siblings_with_tokens(Direction::Next) { let node = match element { NodeOrToken::Token(token) => { - if let Some(ws) = ast::Whitespace::cast(token) { - if !ws.spans_multiple_lines() { - // Ignore whitespace without blank lines - continue; - } + if let Some(ws) = ast::Whitespace::cast(token) + && !ws.spans_multiple_lines() + { + // Ignore whitespace without blank lines + continue; } // There is a blank line or another token, which means that the // group ends here @@ -268,21 +262,21 @@ for element in first.syntax().siblings_with_tokens(Direction::Next) { match element { NodeOrToken::Token(token) => { - if let Some(ws) = ast::Whitespace::cast(token.clone()) { - if !ws.spans_multiple_lines() { - // Ignore whitespace without blank lines - continue; - } + if let Some(ws) = ast::Whitespace::cast(token.clone()) + && !ws.spans_multiple_lines() + { + // Ignore whitespace without blank lines + continue; } - if let Some(c) = ast::Comment::cast(token) { - if c.kind() == group_kind { - let text = c.text().trim_start(); - // regions are not real comments - if !(text.starts_with(REGION_START) || text.starts_with(REGION_END)) { - visited.insert(c.clone()); - last = c; - continue; - } + if let Some(c) = ast::Comment::cast(token) + && c.kind() == group_kind + { + let text = c.text().trim_start(); + // regions are not real comments + if !(text.starts_with(REGION_START) || text.starts_with(REGION_END)) { + visited.insert(c.clone()); + last = c; + continue; } } // The comment group ends because either: @@ -351,7 +345,6 @@ FoldKind::ReturnType => "returntype", FoldKind::MatchArm => "matcharm", FoldKind::Function => "function", - FoldKind::TraitAliases => "traitaliases", FoldKind::ExternCrates => "externcrates", }; assert_eq!(kind, &attr.unwrap()); @@ -688,4 +681,21 @@ "#, ) } + + #[test] + fn test_fold_doc_comments_with_multiline_paramlist_function() { + check( + r#" +<fold comment>/// A very very very very very very very very very very very very very very very +/// very very very long description</fold> +<fold function>fn foo<fold arglist>( + very_long_parameter_name: u32, + another_very_long_parameter_name: u32, + third_very_long_param: u32, +)</fold> <fold block>{ + todo!() +}</fold></fold> +"#, + ); + } }
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 29fc68b..633feec 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs
@@ -10,7 +10,7 @@ }; use ide_db::{ RootDatabase, SymbolKind, - base_db::{AnchoredPath, SourceDatabase}, + base_db::{AnchoredPath, SourceDatabase, salsa}, defs::{Definition, IdentClass}, famous_defs::FamousDefs, helpers::pick_best_token, @@ -83,33 +83,32 @@ return Some(RangeInfo::new(original_token.text_range(), navs)); } - if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) { - return Some(RangeInfo::new(original_token.text_range(), navs)); - } - let navs = sema .descend_into_macros_no_opaque(original_token.clone(), false) .into_iter() .filter_map(|token| { + if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) { + return Some(navs); + } + let parent = token.value.parent()?; let token_file_id = token.file_id; - if let Some(token) = ast::String::cast(token.value.clone()) { - if let Some(x) = + if let Some(token) = ast::String::cast(token.value.clone()) + && let Some(x) = try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id) - { - return Some(vec![x]); - } + { + return Some(vec![x]); } - if ast::TokenTree::can_cast(parent.kind()) { - if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value) { - return Some(vec![x]); - } + if ast::TokenTree::can_cast(parent.kind()) + && let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value) + { + return Some(vec![x]); } Some( - IdentClass::classify_node(sema, &parent)? + salsa::attach(sema.db, || IdentClass::classify_node(sema, &parent))? .definitions() .into_iter() .flat_map(|(def, _)| { @@ -245,12 +244,11 @@ let krate = extern_crate.resolved_crate(sema.db)?; for mod_def in krate.root_module().declarations(sema.db) { - if let ModuleDef::Macro(mac) = mod_def { - if mac.name(sema.db).as_str() == token.text() { - if let Some(nav) = mac.try_to_nav(sema.db) { - return Some(nav.call_site); - } - } + if let ModuleDef::Macro(mac) = mod_def + && mac.name(sema.db).as_str() == token.text() + && let Some(nav) = mac.try_to_nav(sema.db) + { + return Some(nav.call_site); } } @@ -3286,6 +3284,32 @@ } #[test] + fn into_call_to_from_definition_within_macro() { + check( + r#" +//- proc_macros: identity +//- minicore: from +struct A; + +struct B; + +impl From<A> for B { + fn from(value: A) -> Self { + //^^^^ + B + } +} + +#[proc_macros::identity] +fn f() { + let a = A; + let b: B = a.into$0(); +} + "#, + ); + } + + #[test] fn into_call_to_from_definition_with_trait_bounds() { check( r#"
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 02d96a6..4f17262 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs
@@ -234,6 +234,7 @@ ); } + // FIXME(next-solver): it would be nice to be able to also point to `&Foo` #[test] fn goto_implementation_all_impls() { check( @@ -246,7 +247,6 @@ impl T for Foo {} //^^^ impl T for &Foo {} - //^^^^ "#, ); }
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 356bd69..9960e79 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs
@@ -722,20 +722,19 @@ self.depth += 1; } - if let ast::Expr::MacroExpr(expr) = expr { - if let Some(expanded) = + if let ast::Expr::MacroExpr(expr) = expr + && let Some(expanded) = expr.macro_call().and_then(|call| self.sema.expand_macro_call(&call)) - { - match_ast! { - match (expanded.value) { - ast::MacroStmts(it) => { - self.handle_expanded(it, cb); - }, - ast::Expr(it) => { - self.walk(&it, cb); - }, - _ => {} - } + { + match_ast! { + match (expanded.value) { + ast::MacroStmts(it) => { + self.handle_expanded(it, cb); + }, + ast::Expr(it) => { + self.walk(&it, cb); + }, + _ => {} } } } @@ -755,10 +754,10 @@ } for stmt in expanded.statements() { - if let ast::Stmt::ExprStmt(stmt) = stmt { - if let Some(expr) = stmt.expr() { - self.walk(&expr, cb); - } + if let ast::Stmt::ExprStmt(stmt) = stmt + && let Some(expr) = stmt.expr() + { + self.walk(&expr, cb); } } } @@ -806,12 +805,12 @@ push_to_highlights(unsafe_token_file_id, Some(unsafe_token.text_range())); // highlight unsafe operations - if let Some(block) = block_expr { - if let Some(body) = sema.body_for(InFile::new(unsafe_token_file_id, block.syntax())) { - let unsafe_ops = sema.get_unsafe_ops(body); - for unsafe_op in unsafe_ops { - push_to_highlights(unsafe_op.file_id, Some(unsafe_op.value.text_range())); - } + if let Some(block) = block_expr + && let Some(body) = sema.body_for(InFile::new(unsafe_token_file_id, block.syntax())) + { + let unsafe_ops = sema.get_unsafe_ops(body); + for unsafe_op in unsafe_ops { + push_to_highlights(unsafe_op.file_id, Some(unsafe_op.value.text_range())); } }
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index e4d6279..a48fe43 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs
@@ -12,6 +12,7 @@ }; use ide_db::{ FileRange, FxIndexSet, Ranker, RootDatabase, + base_db::salsa, defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, @@ -244,17 +245,15 @@ let node = token.parent()?; // special case macro calls, we wanna render the invoked arm index - if let Some(name) = ast::NameRef::cast(node.clone()) { - if let Some(path_seg) = + if let Some(name) = ast::NameRef::cast(node.clone()) + && let Some(path_seg) = name.syntax().parent().and_then(ast::PathSegment::cast) - { - if let Some(macro_call) = path_seg + && let Some(macro_call) = path_seg .parent_path() .syntax() .parent() .and_then(ast::MacroCall::cast) - { - if let Some(macro_) = sema.resolve_macro_call(¯o_call) { + && let Some(macro_) = sema.resolve_macro_call(¯o_call) { break 'a vec![( (Definition::Macro(macro_), None), sema.resolve_macro_call_arm(¯o_call), @@ -262,9 +261,6 @@ node, )]; } - } - } - } match IdentClass::classify_node(sema, &node)? { // It's better for us to fall back to the keyword hover here, @@ -295,7 +291,7 @@ .into_iter() .unique_by(|&((def, _), _, _, _)| def) .map(|((def, subst), macro_arm, hovered_definition, node)| { - hover_for_definition( + salsa::attach(sema.db, || hover_for_definition( sema, file_id, def, @@ -306,7 +302,7 @@ config, edition, display_target, - ) + )) }) .collect::<Vec<_>>(), )
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 670210d..e4c7d42 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs
@@ -10,6 +10,7 @@ }; use ide_db::{ RootDatabase, + base_db::salsa, defs::Definition, documentation::{DocsRangeMap, HasDocs}, famous_defs::FamousDefs, @@ -44,7 +45,7 @@ Either::Left(expr) => sema.type_of_expr(expr)?, Either::Right(pat) => sema.type_of_pat(pat)?, }; - type_info(sema, _config, ty_info, edition, display_target) + salsa::attach(sema.db, || type_info(sema, _config, ty_info, edition, display_target)) } pub(super) fn closure_expr( @@ -95,23 +96,25 @@ if let Some((hir::Adt::Enum(inner), hir::Adt::Enum(body))) = adts { let famous_defs = FamousDefs(sema, sema.scope(try_expr.syntax())?.krate()); // special case for two options, there is no value in showing them - if let Some(option_enum) = famous_defs.core_option_Option() { - if inner == option_enum && body == option_enum { - cov_mark::hit!(hover_try_expr_opt_opt); - return None; - } + if let Some(option_enum) = famous_defs.core_option_Option() + && inner == option_enum + && body == option_enum + { + cov_mark::hit!(hover_try_expr_opt_opt); + return None; } // special case two results to show the error variants only - if let Some(result_enum) = famous_defs.core_result_Result() { - if inner == result_enum && body == result_enum { - let error_type_args = - inner_ty.type_arguments().nth(1).zip(body_ty.type_arguments().nth(1)); - if let Some((inner, body)) = error_type_args { - inner_ty = inner; - body_ty = body; - "Try Error".clone_into(&mut s); - } + if let Some(result_enum) = famous_defs.core_result_Result() + && inner == result_enum + && body == result_enum + { + let error_type_args = + inner_ty.type_arguments().nth(1).zip(body_ty.type_arguments().nth(1)); + if let Some((inner, body)) = error_type_args { + inner_ty = inner; + body_ty = body; + "Try Error".clone_into(&mut s); } } } @@ -398,7 +401,6 @@ Definition::GenericParam(generic_param) => match generic_param.parent() { hir::GenericDef::Adt(it) => Some(it.name(db)), hir::GenericDef::Trait(it) => Some(it.name(db)), - hir::GenericDef::TraitAlias(it) => Some(it.name(db)), hir::GenericDef::TypeAlias(it) => Some(it.name(db)), hir::GenericDef::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)), @@ -1132,10 +1134,10 @@ ) -> (Markup, Option<DocsRangeMap>) { let mut buf = String::new(); - if let Some(mod_path) = mod_path { - if !mod_path.is_empty() { - format_to!(buf, "```rust\n{}\n```\n\n", mod_path); - } + if let Some(mod_path) = mod_path + && !mod_path.is_empty() + { + format_to!(buf, "```rust\n{}\n```\n\n", mod_path); } format_to!(buf, "```rust\n{}\n```", rust); @@ -1217,55 +1219,55 @@ format_to!(label, ", "); } - if let Some(render) = config.offset { - if let Some(offset) = offset(&layout) { - format_to!(label, "offset = "); - match render { - MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{offset}"), - MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{offset:#X}"), - MemoryLayoutHoverRenderKind::Both if offset >= 10 => { - format_to!(label, "{offset} ({offset:#X})") - } - MemoryLayoutHoverRenderKind::Both => { - format_to!(label, "{offset}") - } + if let Some(render) = config.offset + && let Some(offset) = offset(&layout) + { + format_to!(label, "offset = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{offset}"), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{offset:#X}"), + MemoryLayoutHoverRenderKind::Both if offset >= 10 => { + format_to!(label, "{offset} ({offset:#X})") } - format_to!(label, ", "); + MemoryLayoutHoverRenderKind::Both => { + format_to!(label, "{offset}") + } } + format_to!(label, ", "); } - if let Some(render) = config.padding { - if let Some((padding_name, padding)) = padding(&layout) { - format_to!(label, "{padding_name} = "); - match render { - MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{padding}"), - MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{padding:#X}"), - MemoryLayoutHoverRenderKind::Both if padding >= 10 => { - format_to!(label, "{padding} ({padding:#X})") - } - MemoryLayoutHoverRenderKind::Both => { - format_to!(label, "{padding}") - } + if let Some(render) = config.padding + && let Some((padding_name, padding)) = padding(&layout) + { + format_to!(label, "{padding_name} = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{padding}"), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{padding:#X}"), + MemoryLayoutHoverRenderKind::Both if padding >= 10 => { + format_to!(label, "{padding} ({padding:#X})") } - format_to!(label, ", "); + MemoryLayoutHoverRenderKind::Both => { + format_to!(label, "{padding}") + } } + format_to!(label, ", "); } - if config.niches { - if let Some(niches) = layout.niches() { - if niches > 1024 { - if niches.is_power_of_two() { - format_to!(label, "niches = 2{}, ", pwr2_to_exponent(niches)); - } else if is_pwr2plus1(niches) { - format_to!(label, "niches = 2{} + 1, ", pwr2_to_exponent(niches - 1)); - } else if is_pwr2minus1(niches) { - format_to!(label, "niches = 2{} - 1, ", pwr2_to_exponent(niches + 1)); - } else { - format_to!(label, "niches = a lot, "); - } + if config.niches + && let Some(niches) = layout.niches() + { + if niches > 1024 { + if niches.is_power_of_two() { + format_to!(label, "niches = 2{}, ", pwr2_to_exponent(niches)); + } else if is_pwr2plus1(niches) { + format_to!(label, "niches = 2{} + 1, ", pwr2_to_exponent(niches - 1)); + } else if is_pwr2minus1(niches) { + format_to!(label, "niches = 2{} - 1, ", pwr2_to_exponent(niches + 1)); } else { - format_to!(label, "niches = {niches}, "); + format_to!(label, "niches = a lot, "); } + } else { + format_to!(label, "niches = {niches}, "); } } label.pop(); // ' '
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index c548021..6f1bbad 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs
@@ -6,6 +6,8 @@ HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, fixture, }; +use hir::setup_tracing; + const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { links_in_hover: false, memory_layout: Some(MemoryLayoutHoverConfig { @@ -38,6 +40,7 @@ #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let _tracing = setup_tracing(); let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover(
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 19e5509..424890f 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs
@@ -8,7 +8,9 @@ ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError, HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym, }; -use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder}; +use ide_db::{ + FileRange, RootDatabase, base_db::salsa, famous_defs::FamousDefs, text_edit::TextEditBuilder, +}; use ide_db::{FxHashSet, text_edit::TextEdit}; use itertools::Itertools; use smallvec::{SmallVec, smallvec}; @@ -105,14 +107,16 @@ } }; let mut preorder = file.preorder(); - while let Some(event) = preorder.next() { - if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none()) - { - preorder.skip_subtree(); - continue; + salsa::attach(sema.db, || { + while let Some(event) = preorder.next() { + if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none()) + { + preorder.skip_subtree(); + continue; + } + hints(event); } - hints(event); - } + }); if let Some(range_limit) = range_limit { acc.retain(|hint| range_limit.contains_range(hint.range)); } @@ -228,9 +232,9 @@ chaining::hints(hints, famous_defs, config, display_target, &expr); adjustment::hints(hints, famous_defs, config, display_target, &expr); match expr { - ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, ast::Expr::from(it)), + ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)), ast::Expr::MethodCallExpr(it) => { - param_name::hints(hints, famous_defs, config, ast::Expr::from(it)) + param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)) } ast::Expr::ClosureExpr(it) => { closure_captures::hints(hints, famous_defs, config, it.clone()); @@ -576,13 +580,13 @@ } pub fn append_part(&mut self, part: InlayHintLabelPart) { - if part.linked_location.is_none() && part.tooltip.is_none() { - if let Some(InlayHintLabelPart { text, linked_location: None, tooltip: None }) = + if part.linked_location.is_none() + && part.tooltip.is_none() + && let Some(InlayHintLabelPart { text, linked_location: None, tooltip: None }) = self.parts.last_mut() - { - text.push_str(&part.text); - return; - } + { + text.push_str(&part.text); + return; } self.parts.push(part); } @@ -1065,4 +1069,34 @@ "#, ); } + + #[test] + fn regression_20239() { + check_with_config( + InlayHintsConfig { parameter_hints: true, type_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: fn +trait Iterator { + type Item; + fn map<B, F: FnMut(Self::Item) -> B>(self, f: F); +} +trait ToString { + fn to_string(&self); +} + +fn check_tostr_eq<L, R>(left: L, right: R) +where + L: Iterator, + L::Item: ToString, + R: Iterator, + R::Item: ToString, +{ + left.map(|s| s.to_string()); + // ^ impl ToString + right.map(|s| s.to_string()); + // ^ impl ToString +} + "#, + ); + } }
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 49b43fc..39554d7 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -39,10 +39,10 @@ if let ast::Expr::ParenExpr(_) = expr { return None; } - if let ast::Expr::BlockExpr(b) = expr { - if !b.is_standalone() { - return None; - } + if let ast::Expr::BlockExpr(b) = expr + && !b.is_standalone() + { + return None; } let descended = sema.descend_node_into_attributes(expr.clone()).pop(); @@ -411,10 +411,9 @@ (()) == {()}; // ^^& // ^^^^& - let closure: dyn Fn = || (); + let closure: &dyn Fn = &|| (); + //^^^^^^<unsize>&* closure(); - //^^^^^^^(& - //^^^^^^^) Struct[0]; //^^^^^^(& //^^^^^^) @@ -507,9 +506,10 @@ (()) == {()}; // ^^.& // ^^^^.& - let closure: dyn Fn = || (); + let closure: &dyn Fn = &|| (); + //^^^^^^( + //^^^^^^).*.&.<unsize> closure(); - //^^^^^^^.& Struct[0]; //^^^^^^.& &mut Struct[0];
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 7293493..7a4af4f 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -41,13 +41,11 @@ Some(it.colon_token()) }, ast::LetStmt(it) => { - if config.hide_closure_initialization_hints { - if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() { - if closure_has_block_body(&closure) { + if config.hide_closure_initialization_hints + && let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() + && closure_has_block_body(&closure) { return None; } - } - } if it.ty().is_some() { return None; } @@ -911,7 +909,7 @@ foo(plus_one); let add_mul = bar(|x: u8| { x + 1 }); - // ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized + // ^^^^^^^ impl FnOnce(u8) -> u8 let closure = if let Some(6) = add_mul(2).checked_sub(1) { // ^^^^^^^ fn(i32) -> i32
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index ff157fa..a8bb652 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -51,12 +51,11 @@ if ty.is_unknown() { return None; } - if matches!(expr, ast::Expr::PathExpr(_)) { - if let Some(hir::Adt::Struct(st)) = ty.as_adt() { - if st.fields(sema.db).is_empty() { - return None; - } - } + if matches!(expr, ast::Expr::PathExpr(_)) + && let Some(hir::Adt::Struct(st)) = ty.as_adt() + && st.fields(sema.db).is_empty() + { + return None; } let label = label_of_ty(famous_defs, config, &ty, display_target)?; acc.push(InlayHint {
diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs index 05253b6..e80c9dc 100644 --- a/crates/ide/src/inlay_hints/closing_brace.rs +++ b/crates/ide/src/inlay_hints/closing_brace.rs
@@ -120,11 +120,11 @@ }; if let Some(mut next) = closing_token.next_token() { - if next.kind() == T![;] { - if let Some(tok) = next.next_token() { - closing_token = next; - next = tok; - } + if next.kind() == T![;] + && let Some(tok) = next.next_token() + { + closing_token = next; + next = tok; } if !(next.kind() == SyntaxKind::WHITESPACE && next.text().contains('\n')) { // Only display the hint if the `}` is the last token on the line
diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs index 9e600b5..fef1cb8 100644 --- a/crates/ide/src/inlay_hints/closure_ret.rs +++ b/crates/ide/src/inlay_hints/closure_ret.rs
@@ -55,11 +55,9 @@ // Insert braces if necessary let insert_braces = |builder: &mut TextEditBuilder| { - if !has_block_body { - if let Some(range) = closure.body().map(|b| b.syntax().text_range()) { - builder.insert(range.start(), "{ ".to_owned()); - builder.insert(range.end(), " }".to_owned()); - } + if !has_block_body && let Some(range) = closure.body().map(|b| b.syntax().text_range()) { + builder.insert(range.start(), "{ ".to_owned()); + builder.insert(range.end(), " }".to_owned()); } };
diff --git a/crates/ide/src/inlay_hints/extern_block.rs b/crates/ide/src/inlay_hints/extern_block.rs index 88152bf..491018a 100644 --- a/crates/ide/src/inlay_hints/extern_block.rs +++ b/crates/ide/src/inlay_hints/extern_block.rs
@@ -81,10 +81,10 @@ text_edit: Some(config.lazy_text_edit(|| { let mut builder = TextEdit::builder(); builder.insert(token.text_range().start(), "unsafe ".to_owned()); - if extern_block.unsafe_token().is_none() { - if let Some(abi) = extern_block.abi() { - builder.insert(abi.syntax().text_range().start(), "unsafe ".to_owned()); - } + if extern_block.unsafe_token().is_none() + && let Some(abi) = extern_block.abi() + { + builder.insert(abi.syntax().text_range().start(), "unsafe ".to_owned()); } builder.finish() })),
diff --git a/crates/ide/src/inlay_hints/generic_param.rs b/crates/ide/src/inlay_hints/generic_param.rs index 6e1b3bd..1fddb6f 100644 --- a/crates/ide/src/inlay_hints/generic_param.rs +++ b/crates/ide/src/inlay_hints/generic_param.rs
@@ -33,10 +33,10 @@ let mut args = generic_arg_list.generic_args().peekable(); let start_with_lifetime = matches!(args.peek()?, ast::GenericArg::LifetimeArg(_)); let params = generic_def.params(sema.db).into_iter().filter(|p| { - if let hir::GenericParam::TypeParam(it) = p { - if it.is_implicit(sema.db) { - return false; - } + if let hir::GenericParam::TypeParam(it) = p + && it.is_implicit(sema.db) + { + return false; } if !start_with_lifetime { return !matches!(p, hir::GenericParam::LifetimeParam(_));
diff --git a/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs index 7212efd..bddce90 100644 --- a/crates/ide/src/inlay_hints/implicit_static.rs +++ b/crates/ide/src/inlay_hints/implicit_static.rs
@@ -22,30 +22,31 @@ return None; } - if let Either::Right(it) = &statik_or_const { - if ast::AssocItemList::can_cast( + if let Either::Right(it) = &statik_or_const + && ast::AssocItemList::can_cast( it.syntax().parent().map_or(SyntaxKind::EOF, |it| it.kind()), - ) { - return None; - } + ) + { + return None; } - if let Some(ast::Type::RefType(ty)) = statik_or_const.either(|it| it.ty(), |it| it.ty()) { - if ty.lifetime().is_none() { - let t = ty.amp_token()?; - acc.push(InlayHint { - range: t.text_range(), - kind: InlayKind::Lifetime, - label: "'static".into(), - text_edit: Some(config.lazy_text_edit(|| { - TextEdit::insert(t.text_range().start(), "'static ".into()) - })), - position: InlayHintPosition::After, - pad_left: false, - pad_right: true, - resolve_parent: None, - }); - } + if let Some(ast::Type::RefType(ty)) = statik_or_const.either(|it| it.ty(), |it| it.ty()) + && ty.lifetime().is_none() + { + let t = ty.amp_token()?; + acc.push(InlayHint { + range: t.text_range(), + kind: InlayKind::Lifetime, + label: "'static".into(), + text_edit: Some( + config + .lazy_text_edit(|| TextEdit::insert(t.text_range().start(), "'static ".into())), + ), + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, + resolve_parent: None, + }); } Some(())
diff --git a/crates/ide/src/inlay_hints/lifetime.rs b/crates/ide/src/inlay_hints/lifetime.rs index 49fec0a..a89c53e 100644 --- a/crates/ide/src/inlay_hints/lifetime.rs +++ b/crates/ide/src/inlay_hints/lifetime.rs
@@ -324,35 +324,35 @@ // apply hints // apply output if required - if let (Some(output_lt), Some(r)) = (&output, ret_type) { - if let Some(ty) = r.ty() { - walk_ty(&ty, &mut |ty| match ty { - ast::Type::RefType(ty) if ty.lifetime().is_none() => { - if let Some(amp) = ty.amp_token() { - is_trivial = false; - acc.push(mk_lt_hint(amp, output_lt.to_string())); - } - false + if let (Some(output_lt), Some(r)) = (&output, ret_type) + && let Some(ty) = r.ty() + { + walk_ty(&ty, &mut |ty| match ty { + ast::Type::RefType(ty) if ty.lifetime().is_none() => { + if let Some(amp) = ty.amp_token() { + is_trivial = false; + acc.push(mk_lt_hint(amp, output_lt.to_string())); } - ast::Type::FnPtrType(_) => { + false + } + ast::Type::FnPtrType(_) => { + is_trivial = false; + true + } + ast::Type::PathType(t) => { + if t.path() + .and_then(|it| it.segment()) + .and_then(|it| it.parenthesized_arg_list()) + .is_some() + { is_trivial = false; true + } else { + false } - ast::Type::PathType(t) => { - if t.path() - .and_then(|it| it.segment()) - .and_then(|it| it.parenthesized_arg_list()) - .is_some() - { - is_trivial = false; - true - } else { - false - } - } - _ => false, - }) - } + } + _ => false, + }) } if config.lifetime_elision_hints == LifetimeElisionHints::SkipTrivial && is_trivial {
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs index 5174228..7547077 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -7,7 +7,7 @@ use std::iter::zip; use either::Either; -use hir::Semantics; +use hir::{EditionedFileId, Semantics}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use stdx::to_lower_snake_case; @@ -19,6 +19,7 @@ acc: &mut Vec<InlayHint>, FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + file_id: EditionedFileId, expr: ast::Expr, ) -> Option<()> { if !config.parameter_hints { @@ -39,6 +40,9 @@ .filter_map(|(p, arg)| { // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; + if range.file_id != file_id { + return None; + } let param_name = p.name(sema.db)?; Some((p, param_name, arg, range)) }) @@ -135,10 +139,10 @@ } if unary_function { - if let Some(function_name) = function_name { - if is_param_name_suffix_of_fn_name(param_name, function_name) { - return true; - } + if let Some(function_name) = function_name + && is_param_name_suffix_of_fn_name(param_name, function_name) + { + return true; } if is_obvious_param(param_name) { return true;
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index 0188c10..a946559 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs
@@ -144,15 +144,15 @@ } } - if config.join_else_if { - if let (Some(prev), Some(_next)) = (as_if_expr(&prev), as_if_expr(&next)) { - match prev.else_token() { - Some(_) => cov_mark::hit!(join_two_ifs_with_existing_else), - None => { - cov_mark::hit!(join_two_ifs); - edit.replace(token.text_range(), " else ".to_owned()); - return; - } + if config.join_else_if + && let (Some(prev), Some(_next)) = (as_if_expr(&prev), as_if_expr(&next)) + { + match prev.else_token() { + Some(_) => cov_mark::hit!(join_two_ifs_with_existing_else), + None => { + cov_mark::hit!(join_two_ifs); + edit.replace(token.text_range(), " else ".to_owned()); + return; } } } @@ -213,10 +213,10 @@ let mut buf = expr.syntax().text().to_string(); // Match block needs to have a comma after the block - if let Some(match_arm) = block_expr.syntax().parent().and_then(ast::MatchArm::cast) { - if match_arm.comma_token().is_none() { - buf.push(','); - } + if let Some(match_arm) = block_expr.syntax().parent().and_then(ast::MatchArm::cast) + && match_arm.comma_token().is_none() + { + buf.push(','); } edit.replace(block_range, buf);
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index b3b8deb..5349ebb 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs
@@ -81,7 +81,7 @@ annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation}, call_hierarchy::{CallHierarchyConfig, CallItem}, expand_macro::ExpandedMacro, - file_structure::{StructureNode, StructureNodeKind}, + file_structure::{FileStructureConfig, StructureNode, StructureNodeKind}, folding_ranges::{Fold, FoldKind}, highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{ @@ -299,7 +299,7 @@ /// Gets the text of the source file. pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> { - self.with_db(|db| SourceDatabase::file_text(db, file_id).text(db)) + self.with_db(|db| SourceDatabase::file_text(db, file_id).text(db).clone()) } /// Gets the syntax tree of the file. @@ -430,12 +430,16 @@ /// Returns a tree representation of symbols in the file. Useful to draw a /// file outline. - pub fn file_structure(&self, file_id: FileId) -> Cancellable<Vec<StructureNode>> { + pub fn file_structure( + &self, + config: &FileStructureConfig, + file_id: FileId, + ) -> Cancellable<Vec<StructureNode>> { // FIXME: Edition self.with_db(|db| { let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); - - file_structure::file_structure(&db.parse(editioned_file_id_wrapper).tree()) + let source_file = db.parse(editioned_file_id_wrapper).tree(); + file_structure::file_structure(&source_file, config) }) }
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 795c1f2..f1aa03c 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs
@@ -209,7 +209,6 @@ Definition::Const(..) => Constant, Definition::Static(..) => StaticVariable, Definition::Trait(..) => Trait, - Definition::TraitAlias(..) => Trait, Definition::TypeAlias(it) => { if it.as_assoc_item(db).is_some() { AssociatedType
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index f3bb3df..b5d47c8 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs
@@ -72,7 +72,6 @@ SyntaxKind::MACRO_CALL, SyntaxKind::TYPE_ALIAS, SyntaxKind::TRAIT, - SyntaxKind::TRAIT_ALIAS, SyntaxKind::IMPL, SyntaxKind::MACRO_DEF, SyntaxKind::STRUCT,
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 7dc1814..61b4de0 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs
@@ -226,9 +226,6 @@ hir::ModuleDef::Trait(it) => { Some(it.display(db, display_target).to_string()) } - hir::ModuleDef::TraitAlias(it) => { - Some(it.display(db, display_target).to_string()) - } hir::ModuleDef::TypeAlias(it) => { Some(it.display(db, display_target).to_string()) } @@ -261,7 +258,6 @@ Definition::Const(it) => it.try_to_nav(db), Definition::Static(it) => it.try_to_nav(db), Definition::Trait(it) => it.try_to_nav(db), - Definition::TraitAlias(it) => it.try_to_nav(db), Definition::TypeAlias(it) => it.try_to_nav(db), Definition::ExternCrateDecl(it) => it.try_to_nav(db), Definition::InlineAsmOperand(it) => it.try_to_nav(db), @@ -287,7 +283,6 @@ hir::ModuleDef::Const(it) => it.try_to_nav(db), hir::ModuleDef::Static(it) => it.try_to_nav(db), hir::ModuleDef::Trait(it) => it.try_to_nav(db), - hir::ModuleDef::TraitAlias(it) => it.try_to_nav(db), hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db), hir::ModuleDef::Macro(it) => it.try_to_nav(db), hir::ModuleDef::BuiltinType(_) => None, @@ -366,12 +361,6 @@ container_name(db, self, self.krate(db).edition(db)) } } -impl ToNavFromAst for hir::TraitAlias { - const KIND: SymbolKind = SymbolKind::TraitAlias; - fn container_name(self, db: &RootDatabase) -> Option<SmolStr> { - container_name(db, self, self.krate(db).edition(db)) - } -} impl<D> TryToNav for D where
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index 50219ce..96d829d 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs
@@ -29,14 +29,13 @@ let mut module = find_node_at_offset::<ast::Module>(source_file.syntax(), position.offset); // If cursor is literally on `mod foo`, go to the grandpa. - if let Some(m) = &module { - if !m + if let Some(m) = &module + && !m .item_list() .is_some_and(|it| it.syntax().text_range().contains_inclusive(position.offset)) - { - cov_mark::hit!(test_resolve_parent_module_on_module_decl); - module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); - } + { + cov_mark::hit!(test_resolve_parent_module_on_module_decl); + module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); } match module {
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 86b88a1..dc509b3 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs
@@ -1783,7 +1783,7 @@ fn foo<T: Bar>(_: impl Bar, _: &dyn Bar) {} "#, expect![[r#" - Bar TraitAlias FileId(0) 13..42 19..22 + Bar Trait FileId(0) 13..42 19..22 FileId(0) 53..56 FileId(0) 66..69
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index a07c647..8922a8e 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs
@@ -13,8 +13,11 @@ }; use itertools::Itertools; use std::fmt::Write; -use stdx::{always, never}; -use syntax::{AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, ast}; +use stdx::{always, format_to, never}; +use syntax::{ + AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, + ast::{self, HasArgList, prec::ExprPrecedence}, +}; use ide_db::text_edit::TextEdit; @@ -24,6 +27,27 @@ type RenameResult<T> = Result<T, RenameError>; +/// This is similar to `collect::<Result<Vec<_>, _>>`, but unlike it, it succeeds if there is *any* `Ok` item. +fn ok_if_any<T, E>(iter: impl Iterator<Item = Result<T, E>>) -> Result<Vec<T>, E> { + let mut err = None; + let oks = iter + .filter_map(|item| match item { + Ok(it) => Some(it), + Err(it) => { + err = Some(it); + None + } + }) + .collect::<Vec<_>>(); + if !oks.is_empty() { + Ok(oks) + } else if let Some(err) = err { + Err(err) + } else { + Ok(Vec::new()) + } +} + /// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is /// being targeted for a rename. pub(crate) fn prepare_rename( @@ -35,13 +59,8 @@ let syntax = source_file.syntax(); let res = find_definitions(&sema, syntax, position, &Name::new_symbol_root(sym::underscore))? - .map(|(frange, kind, def, _, _)| { - // ensure all ranges are valid - - if def.range_for_rename(&sema).is_none() { - bail!("No references found at position") - } - + .filter(|(_, _, def, _, _)| def.range_for_rename(&sema).is_some()) + .map(|(frange, kind, _, _, _)| { always!( frange.range.contains_inclusive(position.offset) && frange.file_id == position.file_id @@ -97,58 +116,57 @@ alias_fallback(syntax, position, &new_name.display(db, edition).to_string()); let ops: RenameResult<Vec<SourceChange>> = match alias_fallback { - Some(_) => defs - // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can - // properly find "direct" usages/references. - .map(|(.., def, new_name, _)| { - match kind { - IdentifierKind::Ident => (), - IdentifierKind::Lifetime => { - bail!("Cannot alias reference to a lifetime identifier") - } - IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"), - IdentifierKind::LowercaseSelf => { - bail!("Cannot rename alias reference to `self`") - } - }; - let mut usages = def.usages(&sema).all(); + Some(_) => ok_if_any( + defs + // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can + // properly find "direct" usages/references. + .map(|(.., def, new_name, _)| { + match kind { + IdentifierKind::Ident => (), + IdentifierKind::Lifetime => { + bail!("Cannot alias reference to a lifetime identifier") + } + IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"), + IdentifierKind::LowercaseSelf => { + bail!("Cannot rename alias reference to `self`") + } + }; + let mut usages = def.usages(&sema).all(); - // FIXME: hack - removes the usage that triggered this rename operation. - match usages.references.get_mut(&file_id).and_then(|refs| { - refs.iter() - .position(|ref_| ref_.range.contains_inclusive(position.offset)) - .map(|idx| refs.remove(idx)) - }) { - Some(_) => (), - None => never!(), - }; + // FIXME: hack - removes the usage that triggered this rename operation. + match usages.references.get_mut(&file_id).and_then(|refs| { + refs.iter() + .position(|ref_| ref_.range.contains_inclusive(position.offset)) + .map(|idx| refs.remove(idx)) + }) { + Some(_) => (), + None => never!(), + }; - let mut source_change = SourceChange::default(); - source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| { - ( - position.file_id, - source_edit_from_references(db, refs, def, &new_name, edition), - ) - })); + let mut source_change = SourceChange::default(); + source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| { + ( + position.file_id, + source_edit_from_references(db, refs, def, &new_name, edition), + ) + })); - Ok(source_change) - }) - .collect(), - None => defs - .map(|(.., def, new_name, rename_def)| { - if let Definition::Local(local) = def { - if let Some(self_param) = local.as_self_param(sema.db) { - cov_mark::hit!(rename_self_to_param); - return rename_self_to_param(&sema, local, self_param, &new_name, kind); - } - if kind == IdentifierKind::LowercaseSelf { - cov_mark::hit!(rename_to_self); - return rename_to_self(&sema, local); - } + Ok(source_change) + }), + ), + None => ok_if_any(defs.map(|(.., def, new_name, rename_def)| { + if let Definition::Local(local) = def { + if let Some(self_param) = local.as_self_param(sema.db) { + cov_mark::hit!(rename_self_to_param); + return rename_self_to_param(&sema, local, self_param, &new_name, kind); } - def.rename(&sema, new_name.as_str(), rename_def) - }) - .collect(), + if kind == IdentifierKind::LowercaseSelf { + cov_mark::hit!(rename_to_self); + return rename_to_self(&sema, local); + } + } + def.rename(&sema, new_name.as_str(), rename_def) + })), }; ops?.into_iter() @@ -322,7 +340,7 @@ }) }); - let res: RenameResult<Vec<_>> = symbols.filter_map(Result::transpose).collect(); + let res: RenameResult<Vec<_>> = ok_if_any(symbols.filter_map(Result::transpose)); match res { Ok(v) => { // remove duplicates, comparing `Definition`s @@ -336,6 +354,85 @@ } } +fn transform_assoc_fn_into_method_call( + sema: &Semantics<'_, RootDatabase>, + source_change: &mut SourceChange, + f: hir::Function, +) { + let calls = Definition::Function(f).usages(sema).all(); + for (file_id, calls) in calls { + for call in calls { + let Some(fn_name) = call.name.as_name_ref() else { continue }; + let Some(path) = fn_name.syntax().parent().and_then(ast::PathSegment::cast) else { + continue; + }; + let path = path.parent_path(); + // The `PathExpr` is the direct parent, above it is the `CallExpr`. + let Some(call) = + path.syntax().parent().and_then(|it| ast::CallExpr::cast(it.parent()?)) + else { + continue; + }; + + let Some(arg_list) = call.arg_list() else { continue }; + let mut args = arg_list.args(); + let Some(mut self_arg) = args.next() else { continue }; + let second_arg = args.next(); + + // Strip (de)references, as they will be taken automatically by auto(de)ref. + loop { + let self_ = match &self_arg { + ast::Expr::RefExpr(self_) => self_.expr(), + ast::Expr::ParenExpr(self_) => self_.expr(), + ast::Expr::PrefixExpr(self_) + if self_.op_kind() == Some(ast::UnaryOp::Deref) => + { + self_.expr() + } + _ => break, + }; + self_arg = match self_ { + Some(it) => it, + None => break, + }; + } + + let self_needs_parens = + self_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix); + + let replace_start = path.syntax().text_range().start(); + let replace_end = match second_arg { + Some(second_arg) => second_arg.syntax().text_range().start(), + None => arg_list + .r_paren_token() + .map(|it| it.text_range().start()) + .unwrap_or_else(|| arg_list.syntax().text_range().end()), + }; + let replace_range = TextRange::new(replace_start, replace_end); + + let Some(macro_mapped_self) = sema.original_range_opt(self_arg.syntax()) else { + continue; + }; + let mut replacement = String::new(); + if self_needs_parens { + replacement.push('('); + } + replacement.push_str(macro_mapped_self.text(sema.db)); + if self_needs_parens { + replacement.push(')'); + } + replacement.push('.'); + format_to!(replacement, "{fn_name}"); + replacement.push('('); + + source_change.insert_source_edit( + file_id.file_id(sema.db), + TextEdit::replace(replace_range, replacement), + ); + } + } +} + fn rename_to_self( sema: &Semantics<'_, RootDatabase>, local: hir::Local, @@ -413,6 +510,7 @@ file_id.original_file(sema.db).file_id(sema.db), TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)), ); + transform_assoc_fn_into_method_call(sema, &mut source_change, fn_def); Ok(source_change) } @@ -499,10 +597,10 @@ ) { let ra_fixture_after = &trim_indent(ra_fixture_after); let (analysis, position) = fixture::position(ra_fixture_before); - if !ra_fixture_after.starts_with("error: ") { - if let Err(err) = analysis.prepare_rename(position).unwrap() { - panic!("Prepare rename to '{new_name}' was failed: {err}") - } + if !ra_fixture_after.starts_with("error: ") + && let Err(err) = analysis.prepare_rename(position).unwrap() + { + panic!("Prepare rename to '{new_name}' was failed: {err}") } let rename_result = analysis .rename(position, new_name) @@ -3417,4 +3515,78 @@ "#, ); } + + #[test] + fn rename_to_self_callers() { + check( + "self", + r#" +//- minicore: add +struct Foo; +impl core::ops::Add for Foo { + type Target = Foo; + fn add(self, _: Self) -> Foo { Foo } +} + +impl Foo { + fn foo(th$0is: &Self) {} +} + +fn bar(v: &Foo) { + Foo::foo(v); +} + +fn baz() { + Foo::foo(&Foo); + Foo::foo(Foo + Foo); +} + "#, + r#" +struct Foo; +impl core::ops::Add for Foo { + type Target = Foo; + fn add(self, _: Self) -> Foo { Foo } +} + +impl Foo { + fn foo(&self) {} +} + +fn bar(v: &Foo) { + v.foo(); +} + +fn baz() { + Foo.foo(); + (Foo + Foo).foo(); +} + "#, + ); + // Multiple arguments: + check( + "self", + r#" +struct Foo; + +impl Foo { + fn foo(th$0is: &Self, v: i32) {} +} + +fn bar(v: Foo) { + Foo::foo(&v, 123); +} + "#, + r#" +struct Foo; + +impl Foo { + fn foo(&self, v: i32) {} +} + +fn bar(v: Foo) { + v.foo(123); +} + "#, + ); + } }
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 9d1a5ba..77c8429 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs
@@ -493,7 +493,6 @@ Definition::Const(it) => it.attrs(db), Definition::Static(it) => it.attrs(db), Definition::Trait(it) => it.attrs(db), - Definition::TraitAlias(it) => it.attrs(db), Definition::TypeAlias(it) => it.attrs(db), Definition::Macro(it) => it.attrs(db), Definition::SelfType(it) => it.attrs(db), @@ -514,20 +513,19 @@ .flat_map(|it| it.name(db)) .for_each(|name| format_to!(path, "{}::", name.display(db, edition))); // This probably belongs to canonical_path? - if let Some(assoc_item) = def.as_assoc_item(db) { - if let Some(ty) = assoc_item.implementing_ty(db) { - if let Some(adt) = ty.as_adt() { - let name = adt.name(db); - let mut ty_args = ty.generic_parameters(db, display_target).peekable(); - format_to!(path, "{}", name.display(db, edition)); - if ty_args.peek().is_some() { - format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))); - } - format_to!(path, "::{}", def_name.display(db, edition)); - path.retain(|c| c != ' '); - return Some(path); - } + if let Some(assoc_item) = def.as_assoc_item(db) + && let Some(ty) = assoc_item.implementing_ty(db) + && let Some(adt) = ty.as_adt() + { + let name = adt.name(db); + let mut ty_args = ty.generic_parameters(db, display_target).peekable(); + format_to!(path, "{}", name.display(db, edition)); + if ty_args.peek().is_some() { + format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))); } + format_to!(path, "::{}", def_name.display(db, edition)); + path.retain(|c| c != ' '); + return Some(path); } format_to!(path, "{}", def_name.display(db, edition)); Some(path) @@ -697,14 +695,13 @@ continue; }; for item in items { - if let hir::ItemInNs::Macros(makro) = item { - if Definition::Macro(makro) + if let hir::ItemInNs::Macros(makro) = item + && Definition::Macro(makro) .usages(sema) .in_scope(&search_scope) .at_least_one() - { - return true; - } + { + return true; } } }
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index e30a3eb..86d02b4 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs
@@ -146,12 +146,11 @@ // Stop at multi-line expressions, since the signature of the outer call is not very // helpful inside them. - if let Some(expr) = ast::Expr::cast(node.clone()) { - if !matches!(expr, ast::Expr::RecordExpr(..)) - && expr.syntax().text().contains_char('\n') - { - break; - } + if let Some(expr) = ast::Expr::cast(node.clone()) + && !matches!(expr, ast::Expr::RecordExpr(..)) + && expr.syntax().text().contains_char('\n') + { + break; } } @@ -340,10 +339,6 @@ res.doc = it.docs(db); format_to!(res.signature, "trait {}", it.name(db).display(db, edition)); } - hir::GenericDef::TraitAlias(it) => { - res.doc = it.docs(db); - format_to!(res.signature, "trait {}", it.name(db).display(db, edition)); - } hir::GenericDef::TypeAlias(it) => { res.doc = it.docs(db); format_to!(res.signature, "type {}", it.name(db).display(db, edition)); @@ -366,10 +361,10 @@ res.signature.push('<'); let mut buf = String::new(); for param in params { - if let hir::GenericParam::TypeParam(ty) = param { - if ty.is_implicit(db) { - continue; - } + if let hir::GenericParam::TypeParam(ty) = param + && ty.is_implicit(db) + { + continue; } buf.clear(); @@ -734,7 +729,7 @@ mod tests { use expect_test::{Expect, expect}; - use ide_db::FilePosition; + use ide_db::{FilePosition, base_db::salsa}; use stdx::format_to; use test_fixture::ChangeFixture; @@ -763,7 +758,7 @@ "# ); let (db, position) = position(&fixture); - let sig_help = crate::signature_help::signature_help(&db, position); + let sig_help = salsa::attach(&db, || crate::signature_help::signature_help(&db, position)); let actual = match sig_help { Some(sig_help) => { let mut rendered = String::new();
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index efee39c..694ac22 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs
@@ -133,10 +133,10 @@ ) -> Option<ArrayVec<Definition, 2>> { for token in sema.descend_into_macros_exact(token) { let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); - if let Some(defs) = def { - if !defs.is_empty() { - return Some(defs); - } + if let Some(defs) = def + && !defs.is_empty() + { + return Some(defs); } } None
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 3ca1729..e3dc3ed 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs
@@ -16,7 +16,7 @@ use either::Either; use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics}; -use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind}; +use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind, base_db::salsa}; use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::*, @@ -434,15 +434,17 @@ |node| unsafe_ops.contains(&InFile::new(descended_element.file_id, node)); let element = match descended_element.value { NodeOrToken::Node(name_like) => { - let hl = highlight::name_like( - sema, - krate, - bindings_shadow_count, - &is_unsafe_node, - config.syntactic_name_ref_highlighting, - name_like, - edition, - ); + let hl = salsa::attach(sema.db, || { + highlight::name_like( + sema, + krate, + bindings_shadow_count, + &is_unsafe_node, + config.syntactic_name_ref_highlighting, + name_like, + edition, + ) + }); if hl.is_some() && !in_macro { // skip highlighting the contained token of our name-like node // as that would potentially overwrite our result
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 87db0cd..d73575f 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -306,12 +306,12 @@ }; let mut h = match name_class { NameRefClass::Definition(def, _) => { - if let Definition::Local(local) = &def { - if let Some(bindings_shadow_count) = bindings_shadow_count { - let name = local.name(sema.db); - let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); - *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) - } + if let Definition::Local(local) = &def + && let Some(bindings_shadow_count) = bindings_shadow_count + { + let name = local.name(sema.db); + let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); + *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) }; let mut h = highlight_def(sema, krate, def, edition, true); @@ -437,21 +437,21 @@ edition: Edition, ) -> Highlight { let name_kind = NameClass::classify(sema, &name); - if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { - if let Some(bindings_shadow_count) = bindings_shadow_count { - let name = local.name(sema.db); - let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); - *shadow_count += 1; - *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) - } + if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind + && let Some(bindings_shadow_count) = bindings_shadow_count + { + let name = local.name(sema.db); + let shadow_count = bindings_shadow_count.entry(name.clone()).or_default(); + *shadow_count += 1; + *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) }; match name_kind { Some(NameClass::Definition(def)) => { let mut h = highlight_def(sema, krate, def, edition, false) | HlMod::Definition; - if let Definition::Trait(trait_) = &def { - if trait_.is_unsafe(sema.db) { - h |= HlMod::Unsafe; - } + if let Definition::Trait(trait_) = &def + && trait_.is_unsafe(sema.db) + { + h |= HlMod::Unsafe; } h } @@ -576,7 +576,6 @@ h } Definition::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)), - Definition::TraitAlias(_) => Highlight::new(HlTag::Symbol(SymbolKind::TraitAlias)), Definition::TypeAlias(type_) => { let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); @@ -743,10 +742,9 @@ hir::Access::Owned => { if let Some(receiver_ty) = method_call.receiver().and_then(|it| sema.type_of_expr(&it)) + && !receiver_ty.adjusted().is_copy(sema.db) { - if !receiver_ty.adjusted().is_copy(sema.db) { - h |= HlMod::Consuming - } + h |= HlMod::Consuming } } } @@ -781,7 +779,6 @@ MACRO_RULES => SymbolKind::Macro, CONST_PARAM => SymbolKind::ConstParam, SELF_PARAM => SymbolKind::SelfParam, - TRAIT_ALIAS => SymbolKind::TraitAlias, ASM_OPERAND_NAMED => SymbolKind::Local, _ => return default.into(), };
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 7f5c2c1..fb33307 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -311,7 +311,6 @@ Definition::Const(_) => SymbolKind::Const, Definition::Static(_) => SymbolKind::Static, Definition::Trait(_) => SymbolKind::Trait, - Definition::TraitAlias(_) => SymbolKind::TraitAlias, Definition::TypeAlias(_) => SymbolKind::TypeAlias, Definition::BuiltinLifetime(_) => SymbolKind::LifetimeParam, Definition::BuiltinType(_) => return HlTag::BuiltinType,
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 3b5d1af..4b87626 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -160,7 +160,6 @@ SymbolKind::Struct => "struct", SymbolKind::ToolModule => "tool_module", SymbolKind::Trait => "trait", - SymbolKind::TraitAlias => "trait_alias", SymbolKind::TypeAlias => "type_alias", SymbolKind::TypeParam => "type_param", SymbolKind::Union => "union",
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 00925bd..d99b29c 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -127,9 +127,9 @@ <span class="keyword">let</span> <span class="variable declaration mutable reference">y</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration reference">z</span> <span class="operator">=</span> <span class="operator">&</span><span class="variable mutable reference">y</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable declaration">z</span><span class="comma">,</span> <span class="variable declaration">y</span> <span class="brace">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable reference">z</span><span class="comma">,</span> <span class="variable mutable reference">y</span> <span class="brace">}</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable declaration">z</span><span class="comma">,</span> <span class="variable callable declaration">y</span> <span class="brace">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable reference">z</span><span class="comma">,</span> <span class="variable mutable reference">y</span> <span class="brace">}</span><span class="semicolon">;</span> - <span class="variable">y</span><span class="semicolon">;</span> + <span class="variable callable">y</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="unresolved_reference">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="unresolved_reference">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span> @@ -142,7 +142,7 @@ <span class="variable mutable">copy</span><span class="operator">.</span><span class="method mutable reference">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="variable mutable">copy</span><span class="operator">.</span><span class="method">baz</span><span class="parenthesis">(</span><span class="variable mutable">copy</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param callable declaration">x</span><span class="punctuation">|</span> <span class="value_param callable">x</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable callable declaration">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="method associated consuming">baz</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="comma">,</span><span class="parenthesis">)</span><span class="semicolon">;</span> @@ -172,13 +172,13 @@ <span class="brace">}</span> <span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function async declaration">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> - <span class="keyword">let</span> <span class="variable declaration">song</span> <span class="operator">=</span> <span class="unresolved_reference">learn_song</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword async control">await</span><span class="semicolon">;</span> - <span class="unresolved_reference">sing_song</span><span class="parenthesis">(</span><span class="variable consuming">song</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword async control">await</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable callable declaration">song</span> <span class="operator">=</span> <span class="unresolved_reference">learn_song</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword async control">await</span><span class="semicolon">;</span> + <span class="unresolved_reference">sing_song</span><span class="parenthesis">(</span><span class="variable callable">song</span><span class="parenthesis">)</span><span class="operator">.</span><span class="keyword async control">await</span><span class="semicolon">;</span> <span class="brace">}</span> <span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function async declaration">async_main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> <span class="keyword">let</span> <span class="variable declaration">f1</span> <span class="operator">=</span> <span class="function async">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="variable declaration">f2</span> <span class="operator">=</span> <span class="unresolved_reference">dance</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable callable declaration">f2</span> <span class="operator">=</span> <span class="unresolved_reference">dance</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="unresolved_reference">futures</span><span class="operator">::</span><span class="unresolved_reference">join</span><span class="macro_bang">!</span><span class="parenthesis">(</span>f1<span class="comma">,</span> f2<span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index f7d7982..47ee2ad 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -78,8 +78,8 @@ <span class="brace">}</span> <span class="keyword">use</span> <span class="unresolved_reference">foo</span><span class="operator">::</span><span class="unresolved_reference">bar</span> <span class="keyword">as</span> <span class="variable declaration">baz</span><span class="semicolon">;</span> -<span class="keyword">trait</span> <span class="trait_alias declaration">Bar</span> <span class="operator">=</span> <span class="unresolved_reference">Baz</span><span class="semicolon">;</span> -<span class="keyword">trait</span> <span class="trait_alias declaration">Foo</span> <span class="operator">=</span> <span class="trait_alias">Bar</span><span class="semicolon">;</span> +<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="operator">=</span> <span class="unresolved_reference">Baz</span><span class="semicolon">;</span> +<span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="operator">=</span> <span class="trait">Bar</span><span class="semicolon">;</span> <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\n</span><span class="char_literal">'</span><span class="semicolon">;</span>
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index 4780743..983ed36 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs
@@ -178,6 +178,8 @@ core, coroutine_state, coroutine, + coroutine_return, + coroutine_yield, count, crate_type, CStr, @@ -226,6 +228,8 @@ async_fn_once_output, async_fn_mut, async_fn, + call_ref_future, + call_once_future, fn_ptr_addr, fn_ptr_trait, format_alignment, @@ -416,6 +420,7 @@ rustc_allow_incoherent_impl, rustc_builtin_macro, rustc_coherence_is_core, + rustc_coinductive, rustc_const_panic_str, rustc_deallocator, rustc_deprecated_safe_2024,
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 26ee698..ad838a6 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs
@@ -475,10 +475,10 @@ } let changes = vfs.take_changes(); for (_, file) in changes { - if let vfs::Change::Create(v, _) | vfs::Change::Modify(v, _) = file.change { - if let Ok(text) = String::from_utf8(v) { - analysis_change.change_file(file.file_id, Some(text)) - } + if let vfs::Change::Create(v, _) | vfs::Change::Modify(v, _) = file.change + && let Ok(text) = String::from_utf8(v) + { + analysis_change.change_file(file.file_id, Some(text)) } } let source_roots = source_root_config.partition(vfs); @@ -533,7 +533,7 @@ current_dir, ) { Ok(Ok(subtree)) => Ok(subtree), - Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err.0)), + Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err)), Err(err) => Err(ProcMacroExpansionError::System(err.to_string())), } }
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index 04ac85a..b185556 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs
@@ -185,24 +185,22 @@ for it in tokens.iter() { collect_from_op(it, builder, seed); } - if i + 1 != cnt { - if let Some(sep) = separator { - match &**sep { - Separator::Literal(it) => { - builder.push(tt::Leaf::Literal(it.clone())) + if i + 1 != cnt + && let Some(sep) = separator + { + match &**sep { + Separator::Literal(it) => builder.push(tt::Leaf::Literal(it.clone())), + Separator::Ident(it) => builder.push(tt::Leaf::Ident(it.clone())), + Separator::Puncts(puncts) => { + for it in puncts { + builder.push(tt::Leaf::Punct(*it)) } - Separator::Ident(it) => builder.push(tt::Leaf::Ident(it.clone())), - Separator::Puncts(puncts) => { - for it in puncts { - builder.push(tt::Leaf::Punct(*it)) - } - } - Separator::Lifetime(punct, ident) => { - builder.push(tt::Leaf::Punct(*punct)); - builder.push(tt::Leaf::Ident(ident.clone())); - } - }; - } + } + Separator::Lifetime(punct, ident) => { + builder.push(tt::Leaf::Punct(*punct)); + builder.push(tt::Leaf::Ident(ident.clone())); + } + }; } } }
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index a8d5965..189efcd 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs
@@ -475,12 +475,12 @@ }) } OpDelimited::Op(Op::Subtree { tokens, delimiter }) => { - if let Ok((subtree, _)) = src.clone().expect_subtree() { - if subtree.delimiter.kind == delimiter.kind { - item.stack.push(item.dot); - item.dot = tokens.iter_delimited_with(*delimiter); - cur_items.push(item); - } + if let Ok((subtree, _)) = src.clone().expect_subtree() + && subtree.delimiter.kind == delimiter.kind + { + item.stack.push(item.dot); + item.dot = tokens.iter_delimited_with(*delimiter); + cur_items.push(item); } } OpDelimited::Op(Op::Var { kind, name, .. }) => {
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index 2b4151e..41fd72d 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs
@@ -77,38 +77,38 @@ return; } - if let Some((cm, blocklike)) = expr_stmt(p, Some(m)) { - if !(p.at(T!['}']) || (semicolon != Semicolon::Required && p.at(EOF))) { - // test no_semi_after_block - // fn foo() { - // if true {} - // loop {} - // match () {} - // while true {} - // for _ in () {} - // {} - // {} - // macro_rules! test { - // () => {} - // } - // test!{} - // } - let m = cm.precede(p); - match semicolon { - Semicolon::Required => { - if blocklike.is_block() { - p.eat(T![;]); - } else { - p.expect(T![;]); - } - } - Semicolon::Optional => { + if let Some((cm, blocklike)) = expr_stmt(p, Some(m)) + && !(p.at(T!['}']) || (semicolon != Semicolon::Required && p.at(EOF))) + { + // test no_semi_after_block + // fn foo() { + // if true {} + // loop {} + // match () {} + // while true {} + // for _ in () {} + // {} + // {} + // macro_rules! test { + // () => {} + // } + // test!{} + // } + let m = cm.precede(p); + match semicolon { + Semicolon::Required => { + if blocklike.is_block() { p.eat(T![;]); + } else { + p.expect(T![;]); } - Semicolon::Forbidden => (), } - m.complete(p, EXPR_STMT); + Semicolon::Optional => { + p.eat(T![;]); + } + Semicolon::Forbidden => (), } + m.complete(p, EXPR_STMT); } } @@ -134,14 +134,11 @@ if p.at(T![else]) { // test_err let_else_right_curly_brace // fn func() { let Some(_) = {Some(1)} else { panic!("h") };} - if let Some(expr) = expr_after_eq { - if let Some(token) = expr.last_token(p) { - if token == T!['}'] { - p.error( - "right curly brace `}` before `else` in a `let...else` statement not allowed" - ) - } - } + if let Some(expr) = expr_after_eq + && let Some(token) = expr.last_token(p) + && token == T!['}'] + { + p.error("right curly brace `}` before `else` in a `let...else` statement not allowed") } // test let_else
diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs index cb1b59f..d419817 100644 --- a/crates/parser/src/grammar/generic_params.rs +++ b/crates/parser/src/grammar/generic_params.rs
@@ -182,12 +182,6 @@ ); m.complete(p, USE_BOUND_GENERIC_ARGS); } - T![?] if p.nth_at(1, T![for]) => { - // test question_for_type_trait_bound - // fn f<T>() where T: ?for<> Sized {} - p.bump_any(); - types::for_type(p, false) - } _ => { if path_type_bound(p).is_err() { m.abandon(p); @@ -219,8 +213,13 @@ // test async_trait_bound // fn async_foo(_: impl async Fn(&i32)) {} p.eat(T![async]); + // test question_for_type_trait_bound + // fn f<T>() where T: for<> ?Sized {} p.eat(T![?]); + // test_err invalid_question_for_type_trait_bound + // fn f<T>() where T: ?for<> Sized {} + if paths::is_use_path_start(p) { types::path_type_bounds(p, false); // test_err type_bounds_macro_call_recovery
diff --git a/crates/parser/src/grammar/items/traits.rs b/crates/parser/src/grammar/items/traits.rs index 47f86ce..c1b1a3f 100644 --- a/crates/parser/src/grammar/items/traits.rs +++ b/crates/parser/src/grammar/items/traits.rs
@@ -20,7 +20,7 @@ // trait Z<U> = where Self: T<U>; generic_params::opt_where_clause(p); p.expect(T![;]); - m.complete(p, TRAIT_ALIAS); + m.complete(p, TRAIT); return; }
diff --git a/crates/parser/src/input.rs b/crates/parser/src/input.rs index 4490956..331bc58 100644 --- a/crates/parser/src/input.rs +++ b/crates/parser/src/input.rs
@@ -61,7 +61,7 @@ #[inline] fn push_impl(&mut self, kind: SyntaxKind, contextual_kind: SyntaxKind) { let idx = self.len(); - if idx % (bits::BITS as usize) == 0 { + if idx.is_multiple_of(bits::BITS as usize) { self.joint.push(0); } self.kind.push(kind);
diff --git a/crates/parser/src/shortcuts.rs b/crates/parser/src/shortcuts.rs index e2baec8..d5e5139 100644 --- a/crates/parser/src/shortcuts.rs +++ b/crates/parser/src/shortcuts.rs
@@ -252,10 +252,10 @@ WHITESPACE if text.contains("\n\n") => { // we check whether the next token is a doc-comment // and skip the whitespace in this case - if let Some((COMMENT, peek_text)) = trivias.peek().map(|(_, pair)| pair) { - if is_outer(peek_text) { - continue; - } + if let Some((COMMENT, peek_text)) = trivias.peek().map(|(_, pair)| pair) + && is_outer(peek_text) + { + continue; } break; }
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index 3a8041d..93e02a9 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs
@@ -284,7 +284,6 @@ STRUCT, TOKEN_TREE, TRAIT, - TRAIT_ALIAS, TRY_EXPR, TUPLE_EXPR, TUPLE_FIELD, @@ -457,7 +456,6 @@ | STRUCT | TOKEN_TREE | TRAIT - | TRAIT_ALIAS | TRY_EXPR | TUPLE_EXPR | TUPLE_FIELD
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index c642e1a..a3cfe64 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs
@@ -796,6 +796,12 @@ #[test] fn impl_type() { run_and_expect_errors("test_data/parser/inline/err/impl_type.rs"); } #[test] + fn invalid_question_for_type_trait_bound() { + run_and_expect_errors( + "test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs", + ); + } + #[test] fn let_else_right_curly_brace() { run_and_expect_errors("test_data/parser/inline/err/let_else_right_curly_brace.rs"); }
diff --git a/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast b/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast new file mode 100644 index 0000000..b060ee8 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast
@@ -0,0 +1,49 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + GENERIC_PARAM_LIST + L_ANGLE "<" + TYPE_PARAM + NAME + IDENT "T" + R_ANGLE ">" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + WHERE_PRED + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + QUESTION "?" + WHERE_PRED + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + R_ANGLE ">" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Sized" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 20: expected comma +error 31: expected colon
diff --git a/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs b/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs new file mode 100644 index 0000000..f80dd90 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs
@@ -0,0 +1 @@ +fn f<T>() where T: ?for<> Sized {}
diff --git a/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast b/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast index cb29615..69db1ae 100644 --- a/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast +++ b/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast
@@ -27,19 +27,18 @@ WHITESPACE " " TYPE_BOUND_LIST TYPE_BOUND + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + R_ANGLE ">" + WHITESPACE " " QUESTION "?" - FOR_TYPE - FOR_BINDER - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - R_ANGLE ">" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Sized" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Sized" WHITESPACE " " BLOCK_EXPR STMT_LIST
diff --git a/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs b/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs index f80dd90..96353df 100644 --- a/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs +++ b/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs
@@ -1 +1 @@ -fn f<T>() where T: ?for<> Sized {} +fn f<T>() where T: for<> ?Sized {}
diff --git a/crates/parser/test_data/parser/inline/ok/trait_alias.rast b/crates/parser/test_data/parser/inline/ok/trait_alias.rast index c45f870..2ef6648 100644 --- a/crates/parser/test_data/parser/inline/ok/trait_alias.rast +++ b/crates/parser/test_data/parser/inline/ok/trait_alias.rast
@@ -1,5 +1,5 @@ SOURCE_FILE - TRAIT_ALIAS + TRAIT TRAIT_KW "trait" WHITESPACE " " NAME
diff --git a/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast b/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast index 8f67824..4443d9d 100644 --- a/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast +++ b/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast
@@ -1,5 +1,5 @@ SOURCE_FILE - TRAIT_ALIAS + TRAIT TRAIT_KW "trait" WHITESPACE " " NAME @@ -50,7 +50,7 @@ IDENT "Copy" SEMICOLON ";" WHITESPACE "\n" - TRAIT_ALIAS + TRAIT TRAIT_KW "trait" WHITESPACE " " NAME
diff --git a/crates/proc-macro-api/src/legacy_protocol.rs b/crates/proc-macro-api/src/legacy_protocol.rs new file mode 100644 index 0000000..ee96b89 --- /dev/null +++ b/crates/proc-macro-api/src/legacy_protocol.rs
@@ -0,0 +1,172 @@ +//! The initial proc-macro-srv protocol, soon to be deprecated. + +pub mod json; +pub mod msg; + +use std::{ + io::{BufRead, Write}, + sync::Arc, +}; + +use paths::AbsPath; +use span::Span; + +use crate::{ + ProcMacro, ProcMacroKind, ServerError, + legacy_protocol::{ + json::{read_json, write_json}, + msg::{ + ExpandMacro, ExpandMacroData, ExpnGlobals, FlatTree, Message, Request, Response, + ServerConfig, SpanDataIndexMap, deserialize_span_data_index_map, + flat::serialize_span_data_index_map, + }, + }, + process::ProcMacroServerProcess, + version, +}; + +pub(crate) use crate::legacy_protocol::msg::SpanMode; + +/// Legacy span type, only defined here as it is still used by the proc-macro server. +/// While rust-analyzer doesn't use this anymore at all, RustRover relies on the legacy type for +/// proc-macro expansion. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct SpanId(pub u32); + +impl std::fmt::Debug for SpanId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +pub(crate) fn version_check(srv: &ProcMacroServerProcess) -> Result<u32, ServerError> { + let request = Request::ApiVersionCheck {}; + let response = send_task(srv, request)?; + + match response { + Response::ApiVersionCheck(version) => Ok(version), + _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), + } +} + +/// Enable support for rust-analyzer span mode if the server supports it. +pub(crate) fn enable_rust_analyzer_spans( + srv: &ProcMacroServerProcess, +) -> Result<SpanMode, ServerError> { + let request = Request::SetConfig(ServerConfig { span_mode: SpanMode::RustAnalyzer }); + let response = send_task(srv, request)?; + + match response { + Response::SetConfig(ServerConfig { span_mode }) => Ok(span_mode), + _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), + } +} + +/// Finds proc-macros in a given dynamic library. +pub(crate) fn find_proc_macros( + srv: &ProcMacroServerProcess, + dylib_path: &AbsPath, +) -> Result<Result<Vec<(String, ProcMacroKind)>, String>, ServerError> { + let request = Request::ListMacros { dylib_path: dylib_path.to_path_buf().into() }; + + let response = send_task(srv, request)?; + + match response { + Response::ListMacros(it) => Ok(it), + _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), + } +} + +pub(crate) fn expand( + proc_macro: &ProcMacro, + subtree: tt::SubtreeView<'_, Span>, + attr: Option<tt::SubtreeView<'_, Span>>, + env: Vec<(String, String)>, + def_site: Span, + call_site: Span, + mixed_site: Span, + current_dir: String, +) -> Result<Result<tt::TopSubtree<span::SpanData<span::SyntaxContext>>, String>, crate::ServerError> +{ + let version = proc_macro.process.version(); + let mut span_data_table = SpanDataIndexMap::default(); + let def_site = span_data_table.insert_full(def_site).0; + let call_site = span_data_table.insert_full(call_site).0; + let mixed_site = span_data_table.insert_full(mixed_site).0; + let task = ExpandMacro { + data: ExpandMacroData { + macro_body: FlatTree::new(subtree, version, &mut span_data_table), + macro_name: proc_macro.name.to_string(), + attributes: attr.map(|subtree| FlatTree::new(subtree, version, &mut span_data_table)), + has_global_spans: ExpnGlobals { + serialize: version >= version::HAS_GLOBAL_SPANS, + def_site, + call_site, + mixed_site, + }, + span_data_table: if proc_macro.process.rust_analyzer_spans() { + serialize_span_data_index_map(&span_data_table) + } else { + Vec::new() + }, + }, + lib: proc_macro.dylib_path.to_path_buf().into(), + env, + current_dir: Some(current_dir), + }; + + let response = send_task(&proc_macro.process, Request::ExpandMacro(Box::new(task)))?; + + match response { + Response::ExpandMacro(it) => Ok(it + .map(|tree| { + let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table); + if proc_macro.needs_fixup_change() { + proc_macro.change_fixup_to_match_old_server(&mut expanded); + } + expanded + }) + .map_err(|msg| msg.0)), + Response::ExpandMacroExtended(it) => Ok(it + .map(|resp| { + let mut expanded = FlatTree::to_subtree_resolved( + resp.tree, + version, + &deserialize_span_data_index_map(&resp.span_data_table), + ); + if proc_macro.needs_fixup_change() { + proc_macro.change_fixup_to_match_old_server(&mut expanded); + } + expanded + }) + .map_err(|msg| msg.0)), + _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), + } +} + +/// Sends a request to the proc-macro server and waits for a response. +fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result<Response, ServerError> { + if let Some(server_error) = srv.exited() { + return Err(server_error.clone()); + } + + srv.send_task(send_request, req) +} + +/// Sends a request to the server and reads the response. +fn send_request( + mut writer: &mut dyn Write, + mut reader: &mut dyn BufRead, + req: Request, + buf: &mut String, +) -> Result<Option<Response>, ServerError> { + req.write(write_json, &mut writer).map_err(|err| ServerError { + message: "failed to write request".into(), + io: Some(Arc::new(err)), + })?; + let res = Response::read(read_json, &mut reader, buf).map_err(|err| ServerError { + message: "failed to read response".into(), + io: Some(Arc::new(err)), + })?; + Ok(res) +}
diff --git a/crates/proc-macro-api/src/legacy_protocol/msg.rs b/crates/proc-macro-api/src/legacy_protocol/msg.rs index 1659362..b795c45 100644 --- a/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/crates/proc-macro-api/src/legacy_protocol/msg.rs
@@ -1,5 +1,6 @@ //! Defines messages for cross-process message passing based on `ndjson` wire protocol pub(crate) mod flat; +pub use self::flat::*; use std::io::{self, BufRead, Write}; @@ -9,24 +10,6 @@ use crate::ProcMacroKind; -pub use self::flat::{ - FlatTree, SpanDataIndexMap, deserialize_span_data_index_map, serialize_span_data_index_map, -}; -pub use span::TokenId; - -// The versions of the server protocol -pub const NO_VERSION_CHECK_VERSION: u32 = 0; -pub const VERSION_CHECK_VERSION: u32 = 1; -pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2; -pub const HAS_GLOBAL_SPANS: u32 = 3; -pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4; -/// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field. -pub const EXTENDED_LEAF_DATA: u32 = 5; -pub const HASHED_AST_ID: u32 = 6; - -/// Current API version of the proc-macro protocol. -pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID; - /// Represents requests sent from the client to the proc-macro-srv. #[derive(Debug, Serialize, Deserialize)] pub enum Request { @@ -48,7 +31,7 @@ } /// Defines the mode used for handling span data. -#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum SpanMode { /// Default mode, where spans are identified by an ID. #[default] @@ -210,6 +193,8 @@ TopSubtreeBuilder, }; + use crate::version; + use super::*; fn fixture_token_tree() -> TopSubtree<Span> { @@ -308,7 +293,7 @@ #[test] fn test_proc_macro_rpc_works() { let tt = fixture_token_tree(); - for v in RUST_ANALYZER_SPAN_SUPPORT..=CURRENT_API_VERSION { + for v in version::RUST_ANALYZER_SPAN_SUPPORT..=version::CURRENT_API_VERSION { let mut span_data_table = Default::default(); let task = ExpandMacro { data: ExpandMacroData {
diff --git a/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs b/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs index 597ffa0..fb3542d 100644 --- a/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs +++ b/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs
@@ -40,9 +40,12 @@ use intern::Symbol; use rustc_hash::FxHashMap; use serde_derive::{Deserialize, Serialize}; -use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContext, TextRange, TokenId}; +use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContext, TextRange}; -use crate::legacy_protocol::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA}; +use crate::{ + legacy_protocol::SpanId, + version::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA}, +}; pub type SpanDataIndexMap = indexmap::IndexSet<Span, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>; @@ -62,7 +65,7 @@ } pub fn deserialize_span_data_index_map(map: &[u32]) -> SpanDataIndexMap { - debug_assert!(map.len() % 5 == 0); + debug_assert!(map.len().is_multiple_of(5)); map.chunks_exact(5) .map(|span| { let &[file_id, ast_id, start, end, e] = span else { unreachable!() }; @@ -91,27 +94,27 @@ } struct SubtreeRepr { - open: TokenId, - close: TokenId, + open: SpanId, + close: SpanId, kind: tt::DelimiterKind, tt: [u32; 2], } struct LiteralRepr { - id: TokenId, + id: SpanId, text: u32, suffix: u32, kind: u16, } struct PunctRepr { - id: TokenId, + id: SpanId, char: char, spacing: tt::Spacing, } struct IdentRepr { - id: TokenId, + id: SpanId, text: u32, is_raw: bool, } @@ -122,7 +125,7 @@ version: u32, span_data_table: &mut SpanDataIndexMap, ) -> FlatTree { - let mut w = Writer { + let mut w = Writer::<Span> { string_table: FxHashMap::default(), work: VecDeque::new(), span_data_table, @@ -159,8 +162,11 @@ } } - pub fn new_raw(subtree: tt::SubtreeView<'_, TokenId>, version: u32) -> FlatTree { - let mut w = Writer { + pub fn new_raw<T: SpanTransformer<Table = ()>>( + subtree: tt::SubtreeView<'_, T::Span>, + version: u32, + ) -> FlatTree { + let mut w = Writer::<T> { string_table: FxHashMap::default(), work: VecDeque::new(), span_data_table: &mut (), @@ -202,7 +208,7 @@ version: u32, span_data_table: &SpanDataIndexMap, ) -> tt::TopSubtree<Span> { - Reader { + Reader::<Span> { subtree: if version >= ENCODE_CLOSE_SPAN_VERSION { read_vec(self.subtree, SubtreeRepr::read_with_close_span) } else { @@ -227,8 +233,11 @@ .read() } - pub fn to_subtree_unresolved(self, version: u32) -> tt::TopSubtree<TokenId> { - Reader { + pub fn to_subtree_unresolved<T: SpanTransformer<Table = ()>>( + self, + version: u32, + ) -> tt::TopSubtree<T::Span> { + Reader::<T> { subtree: if version >= ENCODE_CLOSE_SPAN_VERSION { read_vec(self.subtree, SubtreeRepr::read_with_close_span) } else { @@ -283,7 +292,7 @@ 3 => tt::DelimiterKind::Bracket, other => panic!("bad kind {other}"), }; - SubtreeRepr { open: TokenId(open), close: TokenId(!0), kind, tt: [lo, len] } + SubtreeRepr { open: SpanId(open), close: SpanId(!0), kind, tt: [lo, len] } } fn write_with_close_span(self) -> [u32; 5] { let kind = match self.kind { @@ -302,7 +311,7 @@ 3 => tt::DelimiterKind::Bracket, other => panic!("bad kind {other}"), }; - SubtreeRepr { open: TokenId(open), close: TokenId(close), kind, tt: [lo, len] } + SubtreeRepr { open: SpanId(open), close: SpanId(close), kind, tt: [lo, len] } } } @@ -311,13 +320,13 @@ [self.id.0, self.text] } fn read([id, text]: [u32; 2]) -> LiteralRepr { - LiteralRepr { id: TokenId(id), text, kind: 0, suffix: !0 } + LiteralRepr { id: SpanId(id), text, kind: 0, suffix: !0 } } fn write_with_kind(self) -> [u32; 4] { [self.id.0, self.text, self.kind as u32, self.suffix] } fn read_with_kind([id, text, kind, suffix]: [u32; 4]) -> LiteralRepr { - LiteralRepr { id: TokenId(id), text, kind: kind as u16, suffix } + LiteralRepr { id: SpanId(id), text, kind: kind as u16, suffix } } } @@ -335,7 +344,7 @@ 1 => tt::Spacing::Joint, other => panic!("bad spacing {other}"), }; - PunctRepr { id: TokenId(id), char: char.try_into().unwrap(), spacing } + PunctRepr { id: SpanId(id), char: char.try_into().unwrap(), spacing } } } @@ -344,44 +353,46 @@ [self.id.0, self.text] } fn read(data: [u32; 2]) -> IdentRepr { - IdentRepr { id: TokenId(data[0]), text: data[1], is_raw: false } + IdentRepr { id: SpanId(data[0]), text: data[1], is_raw: false } } fn write_with_rawness(self) -> [u32; 3] { [self.id.0, self.text, self.is_raw as u32] } fn read_with_rawness([id, text, is_raw]: [u32; 3]) -> IdentRepr { - IdentRepr { id: TokenId(id), text, is_raw: is_raw == 1 } + IdentRepr { id: SpanId(id), text, is_raw: is_raw == 1 } } } -trait InternableSpan: Copy { +pub trait SpanTransformer { type Table; - fn token_id_of(table: &mut Self::Table, s: Self) -> TokenId; - fn span_for_token_id(table: &Self::Table, id: TokenId) -> Self; + type Span: Copy; + fn token_id_of(table: &mut Self::Table, s: Self::Span) -> SpanId; + fn span_for_token_id(table: &Self::Table, id: SpanId) -> Self::Span; } - -impl InternableSpan for TokenId { +impl SpanTransformer for SpanId { type Table = (); - fn token_id_of((): &mut Self::Table, token_id: Self) -> TokenId { + type Span = Self; + fn token_id_of((): &mut Self::Table, token_id: Self::Span) -> SpanId { token_id } - fn span_for_token_id((): &Self::Table, id: TokenId) -> Self { + fn span_for_token_id((): &Self::Table, id: SpanId) -> Self::Span { id } } -impl InternableSpan for Span { +impl SpanTransformer for Span { type Table = SpanDataIndexMap; - fn token_id_of(table: &mut Self::Table, span: Self) -> TokenId { - TokenId(table.insert_full(span).0 as u32) + type Span = Self; + fn token_id_of(table: &mut Self::Table, span: Self::Span) -> SpanId { + SpanId(table.insert_full(span).0 as u32) } - fn span_for_token_id(table: &Self::Table, id: TokenId) -> Self { + fn span_for_token_id(table: &Self::Table, id: SpanId) -> Self::Span { *table.get_index(id.0 as usize).unwrap_or_else(|| &table[0]) } } -struct Writer<'a, 'span, S: InternableSpan> { - work: VecDeque<(usize, tt::iter::TtIter<'a, S>)>, +struct Writer<'a, 'span, S: SpanTransformer> { + work: VecDeque<(usize, tt::iter::TtIter<'a, S::Span>)>, string_table: FxHashMap<std::borrow::Cow<'a, str>, u32>, span_data_table: &'span mut S::Table, version: u32, @@ -394,8 +405,8 @@ text: Vec<String>, } -impl<'a, S: InternableSpan> Writer<'a, '_, S> { - fn write(&mut self, root: tt::SubtreeView<'a, S>) { +impl<'a, T: SpanTransformer> Writer<'a, '_, T> { + fn write(&mut self, root: tt::SubtreeView<'a, T::Span>) { let subtree = root.top_subtree(); self.enqueue(subtree, root.iter()); while let Some((idx, subtree)) = self.work.pop_front() { @@ -403,11 +414,11 @@ } } - fn token_id_of(&mut self, span: S) -> TokenId { - S::token_id_of(self.span_data_table, span) + fn token_id_of(&mut self, span: T::Span) -> SpanId { + T::token_id_of(self.span_data_table, span) } - fn subtree(&mut self, idx: usize, subtree: tt::iter::TtIter<'a, S>) { + fn subtree(&mut self, idx: usize, subtree: tt::iter::TtIter<'a, T::Span>) { let mut first_tt = self.token_tree.len(); let n_tt = subtree.clone().count(); // FIXME: `count()` walks over the entire iterator. self.token_tree.resize(first_tt + n_tt, !0); @@ -478,7 +489,11 @@ } } - fn enqueue(&mut self, subtree: &'a tt::Subtree<S>, contents: tt::iter::TtIter<'a, S>) -> u32 { + fn enqueue( + &mut self, + subtree: &'a tt::Subtree<T::Span>, + contents: tt::iter::TtIter<'a, T::Span>, + ) -> u32 { let idx = self.subtree.len(); let open = self.token_id_of(subtree.delimiter.open); let close = self.token_id_of(subtree.delimiter.close); @@ -507,7 +522,7 @@ } } -struct Reader<'span, S: InternableSpan> { +struct Reader<'span, S: SpanTransformer> { version: u32, subtree: Vec<SubtreeRepr>, literal: Vec<LiteralRepr>, @@ -518,11 +533,11 @@ span_data_table: &'span S::Table, } -impl<S: InternableSpan> Reader<'_, S> { - pub(crate) fn read(self) -> tt::TopSubtree<S> { - let mut res: Vec<Option<(tt::Delimiter<S>, Vec<tt::TokenTree<S>>)>> = +impl<T: SpanTransformer> Reader<'_, T> { + pub(crate) fn read(self) -> tt::TopSubtree<T::Span> { + let mut res: Vec<Option<(tt::Delimiter<T::Span>, Vec<tt::TokenTree<T::Span>>)>> = vec![None; self.subtree.len()]; - let read_span = |id| S::span_for_token_id(self.span_data_table, id); + let read_span = |id| T::span_for_token_id(self.span_data_table, id); for i in (0..self.subtree.len()).rev() { let repr = &self.subtree[i]; let token_trees = &self.token_tree[repr.tt[0] as usize..repr.tt[1] as usize];
diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index 516c741..97919b8 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs
@@ -5,24 +5,29 @@ //! is used to provide basic infrastructure for communication between two //! processes: Client (RA itself), Server (the external program) -pub mod legacy_protocol { - pub mod json; - pub mod msg; -} +pub mod legacy_protocol; mod process; use paths::{AbsPath, AbsPathBuf}; use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span}; use std::{fmt, io, sync::Arc, time::SystemTime}; -use crate::{ - legacy_protocol::msg::{ - ExpandMacro, ExpandMacroData, ExpnGlobals, FlatTree, HAS_GLOBAL_SPANS, HASHED_AST_ID, - PanicMessage, RUST_ANALYZER_SPAN_SUPPORT, Request, Response, SpanDataIndexMap, - deserialize_span_data_index_map, flat::serialize_span_data_index_map, - }, - process::ProcMacroServerProcess, -}; +use crate::process::ProcMacroServerProcess; + +/// The versions of the server protocol +pub mod version { + pub const NO_VERSION_CHECK_VERSION: u32 = 0; + pub const VERSION_CHECK_VERSION: u32 = 1; + pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2; + pub const HAS_GLOBAL_SPANS: u32 = 3; + pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4; + /// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field. + pub const EXTENDED_LEAF_DATA: u32 = 5; + pub const HASHED_AST_ID: u32 = 6; + + /// Current API version of the proc-macro protocol. + pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID; +} /// Represents different kinds of procedural macros that can be expanded by the external server. #[derive(Copy, Clone, Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize)] @@ -163,7 +168,7 @@ fn needs_fixup_change(&self) -> bool { let version = self.process.version(); - (RUST_ANALYZER_SPAN_SUPPORT..HASHED_AST_ID).contains(&version) + (version::RUST_ANALYZER_SPAN_SUPPORT..version::HASHED_AST_ID).contains(&version) } /// On some server versions, the fixup ast id is different than ours. So change it to match. @@ -204,7 +209,7 @@ call_site: Span, mixed_site: Span, current_dir: String, - ) -> Result<Result<tt::TopSubtree<Span>, PanicMessage>, ServerError> { + ) -> Result<Result<tt::TopSubtree<Span>, String>, ServerError> { let (mut subtree, mut attr) = (subtree, attr); let (mut subtree_changed, mut attr_changed); if self.needs_fixup_change() { @@ -219,57 +224,15 @@ } } - let version = self.process.version(); - - let mut span_data_table = SpanDataIndexMap::default(); - let def_site = span_data_table.insert_full(def_site).0; - let call_site = span_data_table.insert_full(call_site).0; - let mixed_site = span_data_table.insert_full(mixed_site).0; - let task = ExpandMacro { - data: ExpandMacroData { - macro_body: FlatTree::new(subtree, version, &mut span_data_table), - macro_name: self.name.to_string(), - attributes: attr - .map(|subtree| FlatTree::new(subtree, version, &mut span_data_table)), - has_global_spans: ExpnGlobals { - serialize: version >= HAS_GLOBAL_SPANS, - def_site, - call_site, - mixed_site, - }, - span_data_table: if version >= RUST_ANALYZER_SPAN_SUPPORT { - serialize_span_data_index_map(&span_data_table) - } else { - Vec::new() - }, - }, - lib: self.dylib_path.to_path_buf().into(), + legacy_protocol::expand( + self, + subtree, + attr, env, - current_dir: Some(current_dir), - }; - - let response = self.process.send_task(Request::ExpandMacro(Box::new(task)))?; - - match response { - Response::ExpandMacro(it) => Ok(it.map(|tree| { - let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table); - if self.needs_fixup_change() { - self.change_fixup_to_match_old_server(&mut expanded); - } - expanded - })), - Response::ExpandMacroExtended(it) => Ok(it.map(|resp| { - let mut expanded = FlatTree::to_subtree_resolved( - resp.tree, - version, - &deserialize_span_data_index_map(&resp.span_data_table), - ); - if self.needs_fixup_change() { - self.change_fixup_to_match_old_server(&mut expanded); - } - expanded - })), - _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), - } + def_site, + call_site, + mixed_site, + current_dir, + ) } }
diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index fcea75e..fe274a0 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs
@@ -12,13 +12,8 @@ use crate::{ ProcMacroKind, ServerError, - legacy_protocol::{ - json::{read_json, write_json}, - msg::{ - CURRENT_API_VERSION, Message, RUST_ANALYZER_SPAN_SUPPORT, Request, Response, - ServerConfig, SpanMode, - }, - }, + legacy_protocol::{self, SpanMode}, + version, }; /// Represents a process handling proc-macro communication. @@ -28,11 +23,16 @@ /// hence the lock on the state. state: Mutex<ProcessSrvState>, version: u32, - mode: SpanMode, + protocol: Protocol, /// Populated when the server exits. exited: OnceLock<AssertUnwindSafe<ServerError>>, } +#[derive(Debug)] +enum Protocol { + LegacyJson { mode: SpanMode }, +} + /// Maintains the state of the proc-macro server process. #[derive(Debug)] struct ProcessSrvState { @@ -56,34 +56,40 @@ io::Result::Ok(ProcMacroServerProcess { state: Mutex::new(ProcessSrvState { process, stdin, stdout }), version: 0, - mode: SpanMode::Id, + protocol: Protocol::LegacyJson { mode: SpanMode::Id }, exited: OnceLock::new(), }) }; let mut srv = create_srv()?; tracing::info!("sending proc-macro server version check"); match srv.version_check() { - Ok(v) if v > CURRENT_API_VERSION => Err(io::Error::other( - format!( "The version of the proc-macro server ({v}) in your Rust toolchain is newer than the version supported by your rust-analyzer ({CURRENT_API_VERSION}). - This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain." - ), - )), + Ok(v) if v > version::CURRENT_API_VERSION => { + #[allow(clippy::disallowed_methods)] + let process_version = Command::new(process_path) + .arg("--version") + .output() + .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned()) + .unwrap_or_else(|_| "unknown version".to_owned()); + Err(io::Error::other(format!( + "Your installed proc-macro server is too new for your rust-analyzer. API version: {}, server version: {process_version}. \ + This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain.", + version::CURRENT_API_VERSION + ))) + } Ok(v) => { tracing::info!("Proc-macro server version: {v}"); srv.version = v; - if srv.version >= RUST_ANALYZER_SPAN_SUPPORT { - if let Ok(mode) = srv.enable_rust_analyzer_spans() { - srv.mode = mode; - } + if srv.version >= version::RUST_ANALYZER_SPAN_SUPPORT + && let Ok(mode) = srv.enable_rust_analyzer_spans() + { + srv.protocol = Protocol::LegacyJson { mode }; } - tracing::info!("Proc-macro server span mode: {:?}", srv.mode); + tracing::info!("Proc-macro server protocol: {:?}", srv.protocol); Ok(srv) } Err(e) => { tracing::info!(%e, "proc-macro version check failed"); - Err( - io::Error::other(format!("proc-macro server version check failed: {e}")), - ) + Err(io::Error::other(format!("proc-macro server version check failed: {e}"))) } } } @@ -98,25 +104,24 @@ self.version } + /// Enable support for rust-analyzer span mode if the server supports it. + pub(crate) fn rust_analyzer_spans(&self) -> bool { + match self.protocol { + Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer, + } + } + /// Checks the API version of the running proc-macro server. fn version_check(&self) -> Result<u32, ServerError> { - let request = Request::ApiVersionCheck {}; - let response = self.send_task(request)?; - - match response { - Response::ApiVersionCheck(version) => Ok(version), - _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), + match self.protocol { + Protocol::LegacyJson { .. } => legacy_protocol::version_check(self), } } /// Enable support for rust-analyzer span mode if the server supports it. fn enable_rust_analyzer_spans(&self) -> Result<SpanMode, ServerError> { - let request = Request::SetConfig(ServerConfig { span_mode: SpanMode::RustAnalyzer }); - let response = self.send_task(request)?; - - match response { - Response::SetConfig(ServerConfig { span_mode }) => Ok(span_mode), - _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), + match self.protocol { + Protocol::LegacyJson { .. } => legacy_protocol::enable_rust_analyzer_spans(self), } } @@ -125,25 +130,24 @@ &self, dylib_path: &AbsPath, ) -> Result<Result<Vec<(String, ProcMacroKind)>, String>, ServerError> { - let request = Request::ListMacros { dylib_path: dylib_path.to_path_buf().into() }; - - let response = self.send_task(request)?; - - match response { - Response::ListMacros(it) => Ok(it), - _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), + match self.protocol { + Protocol::LegacyJson { .. } => legacy_protocol::find_proc_macros(self, dylib_path), } } - /// Sends a request to the proc-macro server and waits for a response. - pub(crate) fn send_task(&self, req: Request) -> Result<Response, ServerError> { - if let Some(server_error) = self.exited.get() { - return Err(server_error.0.clone()); - } - + pub(crate) fn send_task<Request, Response>( + &self, + serialize_req: impl FnOnce( + &mut dyn Write, + &mut dyn BufRead, + Request, + &mut String, + ) -> Result<Option<Response>, ServerError>, + req: Request, + ) -> Result<Response, ServerError> { let state = &mut *self.state.lock().unwrap(); let mut buf = String::new(); - send_request(&mut state.stdin, &mut state.stdout, req, &mut buf) + serialize_req(&mut state.stdin, &mut state.stdout, req, &mut buf) .and_then(|res| { res.ok_or_else(|| { let message = "proc-macro server did not respond with data".to_owned(); @@ -162,10 +166,10 @@ Ok(None) | Err(_) => e, Ok(Some(status)) => { let mut msg = String::new(); - if !status.success() { - if let Some(stderr) = state.process.child.stderr.as_mut() { - _ = stderr.read_to_string(&mut msg); - } + if !status.success() + && let Some(stderr) = state.process.child.stderr.as_mut() + { + _ = stderr.read_to_string(&mut msg); } let server_error = ServerError { message: format!( @@ -242,21 +246,3 @@ } cmd.spawn() } - -/// Sends a request to the server and reads the response. -fn send_request( - mut writer: &mut impl Write, - mut reader: &mut impl BufRead, - req: Request, - buf: &mut String, -) -> Result<Option<Response>, ServerError> { - req.write(write_json, &mut writer).map_err(|err| ServerError { - message: "failed to write request".into(), - io: Some(Arc::new(err)), - })?; - let res = Response::read(read_json, &mut reader, buf).map_err(|err| ServerError { - message: "failed to read response".into(), - io: Some(Arc::new(err)), - })?; - Ok(res) -}
diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml index ab42102..91e9e62 100644 --- a/crates/proc-macro-srv-cli/Cargo.toml +++ b/crates/proc-macro-srv-cli/Cargo.toml
@@ -14,10 +14,14 @@ proc-macro-srv.workspace = true proc-macro-api.workspace = true tt.workspace = true +clap = {version = "4.5.42", default-features = false, features = ["std"]} +postcard = { version = "1.1.3", optional = true } [features] +default = ["postcard"] sysroot-abi = ["proc-macro-srv/sysroot-abi"] in-rust-tree = ["proc-macro-srv/in-rust-tree", "sysroot-abi"] +postcard = ["dep:postcard"] [[bin]]
diff --git a/crates/proc-macro-srv-cli/build.rs b/crates/proc-macro-srv-cli/build.rs index 07f914f..12e7c8b 100644 --- a/crates/proc-macro-srv-cli/build.rs +++ b/crates/proc-macro-srv-cli/build.rs
@@ -1,5 +1,49 @@ -//! This teaches cargo about our cfg(rust_analyzer) +//! Construct version in the `commit-hash date channel` format + +use std::{env, path::PathBuf, process::Command}; fn main() { - println!("cargo:rustc-check-cfg=cfg(rust_analyzer)"); + set_rerun(); + set_commit_info(); + println!("cargo::rustc-check-cfg=cfg(rust_analyzer)"); +} + +fn set_rerun() { + println!("cargo:rerun-if-env-changed=CFG_RELEASE"); + + let mut manifest_dir = PathBuf::from( + env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo."), + ); + + while manifest_dir.parent().is_some() { + let head_ref = manifest_dir.join(".git/HEAD"); + if head_ref.exists() { + println!("cargo:rerun-if-changed={}", head_ref.display()); + return; + } + + manifest_dir.pop(); + } + + println!("cargo:warning=Could not find `.git/HEAD` from manifest dir!"); +} + +fn set_commit_info() { + #[allow(clippy::disallowed_methods)] + let output = match Command::new("git") + .arg("log") + .arg("-1") + .arg("--date=short") + .arg("--format=%H %h %cd") + .output() + { + Ok(output) if output.status.success() => output, + _ => return, + }; + let stdout = String::from_utf8(output.stdout).unwrap(); + let mut parts = stdout.split_whitespace(); + let mut next = || parts.next().unwrap(); + println!("cargo:rustc-env=RA_COMMIT_HASH={}", next()); + println!("cargo:rustc-env=RA_COMMIT_SHORT_HASH={}", next()); + println!("cargo:rustc-env=RA_COMMIT_DATE={}", next()) }
diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs index c47ed05..662d348 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs
@@ -2,13 +2,16 @@ //! Driver for proc macro server #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] -#![allow(clippy::print_stderr)] +#![allow(clippy::print_stdout, clippy::print_stderr)] #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; +mod version; + #[cfg(any(feature = "sysroot-abi", rust_analyzer))] mod main_loop; +use clap::{Command, ValueEnum}; #[cfg(any(feature = "sysroot-abi", rust_analyzer))] use main_loop::run; @@ -23,12 +26,59 @@ ); std::process::exit(122); } + let matches = Command::new("proc-macro-srv") + .args(&[ + clap::Arg::new("format") + .long("format") + .action(clap::ArgAction::Set) + .default_value("json") + .value_parser(clap::builder::EnumValueParser::<ProtocolFormat>::new()), + clap::Arg::new("version") + .long("version") + .action(clap::ArgAction::SetTrue) + .help("Prints the version of the proc-macro-srv"), + ]) + .get_matches(); + if matches.get_flag("version") { + println!("rust-analyzer-proc-macro-srv {}", version::version()); + return Ok(()); + } + let &format = + matches.get_one::<ProtocolFormat>("format").expect("format value should always be present"); + run(format) +} - run() +#[derive(Copy, Clone)] +enum ProtocolFormat { + Json, + #[cfg(feature = "postcard")] + Postcard, +} + +impl ValueEnum for ProtocolFormat { + fn value_variants<'a>() -> &'a [Self] { + &[ProtocolFormat::Json] + } + + fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> { + match self { + ProtocolFormat::Json => Some(clap::builder::PossibleValue::new("json")), + #[cfg(feature = "postcard")] + ProtocolFormat::Postcard => Some(clap::builder::PossibleValue::new("postcard")), + } + } + fn from_str(input: &str, _ignore_case: bool) -> Result<Self, String> { + match input { + "json" => Ok(ProtocolFormat::Json), + #[cfg(feature = "postcard")] + "postcard" => Ok(ProtocolFormat::Postcard), + _ => Err(format!("unknown protocol format: {input}")), + } + } } #[cfg(not(any(feature = "sysroot-abi", rust_analyzer)))] -fn run() -> std::io::Result<()> { +fn run(_: ProtocolFormat) -> std::io::Result<()> { Err(std::io::Error::new( std::io::ErrorKind::Unsupported, "proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function"
diff --git a/crates/proc-macro-srv-cli/src/main_loop.rs b/crates/proc-macro-srv-cli/src/main_loop.rs index f54dff1..703bc96 100644 --- a/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/crates/proc-macro-srv-cli/src/main_loop.rs
@@ -1,16 +1,48 @@ //! The main loop of the proc-macro server. use std::io; -use proc_macro_api::legacy_protocol::{ - json::{read_json, write_json}, - msg::{ - self, CURRENT_API_VERSION, ExpandMacroData, ExpnGlobals, Message, SpanMode, TokenId, - deserialize_span_data_index_map, serialize_span_data_index_map, +use proc_macro_api::{ + legacy_protocol::{ + json::{read_json, write_json}, + msg::{ + self, ExpandMacroData, ExpnGlobals, Message, SpanMode, SpanTransformer, + deserialize_span_data_index_map, serialize_span_data_index_map, + }, }, + version::CURRENT_API_VERSION, }; -use proc_macro_srv::EnvSnapshot; +use proc_macro_srv::{EnvSnapshot, SpanId}; -pub(crate) fn run() -> io::Result<()> { +use crate::ProtocolFormat; + +struct SpanTrans; + +impl SpanTransformer for SpanTrans { + type Table = (); + type Span = SpanId; + fn token_id_of( + _: &mut Self::Table, + span: Self::Span, + ) -> proc_macro_api::legacy_protocol::SpanId { + proc_macro_api::legacy_protocol::SpanId(span.0) + } + fn span_for_token_id( + _: &Self::Table, + id: proc_macro_api::legacy_protocol::SpanId, + ) -> Self::Span { + SpanId(id.0) + } +} + +pub(crate) fn run(format: ProtocolFormat) -> io::Result<()> { + match format { + ProtocolFormat::Json => run_json(), + #[cfg(feature = "postcard")] + ProtocolFormat::Postcard => unimplemented!(), + } +} + +fn run_json() -> io::Result<()> { fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { match kind { proc_macro_srv::ProcMacroKind::CustomDerive => { @@ -54,19 +86,20 @@ } = *task; match span_mode { SpanMode::Id => msg::Response::ExpandMacro({ - let def_site = TokenId(def_site as u32); - let call_site = TokenId(call_site as u32); - let mixed_site = TokenId(mixed_site as u32); + let def_site = SpanId(def_site as u32); + let call_site = SpanId(call_site as u32); + let mixed_site = SpanId(mixed_site as u32); - let macro_body = macro_body.to_subtree_unresolved(CURRENT_API_VERSION); - let attributes = - attributes.map(|it| it.to_subtree_unresolved(CURRENT_API_VERSION)); + let macro_body = + macro_body.to_subtree_unresolved::<SpanTrans>(CURRENT_API_VERSION); + let attributes = attributes + .map(|it| it.to_subtree_unresolved::<SpanTrans>(CURRENT_API_VERSION)); srv.expand( lib, - env, + &env, current_dir, - macro_name, + ¯o_name, macro_body, attributes, def_site, @@ -74,8 +107,12 @@ mixed_site, ) .map(|it| { - msg::FlatTree::new_raw(tt::SubtreeView::new(&it), CURRENT_API_VERSION) + msg::FlatTree::new_raw::<SpanTrans>( + tt::SubtreeView::new(&it), + CURRENT_API_VERSION, + ) }) + .map_err(|e| e.into_string().unwrap_or_default()) .map_err(msg::PanicMessage) }), SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended({ @@ -92,9 +129,9 @@ }); srv.expand( lib, - env, + &env, current_dir, - macro_name, + ¯o_name, macro_body, attributes, def_site, @@ -115,6 +152,7 @@ tree, span_data_table, }) + .map_err(|e| e.into_string().unwrap_or_default()) .map_err(msg::PanicMessage) }), }
diff --git a/crates/proc-macro-srv-cli/src/version.rs b/crates/proc-macro-srv-cli/src/version.rs new file mode 100644 index 0000000..32499d0 --- /dev/null +++ b/crates/proc-macro-srv-cli/src/version.rs
@@ -0,0 +1,58 @@ +//! Code for representing rust-analyzer's release version number. +#![expect(dead_code)] + +use std::fmt; + +/// Information about the git repository where rust-analyzer was built from. +pub(crate) struct CommitInfo { + pub(crate) short_commit_hash: &'static str, + pub(crate) commit_hash: &'static str, + pub(crate) commit_date: &'static str, +} + +/// Cargo's version. +pub(crate) struct VersionInfo { + /// rust-analyzer's version, such as "1.57.0", "1.58.0-beta.1", "1.59.0-nightly", etc. + pub(crate) version: &'static str, + /// The release channel we were built for (stable/beta/nightly/dev). + /// + /// `None` if not built via bootstrap. + pub(crate) release_channel: Option<&'static str>, + /// Information about the Git repository we may have been built from. + /// + /// `None` if not built from a git repo. + pub(crate) commit_info: Option<CommitInfo>, +} + +impl fmt::Display for VersionInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.version)?; + + if let Some(ci) = &self.commit_info { + write!(f, " ({} {})", ci.short_commit_hash, ci.commit_date)?; + }; + Ok(()) + } +} + +/// Returns information about cargo's version. +pub(crate) const fn version() -> VersionInfo { + let version = match option_env!("CFG_RELEASE") { + Some(x) => x, + None => "0.0.0", + }; + + let release_channel = option_env!("CFG_RELEASE_CHANNEL"); + let commit_info = match ( + option_env!("RA_COMMIT_SHORT_HASH"), + option_env!("RA_COMMIT_HASH"), + option_env!("RA_COMMIT_DATE"), + ) { + (Some(short_commit_hash), Some(commit_hash), Some(commit_date)) => { + Some(CommitInfo { short_commit_hash, commit_hash, commit_date }) + } + _ => None, + }; + + VersionInfo { version, release_channel, commit_info } +}
diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml index 4034f24..d037e71 100644 --- a/crates/proc-macro-srv/Cargo.toml +++ b/crates/proc-macro-srv/Cargo.toml
@@ -16,6 +16,7 @@ object.workspace = true libloading.workspace = true memmap2.workspace = true +temp-dir.workspace = true tt.workspace = true syntax-bridge.workspace = true @@ -26,6 +27,7 @@ ra-ap-rustc_lexer.workspace = true + [target.'cfg(unix)'.dependencies] libc.workspace = true
diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs index c49159d..c8513a1 100644 --- a/crates/proc-macro-srv/src/dylib.rs +++ b/crates/proc-macro-srv/src/dylib.rs
@@ -1,65 +1,66 @@ //! Handles dynamic library loading for proc macro +mod proc_macros; mod version; use proc_macro::bridge; use std::{fmt, fs, io, time::SystemTime}; +use temp_dir::TempDir; use libloading::Library; use object::Object; use paths::{Utf8Path, Utf8PathBuf}; -use crate::{ProcMacroKind, ProcMacroSrvSpan, proc_macros::ProcMacros, server_impl::TopSubtree}; +use crate::{ + PanicMessage, ProcMacroKind, ProcMacroSrvSpan, dylib::proc_macros::ProcMacros, + server_impl::TopSubtree, +}; -/// Loads dynamic library in platform dependent manner. -/// -/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described -/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample) -/// and [here](https://github.com/rust-lang/rust/issues/60593). -/// -/// Usage of RTLD_DEEPBIND -/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1) -/// -/// It seems that on Windows that behaviour is default, so we do nothing in that case. -/// -/// # Safety -/// -/// The caller is responsible for ensuring that the path is valid proc-macro library -#[cfg(windows)] -unsafe fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> { - // SAFETY: The caller is responsible for ensuring that the path is valid proc-macro library - unsafe { Library::new(file) } +pub(crate) struct Expander { + inner: ProcMacroLibrary, + modified_time: SystemTime, } -/// Loads dynamic library in platform dependent manner. -/// -/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described -/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample) -/// and [here](https://github.com/rust-lang/rust/issues/60593). -/// -/// Usage of RTLD_DEEPBIND -/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1) -/// -/// It seems that on Windows that behaviour is default, so we do nothing in that case. -/// -/// # Safety -/// -/// The caller is responsible for ensuring that the path is valid proc-macro library -#[cfg(unix)] -unsafe fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> { - // not defined by POSIX, different values on mips vs other targets - #[cfg(target_env = "gnu")] - use libc::RTLD_DEEPBIND; - use libloading::os::unix::Library as UnixLibrary; - // defined by POSIX - use libloading::os::unix::RTLD_NOW; +impl Expander { + pub(crate) fn new( + temp_dir: &TempDir, + lib: &Utf8Path, + ) -> Result<Expander, LoadProcMacroDylibError> { + // Some libraries for dynamic loading require canonicalized path even when it is + // already absolute + let lib = lib.canonicalize_utf8()?; + let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?; - // MUSL and bionic don't have it.. - #[cfg(not(target_env = "gnu"))] - const RTLD_DEEPBIND: std::os::raw::c_int = 0x0; + let path = ensure_file_with_lock_free_access(temp_dir, &lib)?; + let library = ProcMacroLibrary::open(path.as_ref())?; - // SAFETY: The caller is responsible for ensuring that the path is valid proc-macro library - unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) } + Ok(Expander { inner: library, modified_time }) + } + + pub(crate) fn expand<S: ProcMacroSrvSpan>( + &self, + macro_name: &str, + macro_body: TopSubtree<S>, + attributes: Option<TopSubtree<S>>, + def_site: S, + call_site: S, + mixed_site: S, + ) -> Result<TopSubtree<S>, PanicMessage> + where + <S::Server as bridge::server::Types>::TokenStream: Default, + { + self.inner + .proc_macros + .expand(macro_name, macro_body, attributes, def_site, call_site, mixed_site) + } + + pub(crate) fn list_macros(&self) -> impl Iterator<Item = (&str, ProcMacroKind)> { + self.inner.proc_macros.list_macros() + } + + pub(crate) fn modified_time(&self) -> SystemTime { + self.modified_time + } } #[derive(Debug)] @@ -133,54 +134,6 @@ } } -// Drop order matters as we can't remove the dylib before the library is unloaded -pub(crate) struct Expander { - inner: ProcMacroLibrary, - _remove_on_drop: RemoveFileOnDrop, - modified_time: SystemTime, -} - -impl Expander { - pub(crate) fn new(lib: &Utf8Path) -> Result<Expander, LoadProcMacroDylibError> { - // Some libraries for dynamic loading require canonicalized path even when it is - // already absolute - let lib = lib.canonicalize_utf8()?; - let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?; - - let path = ensure_file_with_lock_free_access(&lib)?; - let library = ProcMacroLibrary::open(path.as_ref())?; - - Ok(Expander { inner: library, _remove_on_drop: RemoveFileOnDrop(path), modified_time }) - } - - pub(crate) fn expand<S: ProcMacroSrvSpan>( - &self, - macro_name: &str, - macro_body: TopSubtree<S>, - attributes: Option<TopSubtree<S>>, - def_site: S, - call_site: S, - mixed_site: S, - ) -> Result<TopSubtree<S>, String> - where - <S::Server as bridge::server::Types>::TokenStream: Default, - { - let result = self - .inner - .proc_macros - .expand(macro_name, macro_body, attributes, def_site, call_site, mixed_site); - result.map_err(|e| e.into_string().unwrap_or_default()) - } - - pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - self.inner.proc_macros.list_macros() - } - - pub(crate) fn modified_time(&self) -> SystemTime { - self.modified_time - } -} - fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error { io::Error::new(io::ErrorKind::InvalidData, e) } @@ -210,18 +163,12 @@ })) } -struct RemoveFileOnDrop(Utf8PathBuf); -impl Drop for RemoveFileOnDrop { - fn drop(&mut self) { - #[cfg(windows)] - std::fs::remove_file(&self.0).unwrap(); - _ = self.0; - } -} - /// Copy the dylib to temp directory to prevent locking in Windows #[cfg(windows)] -fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf> { +fn ensure_file_with_lock_free_access( + temp_dir: &TempDir, + path: &Utf8Path, +) -> io::Result<Utf8PathBuf> { use std::collections::hash_map::RandomState; use std::hash::{BuildHasher, Hasher}; @@ -229,9 +176,7 @@ return Ok(path.to_path_buf()); } - let mut to = Utf8PathBuf::from_path_buf(std::env::temp_dir()).unwrap(); - to.push("rust-analyzer-proc-macros"); - _ = fs::create_dir(&to); + let mut to = Utf8Path::from_path(temp_dir.path()).unwrap().to_owned(); let file_name = path.file_stem().ok_or_else(|| { io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}")) @@ -248,6 +193,60 @@ } #[cfg(unix)] -fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf> { +fn ensure_file_with_lock_free_access( + _temp_dir: &TempDir, + path: &Utf8Path, +) -> io::Result<Utf8PathBuf> { Ok(path.to_owned()) } + +/// Loads dynamic library in platform dependent manner. +/// +/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described +/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample) +/// and [here](https://github.com/rust-lang/rust/issues/60593). +/// +/// Usage of RTLD_DEEPBIND +/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1) +/// +/// It seems that on Windows that behaviour is default, so we do nothing in that case. +/// +/// # Safety +/// +/// The caller is responsible for ensuring that the path is valid proc-macro library +#[cfg(windows)] +unsafe fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> { + // SAFETY: The caller is responsible for ensuring that the path is valid proc-macro library + unsafe { Library::new(file) } +} + +/// Loads dynamic library in platform dependent manner. +/// +/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described +/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample) +/// and [here](https://github.com/rust-lang/rust/issues/60593). +/// +/// Usage of RTLD_DEEPBIND +/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1) +/// +/// It seems that on Windows that behaviour is default, so we do nothing in that case. +/// +/// # Safety +/// +/// The caller is responsible for ensuring that the path is valid proc-macro library +#[cfg(unix)] +unsafe fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> { + // not defined by POSIX, different values on mips vs other targets + #[cfg(target_env = "gnu")] + use libc::RTLD_DEEPBIND; + use libloading::os::unix::Library as UnixLibrary; + // defined by POSIX + use libloading::os::unix::RTLD_NOW; + + // MUSL and bionic don't have it.. + #[cfg(not(target_env = "gnu"))] + const RTLD_DEEPBIND: std::os::raw::c_int = 0x0; + + // SAFETY: The caller is responsible for ensuring that the path is valid proc-macro library + unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) } +}
diff --git a/crates/proc-macro-srv/src/proc_macros.rs b/crates/proc-macro-srv/src/dylib/proc_macros.rs similarity index 81% rename from crates/proc-macro-srv/src/proc_macros.rs rename to crates/proc-macro-srv/src/dylib/proc_macros.rs index 1853270..9b5721e 100644 --- a/crates/proc-macro-srv/src/proc_macros.rs +++ b/crates/proc-macro-srv/src/dylib/proc_macros.rs
@@ -75,20 +75,13 @@ Err(bridge::PanicMessage::String(format!("proc-macro `{macro_name}` is missing")).into()) } - pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - self.0 - .iter() - .map(|proc_macro| match proc_macro { - bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { - (trait_name.to_string(), ProcMacroKind::CustomDerive) - } - bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::Bang) - } - bridge::client::ProcMacro::Attr { name, .. } => { - (name.to_string(), ProcMacroKind::Attr) - } - }) - .collect() + pub(crate) fn list_macros(&self) -> impl Iterator<Item = (&str, ProcMacroKind)> { + self.0.iter().map(|proc_macro| match *proc_macro { + bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { + (trait_name, ProcMacroKind::CustomDerive) + } + bridge::client::ProcMacro::Bang { name, .. } => (name, ProcMacroKind::Bang), + bridge::client::ProcMacro::Attr { name, .. } => (name, ProcMacroKind::Attr), + }) } }
diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs index 223c5a5..cb97882 100644 --- a/crates/proc-macro-srv/src/lib.rs +++ b/crates/proc-macro-srv/src/lib.rs
@@ -27,7 +27,6 @@ extern crate rustc_lexer; mod dylib; -mod proc_macros; mod server_impl; use std::{ @@ -41,10 +40,13 @@ }; use paths::{Utf8Path, Utf8PathBuf}; -use span::{Span, TokenId}; +use span::Span; +use temp_dir::TempDir; use crate::server_impl::TokenStream; +pub use crate::server_impl::token_id::SpanId; + #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum ProcMacroKind { CustomDerive, @@ -57,11 +59,16 @@ pub struct ProcMacroSrv<'env> { expanders: Mutex<HashMap<Utf8PathBuf, Arc<dylib::Expander>>>, env: &'env EnvSnapshot, + temp_dir: TempDir, } impl<'env> ProcMacroSrv<'env> { pub fn new(env: &'env EnvSnapshot) -> Self { - Self { expanders: Default::default(), env } + Self { + expanders: Default::default(), + env, + temp_dir: TempDir::with_prefix("proc-macro-srv").unwrap(), + } } } @@ -71,18 +78,19 @@ pub fn expand<S: ProcMacroSrvSpan>( &self, lib: impl AsRef<Utf8Path>, - env: Vec<(String, String)>, + env: &[(String, String)], current_dir: Option<impl AsRef<Path>>, - macro_name: String, + macro_name: &str, macro_body: tt::TopSubtree<S>, attribute: Option<tt::TopSubtree<S>>, def_site: S, call_site: S, mixed_site: S, - ) -> Result<Vec<tt::TokenTree<S>>, String> { + ) -> Result<Vec<tt::TokenTree<S>>, PanicMessage> { let snapped_env = self.env; - let expander = - self.expander(lib.as_ref()).map_err(|err| format!("failed to load macro: {err}"))?; + let expander = self.expander(lib.as_ref()).map_err(|err| PanicMessage { + message: Some(format!("failed to load macro: {err}")), + })?; let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref)); @@ -91,11 +99,11 @@ let result = thread::scope(|s| { let thread = thread::Builder::new() .stack_size(EXPANDER_STACK_SIZE) - .name(macro_name.clone()) + .name(macro_name.to_owned()) .spawn_scoped(s, move || { expander .expand( - ¯o_name, + macro_name, server_impl::TopSubtree(macro_body.0.into_vec()), attribute.map(|it| server_impl::TopSubtree(it.0.into_vec())), def_site, @@ -104,12 +112,7 @@ ) .map(|tt| tt.0) }); - let res = match thread { - Ok(handle) => handle.join(), - Err(e) => return Err(e.to_string()), - }; - - match res { + match thread.unwrap().join() { Ok(res) => res, Err(e) => std::panic::resume_unwind(e), } @@ -124,12 +127,12 @@ dylib_path: &Utf8Path, ) -> Result<Vec<(String, ProcMacroKind)>, String> { let expander = self.expander(dylib_path)?; - Ok(expander.list_macros()) + Ok(expander.list_macros().map(|(k, v)| (k.to_owned(), v)).collect()) } fn expander(&self, path: &Utf8Path) -> Result<Arc<dylib::Expander>, String> { let expander = || { - let expander = dylib::Expander::new(path) + let expander = dylib::Expander::new(&self.temp_dir, path) .map_err(|err| format!("Cannot create expander for {path}: {err}",)); expander.map(Arc::new) }; @@ -159,8 +162,8 @@ fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server; } -impl ProcMacroSrvSpan for TokenId { - type Server = server_impl::token_id::TokenIdServer; +impl ProcMacroSrvSpan for SpanId { + type Server = server_impl::token_id::SpanIdServer; fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server { Self::Server { call_site, def_site, mixed_site } @@ -178,6 +181,8 @@ } } } + +#[derive(Debug, Clone)] pub struct PanicMessage { message: Option<String>, } @@ -201,7 +206,7 @@ static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); struct EnvChange<'snap> { - changed_vars: Vec<String>, + changed_vars: Vec<&'snap str>, prev_working_dir: Option<PathBuf>, snap: &'snap EnvSnapshot, _guard: std::sync::MutexGuard<'snap, ()>, @@ -210,7 +215,7 @@ impl<'snap> EnvChange<'snap> { fn apply( snap: &'snap EnvSnapshot, - new_vars: Vec<(String, String)>, + new_vars: &'snap [(String, String)], current_dir: Option<&Path>, ) -> EnvChange<'snap> { let guard = ENV_LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner); @@ -230,11 +235,11 @@ EnvChange { snap, changed_vars: new_vars - .into_iter() + .iter() .map(|(k, v)| { // SAFETY: We have acquired the environment lock - unsafe { env::set_var(&k, v) }; - k + unsafe { env::set_var(k, v) }; + &**k }) .collect(), prev_working_dir, @@ -257,14 +262,14 @@ } } - if let Some(dir) = &self.prev_working_dir { - if let Err(err) = std::env::set_current_dir(dir) { - eprintln!( - "Failed to set the current working dir to {}. Error: {:?}", - dir.display(), - err - ) - } + if let Some(dir) = &self.prev_working_dir + && let Err(err) = std::env::set_current_dir(dir) + { + eprintln!( + "Failed to set the current working dir to {}. Error: {:?}", + dir.display(), + err + ) } } }
diff --git a/crates/proc-macro-srv/src/server_impl.rs b/crates/proc-macro-srv/src/server_impl.rs index 662f625..32ad327 100644 --- a/crates/proc-macro-srv/src/server_impl.rs +++ b/crates/proc-macro-srv/src/server_impl.rs
@@ -209,7 +209,7 @@ token_trees.push(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { spacing: tt::Spacing::Alone, span: literal.span, - char: '-' as char, + char: '-', }))); symbol = Symbol::intern(&symbol.as_str()[1..]); }
diff --git a/crates/proc-macro-srv/src/server_impl/token_id.rs b/crates/proc-macro-srv/src/server_impl/token_id.rs index b493b32..91e70ea 100644 --- a/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/crates/proc-macro-srv/src/server_impl/token_id.rs
@@ -1,4 +1,4 @@ -//! proc-macro server backend based on [`proc_macro_api::msg::TokenId`] as the backing span. +//! proc-macro server backend based on [`proc_macro_api::msg::SpanId`] as the backing span. //! This backend is rather inflexible, used by RustRover and older rust-analyzer versions. use std::ops::{Bound, Range}; @@ -7,25 +7,34 @@ use crate::server_impl::{from_token_tree, literal_from_str, token_stream::TokenStreamBuilder}; -type Span = span::TokenId; +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct SpanId(pub u32); + +impl std::fmt::Debug for SpanId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +type Span = SpanId; type TokenStream = crate::server_impl::TokenStream<Span>; pub struct FreeFunctions; -pub struct TokenIdServer { +pub struct SpanIdServer { pub call_site: Span, pub def_site: Span, pub mixed_site: Span, } -impl server::Types for TokenIdServer { +impl server::Types for SpanIdServer { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; type Span = Span; type Symbol = Symbol; } -impl server::FreeFunctions for TokenIdServer { +impl server::FreeFunctions for SpanIdServer { fn injected_env_var(&mut self, _: &str) -> Option<std::string::String> { None } @@ -41,7 +50,7 @@ fn emit_diagnostic(&mut self, _: bridge::Diagnostic<Self::Span>) {} } -impl server::TokenStream for TokenIdServer { +impl server::TokenStream for SpanIdServer { fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { stream.is_empty() } @@ -102,12 +111,12 @@ &mut self, stream: Self::TokenStream, ) -> Vec<bridge::TokenTree<Self::TokenStream, Self::Span, Self::Symbol>> { - // Can't join with `TokenId`. + // Can't join with `SpanId`. stream.into_bridge(&mut |first, _second| first) } } -impl server::Span for TokenIdServer { +impl server::Span for SpanIdServer { fn debug(&mut self, span: Self::Span) -> String { format!("{:?}", span.0) } @@ -174,14 +183,14 @@ } } -impl server::Symbol for TokenIdServer { +impl server::Symbol for SpanIdServer { fn normalize_and_validate_ident(&mut self, string: &str) -> Result<Self::Symbol, ()> { // FIXME: nfc-normalize and validate idents Ok(<Self as server::Server>::intern_symbol(string)) } } -impl server::Server for TokenIdServer { +impl server::Server for SpanIdServer { fn globals(&mut self) -> bridge::ExpnGlobals<Self::Span> { bridge::ExpnGlobals { def_site: self.def_site, @@ -201,8 +210,6 @@ #[cfg(test)] mod tests { - use span::TokenId; - use super::*; #[test] @@ -211,18 +218,18 @@ token_trees: vec![ tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym: Symbol::intern("struct"), - span: TokenId(0), + span: SpanId(0), is_raw: tt::IdentIsRaw::No, })), tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym: Symbol::intern("T"), - span: TokenId(0), + span: SpanId(0), is_raw: tt::IdentIsRaw::No, })), tt::TokenTree::Subtree(tt::Subtree { delimiter: tt::Delimiter { - open: TokenId(0), - close: TokenId(0), + open: SpanId(0), + close: SpanId(0), kind: tt::DelimiterKind::Brace, }, len: 0, @@ -238,8 +245,8 @@ let subtree_paren_a = vec![ tt::TokenTree::Subtree(tt::Subtree { delimiter: tt::Delimiter { - open: TokenId(0), - close: TokenId(0), + open: SpanId(0), + close: SpanId(0), kind: tt::DelimiterKind::Parenthesis, }, len: 1, @@ -247,24 +254,24 @@ tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { is_raw: tt::IdentIsRaw::No, sym: Symbol::intern("a"), - span: TokenId(0), + span: SpanId(0), })), ]; - let t1 = TokenStream::from_str("(a)", TokenId(0)).unwrap(); + let t1 = TokenStream::from_str("(a)", SpanId(0)).unwrap(); assert_eq!(t1.token_trees.len(), 2); assert!(t1.token_trees[0..2] == subtree_paren_a); - let t2 = TokenStream::from_str("(a);", TokenId(0)).unwrap(); + let t2 = TokenStream::from_str("(a);", SpanId(0)).unwrap(); assert_eq!(t2.token_trees.len(), 3); assert!(t2.token_trees[0..2] == subtree_paren_a); - let underscore = TokenStream::from_str("_", TokenId(0)).unwrap(); + let underscore = TokenStream::from_str("_", SpanId(0)).unwrap(); assert!( underscore.token_trees[0] == tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym: Symbol::intern("_"), - span: TokenId(0), + span: SpanId(0), is_raw: tt::IdentIsRaw::No, })) );
diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs index 10af566..f5a76e3 100644 --- a/crates/proc-macro-srv/src/tests/utils.rs +++ b/crates/proc-macro-srv/src/tests/utils.rs
@@ -1,14 +1,12 @@ //! utils used in proc-macro tests use expect_test::Expect; -use span::{ - EditionedFileId, FileId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TokenId, -}; +use span::{EditionedFileId, FileId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext}; use tt::TextRange; -use crate::{EnvSnapshot, ProcMacroSrv, dylib, proc_macro_test_dylib_path}; +use crate::{EnvSnapshot, ProcMacroSrv, SpanId, dylib, proc_macro_test_dylib_path}; -fn parse_string(call_site: TokenId, src: &str) -> crate::server_impl::TokenStream<TokenId> { +fn parse_string(call_site: SpanId, src: &str) -> crate::server_impl::TokenStream<SpanId> { crate::server_impl::TokenStream::with_subtree(crate::server_impl::TopSubtree( syntax_bridge::parse_to_token_tree_static_span(span::Edition::CURRENT, call_site, src) .unwrap() @@ -57,11 +55,11 @@ expect_spanned: Expect, ) { let path = proc_macro_test_dylib_path(); - let expander = dylib::Expander::new(&path).unwrap(); + let expander = dylib::Expander::new(&temp_dir::TempDir::new().unwrap(), &path).unwrap(); - let def_site = TokenId(0); - let call_site = TokenId(1); - let mixed_site = TokenId(2); + let def_site = SpanId(0); + let call_site = SpanId(1); + let mixed_site = SpanId(2); let input_ts = parse_string(call_site, input).into_subtree(call_site); let attr_ts = attr.map(|attr| parse_string(call_site, attr).into_subtree(call_site)); let input_ts_string = format!("{input_ts:?}");
diff --git a/crates/profile/src/stop_watch.rs b/crates/profile/src/stop_watch.rs index 9f3e636..00c37c0 100644 --- a/crates/profile/src/stop_watch.rs +++ b/crates/profile/src/stop_watch.rs
@@ -37,10 +37,10 @@ .build() .map_err(|err| eprintln!("Failed to create perf counter: {err}")) .ok(); - if let Some(counter) = &mut counter { - if let Err(err) = counter.enable() { - eprintln!("Failed to start perf counter: {err}") - } + if let Some(counter) = &mut counter + && let Err(err) = counter.enable() + { + eprintln!("Failed to start perf counter: {err}") } counter } else {
diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml index 27fe9f7..0dbb309 100644 --- a/crates/project-model/Cargo.toml +++ b/crates/project-model/Cargo.toml
@@ -20,6 +20,7 @@ serde_json.workspace = true serde.workspace = true serde_derive.workspace = true +temp-dir.workspace = true tracing.workspace = true triomphe.workspace = true la-arena.workspace = true
diff --git a/crates/project-model/src/build_dependencies.rs b/crates/project-model/src/build_dependencies.rs index 499caa6..203173c 100644 --- a/crates/project-model/src/build_dependencies.rs +++ b/crates/project-model/src/build_dependencies.rs
@@ -16,11 +16,13 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize as _; +use stdx::never; use toolchain::Tool; use crate::{ CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath, Package, Sysroot, - TargetKind, utf8_stdout, + TargetKind, cargo_config_file::make_lockfile_copy, + cargo_workspace::MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH, utf8_stdout, }; /// Output of the build script and proc-macro building steps for a workspace. @@ -30,6 +32,15 @@ error: Option<String>, } +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub enum ProcMacroDylibPath { + Path(AbsPathBuf), + DylibNotFound, + NotProcMacro, + #[default] + NotBuilt, +} + /// Output of the build script and proc-macro building step for a concrete package. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub(crate) struct BuildScriptOutput { @@ -43,7 +54,7 @@ /// Directory where a build script might place its output. pub(crate) out_dir: Option<AbsPathBuf>, /// Path to the proc-macro library file if this package exposes proc-macros. - pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, + pub(crate) proc_macro_dylib_path: ProcMacroDylibPath, } impl BuildScriptOutput { @@ -51,7 +62,10 @@ self.cfgs.is_empty() && self.envs.is_empty() && self.out_dir.is_none() - && self.proc_macro_dylib_path.is_none() + && matches!( + self.proc_macro_dylib_path, + ProcMacroDylibPath::NotBuilt | ProcMacroDylibPath::NotProcMacro + ) } } @@ -67,7 +81,7 @@ let current_dir = workspace.workspace_root(); let allowed_features = workspace.workspace_features(); - let cmd = Self::build_command( + let (_guard, cmd) = Self::build_command( config, &allowed_features, workspace.manifest_path(), @@ -88,7 +102,7 @@ ) -> io::Result<Vec<WorkspaceBuildScripts>> { assert_eq!(config.invocation_strategy, InvocationStrategy::Once); - let cmd = Self::build_command( + let (_guard, cmd) = Self::build_command( config, &Default::default(), // This is not gonna be used anyways, so just construct a dummy here @@ -126,6 +140,8 @@ |package, cb| { if let Some(&(package, workspace)) = by_id.get(package) { cb(&workspaces[workspace][package].name, &mut res[workspace].outputs[package]); + } else { + never!("Received compiler message for unknown package: {}", package); } }, progress, @@ -140,12 +156,9 @@ if tracing::enabled!(tracing::Level::INFO) { for (idx, workspace) in workspaces.iter().enumerate() { for package in workspace.packages() { - let package_build_data = &mut res[idx].outputs[package]; + let package_build_data: &mut BuildScriptOutput = &mut res[idx].outputs[package]; if !package_build_data.is_empty() { - tracing::info!( - "{}: {package_build_data:?}", - workspace[package].manifest.parent(), - ); + tracing::info!("{}: {package_build_data:?}", workspace[package].manifest,); } } } @@ -198,10 +211,33 @@ let path = dir_entry.path(); let extension = path.extension()?; if extension == std::env::consts::DLL_EXTENSION { - let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned(); - let path = AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).ok()?) - .ok()?; - return Some((name, path)); + let name = path + .file_stem()? + .to_str()? + .split_once('-')? + .0 + .trim_start_matches("lib") + .to_owned(); + let path = match Utf8PathBuf::from_path_buf(path) { + Ok(path) => path, + Err(path) => { + tracing::warn!( + "Proc-macro dylib path contains non-UTF8 characters: {:?}", + path.display() + ); + return None; + } + }; + return match AbsPathBuf::try_from(path) { + Ok(path) => Some((name, path)), + Err(path) => { + tracing::error!( + "proc-macro dylib path is not absolute: {:?}", + path + ); + None + } + }; } } None @@ -209,28 +245,24 @@ .collect(); for p in rustc.packages() { let package = &rustc[p]; - if package - .targets - .iter() - .any(|&it| matches!(rustc[it].kind, TargetKind::Lib { is_proc_macro: true })) - { - if let Some((_, path)) = proc_macro_dylibs - .iter() - .find(|(name, _)| *name.trim_start_matches("lib") == package.name) - { - bs.outputs[p].proc_macro_dylib_path = Some(path.clone()); + bs.outputs[p].proc_macro_dylib_path = + if package.targets.iter().any(|&it| { + matches!(rustc[it].kind, TargetKind::Lib { is_proc_macro: true }) + }) { + match proc_macro_dylibs.iter().find(|(name, _)| *name == package.name) { + Some((_, path)) => ProcMacroDylibPath::Path(path.clone()), + _ => ProcMacroDylibPath::DylibNotFound, + } + } else { + ProcMacroDylibPath::NotProcMacro } - } } if tracing::enabled!(tracing::Level::INFO) { for package in rustc.packages() { let package_build_data = &bs.outputs[package]; if !package_build_data.is_empty() { - tracing::info!( - "{}: {package_build_data:?}", - rustc[package].manifest.parent(), - ); + tracing::info!("{}: {package_build_data:?}", rustc[package].manifest,); } } } @@ -263,6 +295,12 @@ |package, cb| { if let Some(&package) = by_id.get(package) { cb(&workspace[package].name, &mut outputs[package]); + } else { + never!( + "Received compiler message for unknown package: {}\n {}", + package, + by_id.keys().join(", ") + ); } }, progress, @@ -272,10 +310,7 @@ for package in workspace.packages() { let package_build_data = &outputs[package]; if !package_build_data.is_empty() { - tracing::info!( - "{}: {package_build_data:?}", - workspace[package].manifest.parent(), - ); + tracing::info!("{}: {package_build_data:?}", workspace[package].manifest,); } } } @@ -312,9 +347,7 @@ match message { Message::BuildScriptExecuted(mut message) => { with_output_for(&message.package_id.repr, &mut |name, data| { - progress(format!( - "building compile-time-deps: build script {name} run" - )); + progress(format!("build script {name} run")); let cfgs = { let mut acc = Vec::new(); for cfg in &message.cfgs { @@ -345,18 +378,24 @@ } Message::CompilerArtifact(message) => { with_output_for(&message.package_id.repr, &mut |name, data| { - progress(format!( - "building compile-time-deps: proc-macro {name} built" - )); - if message.target.kind.contains(&cargo_metadata::TargetKind::ProcMacro) + progress(format!("proc-macro {name} built")); + if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt { + data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro; + } + if !matches!(data.proc_macro_dylib_path, ProcMacroDylibPath::Path(_)) + && message + .target + .kind + .contains(&cargo_metadata::TargetKind::ProcMacro) { - // Skip rmeta file - if let Some(filename) = - message.filenames.iter().find(|file| is_dylib(file)) - { - let filename = AbsPath::assert(filename); - data.proc_macro_dylib_path = Some(filename.to_owned()); - } + data.proc_macro_dylib_path = + match message.filenames.iter().find(|file| is_dylib(file)) { + Some(filename) => { + let filename = AbsPath::assert(filename); + ProcMacroDylibPath::Path(filename.to_owned()) + } + None => ProcMacroDylibPath::DylibNotFound, + }; } }); } @@ -393,14 +432,15 @@ current_dir: &AbsPath, sysroot: &Sysroot, toolchain: Option<&semver::Version>, - ) -> io::Result<Command> { + ) -> io::Result<(Option<temp_dir::TempDir>, Command)> { match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = toolchain::command(program, current_dir, &config.extra_env); cmd.args(args); - Ok(cmd) + Ok((None, cmd)) } _ => { + let mut requires_unstable_options = false; let mut cmd = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env); cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); @@ -416,7 +456,19 @@ if let Some(target) = &config.target { cmd.args(["--target", target]); } - + let mut temp_dir_guard = None; + if toolchain + .is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH) + { + let lockfile_path = + <_ as AsRef<Utf8Path>>::as_ref(manifest_path).with_extension("lock"); + if let Some((temp_dir, target_lockfile)) = make_lockfile_copy(&lockfile_path) { + requires_unstable_options = true; + temp_dir_guard = Some(temp_dir); + cmd.arg("--lockfile-path"); + cmd.arg(target_lockfile.as_str()); + } + } match &config.features { CargoFeatures::All => { cmd.arg("--all-features"); @@ -438,6 +490,7 @@ } if manifest_path.is_rust_manifest() { + requires_unstable_options = true; cmd.arg("-Zscript"); } @@ -457,8 +510,7 @@ toolchain.is_some_and(|v| *v >= COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION); if cargo_comp_time_deps_available { - cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); - cmd.arg("-Zunstable-options"); + requires_unstable_options = true; cmd.arg("--compile-time-deps"); // we can pass this unconditionally, because we won't actually build the // binaries, and as such, this will succeed even on targets without libtest @@ -481,7 +533,11 @@ cmd.env("RA_RUSTC_WRAPPER", "1"); } } - Ok(cmd) + if requires_unstable_options { + cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); + cmd.arg("-Zunstable-options"); + } + Ok((temp_dir_guard, cmd)) } } }
diff --git a/crates/project-model/src/cargo_config_file.rs b/crates/project-model/src/cargo_config_file.rs index 7966f74..a1e7ed0 100644 --- a/crates/project-model/src/cargo_config_file.rs +++ b/crates/project-model/src/cargo_config_file.rs
@@ -1,4 +1,5 @@ //! Read `.cargo/config.toml` as a JSON object +use paths::{Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; use toolchain::Tool; @@ -32,3 +33,24 @@ Some(json) } + +pub(crate) fn make_lockfile_copy( + lockfile_path: &Utf8Path, +) -> Option<(temp_dir::TempDir, Utf8PathBuf)> { + let temp_dir = temp_dir::TempDir::with_prefix("rust-analyzer").ok()?; + let target_lockfile = temp_dir.path().join("Cargo.lock").try_into().ok()?; + match std::fs::copy(lockfile_path, &target_lockfile) { + Ok(_) => { + tracing::debug!("Copied lock file from `{}` to `{}`", lockfile_path, target_lockfile); + Some((temp_dir, target_lockfile)) + } + // lockfile does not yet exist, so we can just create a new one in the temp dir + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Some((temp_dir, target_lockfile)), + Err(e) => { + tracing::warn!( + "Failed to copy lock file from `{lockfile_path}` to `{target_lockfile}`: {e}", + ); + None + } + } +}
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index daadcd9..e613fd5 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs
@@ -15,16 +15,18 @@ use stdx::process::spawn_with_streaming_output; use toolchain::Tool; +use crate::cargo_config_file::make_lockfile_copy; use crate::{CfgOverrides, InvocationStrategy}; use crate::{ManifestPath, Sysroot}; -const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH: semver::Version = semver::Version { - major: 1, - minor: 82, - patch: 0, - pre: semver::Prerelease::EMPTY, - build: semver::BuildMetadata::EMPTY, -}; +pub(crate) const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH: semver::Version = + semver::Version { + major: 1, + minor: 82, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, + }; /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo /// workspace. It pretty closely mirrors `cargo metadata` output. @@ -245,7 +247,7 @@ } impl TargetKind { - fn new(kinds: &[cargo_metadata::TargetKind]) -> TargetKind { + pub fn new(kinds: &[cargo_metadata::TargetKind]) -> TargetKind { for kind in kinds { return match kind { cargo_metadata::TargetKind::Bin => TargetKind::Bin, @@ -552,7 +554,10 @@ pub(crate) struct FetchMetadata { command: cargo_metadata::MetadataCommand, + #[expect(dead_code)] + manifest_path: ManifestPath, lockfile_path: Option<Utf8PathBuf>, + #[expect(dead_code)] kind: &'static str, no_deps: bool, no_deps_result: anyhow::Result<cargo_metadata::Metadata>, @@ -596,25 +601,22 @@ } command.current_dir(current_dir); - let mut needs_nightly = false; let mut other_options = vec![]; // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually // the only relevant flags for metadata here are unstable ones, so we pass those along // but nothing else let mut extra_args = config.extra_args.iter(); while let Some(arg) = extra_args.next() { - if arg == "-Z" { - if let Some(arg) = extra_args.next() { - needs_nightly = true; - other_options.push("-Z".to_owned()); - other_options.push(arg.to_owned()); - } + if arg == "-Z" + && let Some(arg) = extra_args.next() + { + other_options.push("-Z".to_owned()); + other_options.push(arg.to_owned()); } } let mut lockfile_path = None; if cargo_toml.is_rust_manifest() { - needs_nightly = true; other_options.push("-Zscript".to_owned()); } else if config .toolchain_version @@ -632,10 +634,6 @@ command.other_options(other_options.clone()); - if needs_nightly { - command.env("RUSTC_BOOTSTRAP", "1"); - } - // Pre-fetch basic metadata using `--no-deps`, which: // - avoids fetching registries like crates.io, // - skips dependency resolution and does not modify lockfiles, @@ -655,7 +653,15 @@ } .with_context(|| format!("Failed to run `{cargo_command:?}`")); - Self { command, lockfile_path, kind: config.kind, no_deps, no_deps_result, other_options } + Self { + manifest_path: cargo_toml.clone(), + command, + lockfile_path, + kind: config.kind, + no_deps, + no_deps_result, + other_options, + } } pub(crate) fn no_deps_metadata(&self) -> Option<&cargo_metadata::Metadata> { @@ -672,40 +678,34 @@ locked: bool, progress: &dyn Fn(String), ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> { - let Self { mut command, lockfile_path, kind, no_deps, no_deps_result, mut other_options } = - self; + _ = target_dir; + let Self { + mut command, + manifest_path: _, + lockfile_path, + kind: _, + no_deps, + no_deps_result, + mut other_options, + } = self; if no_deps { return no_deps_result.map(|m| (m, None)); } let mut using_lockfile_copy = false; - // The manifest is a rust file, so this means its a script manifest - if let Some(lockfile) = lockfile_path { - let target_lockfile = - target_dir.join("rust-analyzer").join("metadata").join(kind).join("Cargo.lock"); - match std::fs::copy(&lockfile, &target_lockfile) { - Ok(_) => { - using_lockfile_copy = true; - other_options.push("--lockfile-path".to_owned()); - other_options.push(target_lockfile.to_string()); - } - Err(e) if e.kind() == std::io::ErrorKind::NotFound => { - // There exists no lockfile yet - using_lockfile_copy = true; - other_options.push("--lockfile-path".to_owned()); - other_options.push(target_lockfile.to_string()); - } - Err(e) => { - tracing::warn!( - "Failed to copy lock file from `{lockfile}` to `{target_lockfile}`: {e}", - ); - } - } + let mut _temp_dir_guard; + if let Some(lockfile) = lockfile_path + && let Some((temp_dir, target_lockfile)) = make_lockfile_copy(&lockfile) + { + _temp_dir_guard = temp_dir; + other_options.push("--lockfile-path".to_owned()); + other_options.push(target_lockfile.to_string()); + using_lockfile_copy = true; } - if using_lockfile_copy { + if using_lockfile_copy || other_options.iter().any(|it| it.starts_with("-Z")) { + command.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); other_options.push("-Zunstable-options".to_owned()); - command.env("RUSTC_BOOTSTRAP", "1"); } // No need to lock it if we copied the lockfile, we won't modify the original after all/ // This way cargo cannot error out on us if the lockfile requires updating. @@ -714,13 +714,11 @@ } command.other_options(other_options); - // FIXME: Fetching metadata is a slow process, as it might require - // calling crates.io. We should be reporting progress here, but it's - // unclear whether cargo itself supports it. progress("cargo metadata: started".to_owned()); let res = (|| -> anyhow::Result<(_, _)> { let mut errored = false; + tracing::debug!("Running `{:?}`", command.cargo_command()); let output = spawn_with_streaming_output(command.cargo_command(), &mut |_| (), &mut |line| { errored = errored || line.starts_with("error") || line.starts_with("warning");
diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 3bf3d06..d39781b 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs
@@ -59,7 +59,7 @@ use rustc_hash::FxHashSet; pub use crate::{ - build_dependencies::WorkspaceBuildScripts, + build_dependencies::{ProcMacroDylibPath, WorkspaceBuildScripts}, cargo_workspace::{ CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustLibSource, Target, TargetData, TargetKind, @@ -139,21 +139,22 @@ } fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<ManifestPath> { - if path.file_name().unwrap_or_default() == target_file_name { - if let Ok(manifest) = ManifestPath::try_from(path.to_path_buf()) { - return Some(manifest); - } + if path.file_name().unwrap_or_default() == target_file_name + && let Ok(manifest) = ManifestPath::try_from(path.to_path_buf()) + { + return Some(manifest); } let mut curr = Some(path); while let Some(path) = curr { let candidate = path.join(target_file_name); - if fs::metadata(&candidate).is_ok() { - if let Ok(manifest) = ManifestPath::try_from(candidate) { - return Some(manifest); - } + if fs::metadata(&candidate).is_ok() + && let Ok(manifest) = ManifestPath::try_from(candidate) + { + return Some(manifest); } + curr = path.parent(); }
diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 9781c46..c0a5009 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs
@@ -143,12 +143,11 @@ Some(root) => { // special case rustc, we can look that up directly in the sysroot's bin folder // as it should never invoke another cargo binary - if let Tool::Rustc = tool { - if let Some(path) = + if let Tool::Rustc = tool + && let Some(path) = probe_for_binary(root.join("bin").join(Tool::Rustc.name()).into()) - { - return toolchain::command(path, current_dir, envs); - } + { + return toolchain::command(path, current_dir, envs); } let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir, envs); @@ -291,29 +290,26 @@ pub fn set_workspace(&mut self, workspace: RustLibSrcWorkspace) { self.workspace = workspace; - if self.error.is_none() { - if let Some(src_root) = &self.rust_lib_src_root { - let has_core = match &self.workspace { - RustLibSrcWorkspace::Workspace(ws) => { - ws.packages().any(|p| ws[p].name == "core") - } - RustLibSrcWorkspace::Json(project_json) => project_json - .crates() - .filter_map(|(_, krate)| krate.display_name.clone()) - .any(|name| name.canonical_name().as_str() == "core"), - RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(), - RustLibSrcWorkspace::Empty => true, + if self.error.is_none() + && let Some(src_root) = &self.rust_lib_src_root + { + let has_core = match &self.workspace { + RustLibSrcWorkspace::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"), + RustLibSrcWorkspace::Json(project_json) => project_json + .crates() + .filter_map(|(_, krate)| krate.display_name.clone()) + .any(|name| name.canonical_name().as_str() == "core"), + RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(), + RustLibSrcWorkspace::Empty => true, + }; + if !has_core { + let var_note = if env::var_os("RUST_SRC_PATH").is_some() { + " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)" + } else { + ", try running `rustup component add rust-src` to possibly fix this" }; - if !has_core { - let var_note = if env::var_os("RUST_SRC_PATH").is_some() { - " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)" - } else { - ", try running `rustup component add rust-src` to possibly fix this" - }; - self.error = Some(format!( - "sysroot at `{src_root}` is missing a `core` library{var_note}", - )); - } + self.error = + Some(format!("sysroot at `{src_root}` is missing a `core` library{var_note}",)); } } }
diff --git a/crates/project-model/src/toolchain_info/rustc_cfg.rs b/crates/project-model/src/toolchain_info/rustc_cfg.rs index 6e06e88..ab69c8e 100644 --- a/crates/project-model/src/toolchain_info/rustc_cfg.rs +++ b/crates/project-model/src/toolchain_info/rustc_cfg.rs
@@ -65,6 +65,7 @@ let (sysroot, current_dir) = match config { QueryConfig::Cargo(sysroot, cargo_toml, _) => { let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env); + cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS); if let Some(target) = target { cmd.args(["--target", target]);
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 677f29e..00032bc 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs
@@ -24,7 +24,7 @@ CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package, ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, - build_dependencies::BuildScriptOutput, + build_dependencies::{BuildScriptOutput, ProcMacroDylibPath}, cargo_config_file, cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource}, env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, @@ -157,7 +157,6 @@ .field("file", &file) .field("cargo_script", &cargo_script.is_some()) .field("n_sysroot_crates", &sysroot.num_packages()) - .field("cargo_script", &cargo_script.is_some()) .field("n_rustc_cfg", &rustc_cfg.len()) .field("toolchain", &toolchain) .field("data_layout", &target_layout) @@ -424,12 +423,12 @@ sysroot.set_workspace(loaded_sysroot); } - if !cargo.requires_rustc_private() { - if let Err(e) = &mut rustc { - // We don't need the rustc sources here, - // so just discard the error. - _ = e.take(); - } + if !cargo.requires_rustc_private() + && let Err(e) = &mut rustc + { + // We don't need the rustc sources here, + // so just discard the error. + _ = e.take(); } Ok(ProjectWorkspace { @@ -1163,17 +1162,15 @@ crate = display_name.as_ref().map(|name| name.canonical_name().as_str()), "added root to crate graph" ); - if *is_proc_macro { - if let Some(path) = proc_macro_dylib_path.clone() { - let node = Ok(( - display_name - .as_ref() - .map(|it| it.canonical_name().as_str().to_owned()) - .unwrap_or_else(|| format!("crate{}", idx.0)), - path, - )); - proc_macros.insert(crate_graph_crate_id, node); - } + if *is_proc_macro && let Some(path) = proc_macro_dylib_path.clone() { + let node = Ok(( + display_name + .as_ref() + .map(|it| it.canonical_name().as_str().to_owned()) + .unwrap_or_else(|| format!("crate{}", idx.0)), + path, + )); + proc_macros.insert(crate_graph_crate_id, node); } (idx, crate_graph_crate_id) }, @@ -1318,16 +1315,17 @@ public_deps.add_to_crate_graph(crate_graph, from); // Add dep edge of all targets to the package's lib target - if let Some((to, name)) = lib_tgt.clone() { - if to != from && kind != TargetKind::BuildScript { - // (build script can not depend on its library target) + if let Some((to, name)) = lib_tgt.clone() + && to != from + && kind != TargetKind::BuildScript + { + // (build script can not depend on its library target) - // For root projects with dashes in their name, - // cargo metadata does not do any normalization, - // so we do it ourselves currently - let name = CrateName::normalize_dashes(&name); - add_dep(crate_graph, from, name, to); - } + // For root projects with dashes in their name, + // cargo metadata does not do any normalization, + // so we do it ourselves currently + let name = CrateName::normalize_dashes(&name); + add_dep(crate_graph, from, name, to); } } } @@ -1638,9 +1636,19 @@ let proc_macro = match build_data { Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => { match proc_macro_dylib_path { - Some(path) => Ok((cargo_name.to_owned(), path.clone())), - None if has_errors => Err(ProcMacroLoadingError::FailedToBuild), - None => Err(ProcMacroLoadingError::MissingDylibPath), + ProcMacroDylibPath::Path(path) => Ok((cargo_name.to_owned(), path.clone())), + ProcMacroDylibPath::NotBuilt => Err(ProcMacroLoadingError::NotYetBuilt), + ProcMacroDylibPath::NotProcMacro | ProcMacroDylibPath::DylibNotFound + if has_errors => + { + Err(ProcMacroLoadingError::FailedToBuild) + } + ProcMacroDylibPath::NotProcMacro => { + Err(ProcMacroLoadingError::ExpectedProcMacroArtifact) + } + ProcMacroDylibPath::DylibNotFound => { + Err(ProcMacroLoadingError::MissingDylibPath) + } } } None => Err(ProcMacroLoadingError::NotYetBuilt), @@ -1905,7 +1913,8 @@ meta.manifest_path(manifest); // `--no-deps` doesn't (over)write lockfiles as it doesn't do any package resolve. // So we can use it to get `target_directory` before copying lockfiles - let mut other_options = vec!["--no-deps".to_owned()]; + meta.no_deps(); + let mut other_options = vec![]; if manifest.is_rust_manifest() { meta.env("RUSTC_BOOTSTRAP", "1"); other_options.push("-Zscript".to_owned());
diff --git a/crates/query-group-macro/src/lib.rs b/crates/query-group-macro/src/lib.rs index ec4b6b2..277cc0b 100644 --- a/crates/query-group-macro/src/lib.rs +++ b/crates/query-group-macro/src/lib.rs
@@ -278,15 +278,15 @@ return Err(syn::Error::new(signature.span(), "Queries must have a return type")); }; - if let syn::Type::Path(ref ty_path) = *return_ty { - if matches!(query_kind, QueryKind::Input) { - let field = InputStructField { - name: method_name.to_token_stream(), - ty: ty_path.path.to_token_stream(), - }; + if let syn::Type::Path(ref ty_path) = *return_ty + && matches!(query_kind, QueryKind::Input) + { + let field = InputStructField { + name: method_name.to_token_stream(), + ty: ty_path.path.to_token_stream(), + }; - input_struct_fields.push(field); - } + input_struct_fields.push(field); } if let Some(block) = &mut method.default {
diff --git a/crates/query-group-macro/src/queries.rs b/crates/query-group-macro/src/queries.rs index c151cca..22a26c4 100644 --- a/crates/query-group-macro/src/queries.rs +++ b/crates/query-group-macro/src/queries.rs
@@ -48,7 +48,8 @@ quote!(#(#options),*) }) .into_iter() - .chain(self.lru.map(|lru| quote!(lru = #lru))); + .chain(self.lru.map(|lru| quote!(lru = #lru))) + .chain(Some(quote!(unsafe(non_update_return_type)))); let annotation = quote!(#[salsa_macros::tracked( #(#options),* )]); let pat_and_tys = &self.pat_and_tys;
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 4dba97c..ab045e0 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs
@@ -122,12 +122,12 @@ // directory which we set to the project workspace. // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/general-environment-variables // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-syminitialize - if let Ok(path) = env::current_exe() { - if let Some(path) = path.parent() { - // SAFETY: This is safe because this is single-threaded. - unsafe { - env::set_var("_NT_SYMBOL_PATH", path); - } + if let Ok(path) = env::current_exe() + && let Some(path) = path.parent() + { + // SAFETY: This is safe because this is single-threaded. + unsafe { + env::set_var("_NT_SYMBOL_PATH", path); } } }
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 4f75d14..9788684 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -136,34 +136,30 @@ for source_root_id in source_roots { let source_root = db.source_root(source_root_id).source_root(db); for file_id in source_root.iter() { - if let Some(p) = source_root.path_for_file(&file_id) { - if let Some((_, Some("rs"))) = p.name_and_extension() { - // measure workspace/project code - if !source_root.is_library || self.with_deps { - let length = db.file_text(file_id).text(db).lines().count(); - let item_stats = db - .file_item_tree( - EditionedFileId::current_edition(db, file_id).into(), - ) - .item_tree_stats() - .into(); + if let Some(p) = source_root.path_for_file(&file_id) + && let Some((_, Some("rs"))) = p.name_and_extension() + { + // measure workspace/project code + if !source_root.is_library || self.with_deps { + let length = db.file_text(file_id).text(db).lines().count(); + let item_stats = db + .file_item_tree(EditionedFileId::current_edition(db, file_id).into()) + .item_tree_stats() + .into(); - workspace_loc += length; - workspace_item_trees += 1; - workspace_item_stats += item_stats; - } else { - let length = db.file_text(file_id).text(db).lines().count(); - let item_stats = db - .file_item_tree( - EditionedFileId::current_edition(db, file_id).into(), - ) - .item_tree_stats() - .into(); + workspace_loc += length; + workspace_item_trees += 1; + workspace_item_stats += item_stats; + } else { + let length = db.file_text(file_id).text(db).lines().count(); + let item_stats = db + .file_item_tree(EditionedFileId::current_edition(db, file_id).into()) + .item_tree_stats() + .into(); - dep_loc += length; - dep_item_trees += 1; - dep_item_stats += item_stats; - } + dep_loc += length; + dep_item_trees += 1; + dep_item_stats += item_stats; } } } @@ -560,29 +556,35 @@ std::fs::write(path, txt).unwrap(); let res = ws.run_build_scripts(&cargo_config, &|_| ()).unwrap(); - if let Some(err) = res.error() { - if err.contains("error: could not compile") { - if let Some(mut err_idx) = err.find("error[E") { - err_idx += 7; - let err_code = &err[err_idx..err_idx + 4]; - match err_code { - "0282" | "0283" => continue, // Byproduct of testing method - "0277" | "0308" if generated.contains(&todo) => continue, // See https://github.com/rust-lang/rust/issues/69882 - // FIXME: In some rare cases `AssocItem::container_or_implemented_trait` returns `None` for trait methods. - // Generated code is valid in case traits are imported - "0599" if err.contains("the following trait is implemented but not in scope") => continue, - _ => (), + if let Some(err) = res.error() + && err.contains("error: could not compile") + { + if let Some(mut err_idx) = err.find("error[E") { + err_idx += 7; + let err_code = &err[err_idx..err_idx + 4]; + match err_code { + "0282" | "0283" => continue, // Byproduct of testing method + "0277" | "0308" if generated.contains(&todo) => continue, // See https://github.com/rust-lang/rust/issues/69882 + // FIXME: In some rare cases `AssocItem::container_or_implemented_trait` returns `None` for trait methods. + // Generated code is valid in case traits are imported + "0599" + if err.contains( + "the following trait is implemented but not in scope", + ) => + { + continue; } - bar.println(err); - bar.println(generated); - acc.error_codes - .entry(err_code.to_owned()) - .and_modify(|n| *n += 1) - .or_insert(1); - } else { - acc.syntax_errors += 1; - bar.println(format!("Syntax error: \n{err}")); + _ => (), } + bar.println(err); + bar.println(generated); + acc.error_codes + .entry(err_code.to_owned()) + .and_modify(|n| *n += 1) + .or_insert(1); + } else { + acc.syntax_errors += 1; + bar.println(format!("Syntax error: \n{err}")); } } } @@ -731,12 +733,11 @@ let name = body_id.name(db).unwrap_or_else(Name::missing); let module = body_id.module(db); let display_target = module.krate().to_display_target(db); - if let Some(only_name) = self.only.as_deref() { - if name.display(db, Edition::LATEST).to_string() != only_name - && full_name(db, body_id, module) != only_name - { - continue; - } + if let Some(only_name) = self.only.as_deref() + && name.display(db, Edition::LATEST).to_string() != only_name + && full_name(db, body_id, module) != only_name + { + continue; } let msg = move || { if verbosity.is_verbose() {
diff --git a/crates/rust-analyzer/src/cli/progress_report.rs b/crates/rust-analyzer/src/cli/progress_report.rs index 1b9b870..0283113 100644 --- a/crates/rust-analyzer/src/cli/progress_report.rs +++ b/crates/rust-analyzer/src/cli/progress_report.rs
@@ -83,11 +83,11 @@ output.extend(text.chars().skip(common_prefix_length)); // If the new text is shorter than the old one: delete overlapping characters - if let Some(overlap_count) = self.text.len().checked_sub(text.len()) { - if overlap_count > 0 { - output += &" ".repeat(overlap_count); - output += &"\x08".repeat(overlap_count); - } + if let Some(overlap_count) = self.text.len().checked_sub(text.len()) + && overlap_count > 0 + { + output += &" ".repeat(overlap_count); + output += &"\x08".repeat(overlap_count); } let _ = io::stdout().write(output.as_bytes());
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index 30ac93f..36ae98b 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -305,10 +305,10 @@ for i in walk_dir { let i = i?; let p = i.into_path(); - if let Some(f) = &self.filter { - if !p.as_os_str().to_string_lossy().contains(f) { - continue; - } + if let Some(f) = &self.filter + && !p.as_os_str().to_string_lossy().contains(f) + { + continue; } if p.extension().is_none_or(|x| x != "rs") { continue;
diff --git a/crates/rust-analyzer/src/cli/symbols.rs b/crates/rust-analyzer/src/cli/symbols.rs index 9fad672..d7af56d 100644 --- a/crates/rust-analyzer/src/cli/symbols.rs +++ b/crates/rust-analyzer/src/cli/symbols.rs
@@ -1,5 +1,5 @@ //! Read Rust code on stdin, print syntax tree on stdout. -use ide::Analysis; +use ide::{Analysis, FileStructureConfig}; use crate::cli::{flags, read_stdin}; @@ -7,7 +7,12 @@ pub fn run(self) -> anyhow::Result<()> { let text = read_stdin()?; let (analysis, file_id) = Analysis::from_single_file(text); - let structure = analysis.file_structure(file_id).unwrap(); + 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 + // local variables, but here we include them deliberately. + .file_structure(&FileStructureConfig { exclude_locals: false }, file_id) + .unwrap(); for s in structure { println!("{s:?}"); }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 9456fd8..d4cd56d 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs
@@ -61,233 +61,312 @@ Limit(usize), } -// Defines the server-side configuration of the rust-analyzer. We generate -// *parts* of VS Code's `package.json` config from this. Run `cargo test` to -// re-generate that file. +// Defines the server-side configuration of the rust-analyzer. We generate *parts* of VS Code's +// `package.json` config from this. Run `cargo test` to re-generate that file. // -// However, editor specific config, which the server doesn't know about, should -// be specified directly in `package.json`. +// However, editor specific config, which the server doesn't know about, should be specified +// directly in `package.json`. // -// To deprecate an option by replacing it with another name use `new_name` | `old_name` so that we keep -// parsing the old name. +// To deprecate an option by replacing it with another name use `new_name` | `old_name` so that we +// keep parsing the old name. config_data! { - /// Configs that apply on a workspace-wide scope. There are 2 levels on which a global configuration can be configured + /// Configs that apply on a workspace-wide scope. There are 2 levels on which a global + /// configuration can be configured /// - /// 1. `rust-analyzer.toml` file under user's config directory (e.g ~/.config/rust-analyzer/rust-analyzer.toml) + /// 1. `rust-analyzer.toml` file under user's config directory (e.g + /// ~/.config/rust-analyzer/rust-analyzer.toml) /// 2. Client's own configurations (e.g `settings.json` on VS Code) /// - /// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle. + /// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen + /// by the nearest first principle. global: struct GlobalDefaultConfigData <- GlobalConfigInput -> { /// Warm up caches on project load. cachePriming_enable: bool = true, - /// How many worker threads to handle priming caches. The default `0` means to pick automatically. + + /// How many worker threads to handle priming caches. The default `0` means to pick + /// automatically. cachePriming_numThreads: NumThreads = NumThreads::Physical, /// Custom completion snippets. - completion_snippets_custom: FxIndexMap<String, SnippetDef> = Config::completion_snippets_default(), + completion_snippets_custom: FxIndexMap<String, SnippetDef> = + Config::completion_snippets_default(), - - /// These paths (file/directories) will be ignored by rust-analyzer. They are - /// relative to the workspace root, and globs are not supported. You may - /// also need to add the folders to Code's `files.watcherExclude`. + /// List of files to ignore + /// + /// These paths (file/directories) will be ignored by rust-analyzer. They are relative to + /// the workspace root, and globs are not supported. You may also need to add the folders to + /// Code's `files.watcherExclude`. files_exclude | files_excludeDirs: Vec<Utf8PathBuf> = vec![], - - - /// Enables highlighting of related return values while the cursor is on any `match`, `if`, or match arm arrow (`=>`). + /// Highlight related return values while the cursor is on any `match`, `if`, or match arm + /// arrow (`=>`). highlightRelated_branchExitPoints_enable: bool = true, - /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. + + /// Highlight related references while the cursor is on `break`, `loop`, `while`, or `for` + /// keywords. highlightRelated_breakPoints_enable: bool = true, - /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. + + /// Highlight all captures of a closure while the cursor is on the `|` or move keyword of a closure. highlightRelated_closureCaptures_enable: bool = true, - /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). + + /// Highlight all exit points while the cursor is on any `return`, `?`, `fn`, or return type + /// arrow (`->`). highlightRelated_exitPoints_enable: bool = true, - /// Enables highlighting of related references while the cursor is on any identifier. + + /// Highlight related references while the cursor is on any identifier. highlightRelated_references_enable: bool = true, - /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. + + /// Highlight all break points for a loop or block context while the cursor is on any + /// `async` or `await` keywords. highlightRelated_yieldPoints_enable: bool = true, - /// Whether to show `Debug` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_debug_enable: bool = true, - /// Whether to show HoverActions in Rust files. - hover_actions_enable: bool = true, - /// Whether to show `Go to Type Definition` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_gotoTypeDef_enable: bool = true, - /// Whether to show `Implementations` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_implementations_enable: bool = true, - /// Whether to show `References` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_references_enable: bool = false, - /// Whether to show `Run` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_run_enable: bool = true, - /// Whether to show `Update Test` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. - hover_actions_updateTest_enable: bool = true, + /// Show `Debug` action. Only applies when `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_debug_enable: bool = true, - /// Whether to show documentation on hover. - hover_documentation_enable: bool = true, - /// Whether to show keyword hover popups. Only applies when + /// Show HoverActions in Rust files. + hover_actions_enable: bool = true, + + /// Show `Go to Type Definition` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_gotoTypeDef_enable: bool = true, + + /// Show `Implementations` action. Only applies when `#rust-analyzer.hover.actions.enable#` + /// is set. + hover_actions_implementations_enable: bool = true, + + /// Show `References` action. Only applies when `#rust-analyzer.hover.actions.enable#` is + /// set. + hover_actions_references_enable: bool = false, + + /// Show `Run` action. Only applies when `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_run_enable: bool = true, + + /// Show `Update Test` action. Only applies when `#rust-analyzer.hover.actions.enable#` and + /// `#rust-analyzer.hover.actions.run.enable#` are set. + hover_actions_updateTest_enable: bool = true, + + /// Show documentation on hover. + hover_documentation_enable: bool = true, + + /// Show keyword hover popups. Only applies when /// `#rust-analyzer.hover.documentation.enable#` is set. - hover_documentation_keywords_enable: bool = true, - /// Whether to show drop glue information on hover. - hover_dropGlue_enable: bool = true, + hover_documentation_keywords_enable: bool = true, + + /// Show drop glue information on hover. + hover_dropGlue_enable: bool = true, + /// Use markdown syntax for links on hover. hover_links_enable: bool = true, - /// Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. + + /// Show what types are used as generic arguments in calls etc. on hover, and limit the max + /// length to show such types, beyond which they will be shown with ellipsis. /// - /// This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. + /// This can take three values: `null` means "unlimited", the string `"hide"` means to not + /// show generic substitutions at all, and a number means to limit them to X characters. /// /// The default is 20 characters. - hover_maxSubstitutionLength: Option<MaxSubstitutionLength> = Some(MaxSubstitutionLength::Limit(20)), + hover_maxSubstitutionLength: Option<MaxSubstitutionLength> = + Some(MaxSubstitutionLength::Limit(20)), + /// How to render the align information in a memory layout hover. - hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), - /// Whether to show memory layout data on hover. + hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = + Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + + /// Show memory layout data on hover. hover_memoryLayout_enable: bool = true, + /// How to render the niche information in a memory layout hover. hover_memoryLayout_niches: Option<bool> = Some(false), + /// How to render the offset information in a memory layout hover. - hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = + Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + /// How to render the padding information in a memory layout hover. hover_memoryLayout_padding: Option<MemoryLayoutHoverRenderKindDef> = None, + /// How to render the size information in a memory layout hover. - hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Both), + hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = + Some(MemoryLayoutHoverRenderKindDef::Both), /// How many variants of an enum to display when hovering on. Show none if empty. hover_show_enumVariants: Option<usize> = Some(5), - /// How many fields of a struct, variant or union to display when hovering on. Show none if empty. + + /// How many fields of a struct, variant or union to display when hovering on. Show none if + /// empty. hover_show_fields: Option<usize> = Some(5), + /// How many associated items of a trait to display when hovering a trait. hover_show_traitAssocItems: Option<usize> = None, - /// Whether to show inlay type hints for binding modes. - inlayHints_bindingModeHints_enable: bool = false, - /// Whether to show inlay type hints for method chains. - inlayHints_chainingHints_enable: bool = true, - /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to. - inlayHints_closingBraceHints_enable: bool = true, + /// Show inlay type hints for binding modes. + inlayHints_bindingModeHints_enable: bool = false, + + /// Show inlay type hints for method chains. + inlayHints_chainingHints_enable: bool = true, + + /// Show inlay hints after a closing `}` to indicate what item it belongs to. + inlayHints_closingBraceHints_enable: bool = true, + /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 /// to always show them). - inlayHints_closingBraceHints_minLines: usize = 25, - /// Whether to show inlay hints for closure captures. - inlayHints_closureCaptureHints_enable: bool = false, - /// Whether to show inlay type hints for return types of closures. - inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = ClosureReturnTypeHintsDef::Never, - /// Closure notation in type and chaining inlay hints. - inlayHints_closureStyle: ClosureStyle = ClosureStyle::ImplFn, - /// Whether to show enum variant discriminant hints. - inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never, - /// Whether to show inlay hints for type adjustments. - inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never, - /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. - inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false, - /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). - inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix, - /// Whether to show const generic parameter name inlay hints. - inlayHints_genericParameterHints_const_enable: bool= true, - /// Whether to show generic lifetime parameter name inlay hints. - inlayHints_genericParameterHints_lifetime_enable: bool = false, - /// Whether to show generic type parameter name inlay hints. - inlayHints_genericParameterHints_type_enable: bool = false, - /// Whether to show implicit drop hints. - inlayHints_implicitDrops_enable: bool = false, - /// Whether to show inlay hints for the implied type parameter `Sized` bound. - inlayHints_implicitSizedBoundHints_enable: bool = false, - /// Whether to show inlay type hints for elided lifetimes in function signatures. - inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never, - /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. - inlayHints_lifetimeElisionHints_useParameterNames: bool = false, - /// Maximum length for inlay hints. Set to null to have an unlimited length. - inlayHints_maxLength: Option<usize> = Some(25), - /// Whether to show function parameter name inlay hints at the call - /// site. - inlayHints_parameterHints_enable: bool = true, - /// Whether to show exclusive range inlay hints. - inlayHints_rangeExclusiveHints_enable: bool = false, - /// Whether to show inlay hints for compiler inserted reborrows. - /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. - inlayHints_reborrowHints_enable: ReborrowHintsDef = ReborrowHintsDef::Never, - /// Whether to render leading colons for type hints, and trailing colons for parameter hints. - inlayHints_renderColons: bool = true, - /// Whether to show inlay type hints for variables. - inlayHints_typeHints_enable: bool = true, - /// Whether to hide inlay type hints for `let` statements that initialize to a closure. - /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. - inlayHints_typeHints_hideClosureInitialization: bool = false, - /// Whether to hide inlay parameter type hints for closures. - inlayHints_typeHints_hideClosureParameter:bool = false, - /// Whether to hide inlay type hints for constructors. - inlayHints_typeHints_hideNamedConstructor: bool = false, + inlayHints_closingBraceHints_minLines: usize = 25, - /// Enables the experimental support for interpreting tests. + /// Show inlay hints for closure captures. + inlayHints_closureCaptureHints_enable: bool = false, + + /// Show inlay type hints for return types of closures. + inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = + ClosureReturnTypeHintsDef::Never, + + /// Closure notation in type and chaining inlay hints. + inlayHints_closureStyle: ClosureStyle = ClosureStyle::ImplFn, + + /// Show enum variant discriminant hints. + inlayHints_discriminantHints_enable: DiscriminantHintsDef = + DiscriminantHintsDef::Never, + + /// Show inlay hints for type adjustments. + inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = + AdjustmentHintsDef::Never, + + /// Hide inlay hints for type adjustments outside of `unsafe` blocks. + inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false, + + /// Show inlay hints as postfix ops (`.*` instead of `*`, etc). + inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = + AdjustmentHintsModeDef::Prefix, + + /// Show const generic parameter name inlay hints. + inlayHints_genericParameterHints_const_enable: bool = true, + + /// Show generic lifetime parameter name inlay hints. + inlayHints_genericParameterHints_lifetime_enable: bool = false, + + /// Show generic type parameter name inlay hints. + inlayHints_genericParameterHints_type_enable: bool = false, + + /// Show implicit drop hints. + inlayHints_implicitDrops_enable: bool = false, + + /// Show inlay hints for the implied type parameter `Sized` bound. + inlayHints_implicitSizedBoundHints_enable: bool = false, + + /// Show inlay type hints for elided lifetimes in function signatures. + inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never, + + /// Prefer using parameter names as the name for elided lifetime hints if possible. + inlayHints_lifetimeElisionHints_useParameterNames: bool = false, + + /// Maximum length for inlay hints. Set to null to have an unlimited length. + inlayHints_maxLength: Option<usize> = Some(25), + + /// Show function parameter name inlay hints at the call site. + inlayHints_parameterHints_enable: bool = true, + + /// Show exclusive range inlay hints. + inlayHints_rangeExclusiveHints_enable: bool = false, + + /// Show inlay hints for compiler inserted reborrows. + /// + /// This setting is deprecated in favor of + /// #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. + inlayHints_reborrowHints_enable: ReborrowHintsDef = ReborrowHintsDef::Never, + + /// Whether to render leading colons for type hints, and trailing colons for parameter hints. + inlayHints_renderColons: bool = true, + + /// Show inlay type hints for variables. + inlayHints_typeHints_enable: bool = true, + + /// Hide inlay type hints for `let` statements that initialize to a closure. + /// + /// Only applies to closures with blocks, same as + /// `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. + inlayHints_typeHints_hideClosureInitialization: bool = false, + + /// Hide inlay parameter type hints for closures. + inlayHints_typeHints_hideClosureParameter: bool = false, + + /// Hide inlay type hints for constructors. + inlayHints_typeHints_hideNamedConstructor: bool = false, + + /// Enable the experimental support for interpreting tests. interpret_tests: bool = false, /// Join lines merges consecutive declaration and initialization of an assignment. joinLines_joinAssignments: bool = true, + /// Join lines inserts else between consecutive ifs. joinLines_joinElseIf: bool = true, + /// Join lines removes trailing commas. joinLines_removeTrailingComma: bool = true, + /// Join lines unwraps trivial blocks. joinLines_unwrapTrivialBlock: bool = true, - /// Whether to show `Debug` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_debug_enable: bool = true, - /// Whether to show CodeLens in Rust files. - lens_enable: bool = true, - /// Whether to show `Implementations` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_implementations_enable: bool = true, + /// Show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set. + lens_debug_enable: bool = true, + + /// Show CodeLens in Rust files. + lens_enable: bool = true, + + /// Show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set. + lens_implementations_enable: bool = true, + /// Where to render annotations. lens_location: AnnotationLocation = AnnotationLocation::AboveName, - /// Whether to show `References` lens for Struct, Enum, and Union. - /// Only applies when `#rust-analyzer.lens.enable#` is set. + + /// Show `References` lens for Struct, Enum, and Union. Only applies when + /// `#rust-analyzer.lens.enable#` is set. lens_references_adt_enable: bool = false, - /// Whether to show `References` lens for Enum Variants. - /// Only applies when `#rust-analyzer.lens.enable#` is set. + + /// Show `References` lens for Enum Variants. Only applies when + /// `#rust-analyzer.lens.enable#` is set. lens_references_enumVariant_enable: bool = false, - /// Whether to show `Method References` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. + + /// Show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. lens_references_method_enable: bool = false, - /// Whether to show `References` lens for Trait. - /// Only applies when `#rust-analyzer.lens.enable#` is set. + + /// Show `References` lens for Trait. Only applies when `#rust-analyzer.lens.enable#` is + /// set. lens_references_trait_enable: bool = false, - /// Whether to show `Run` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_run_enable: bool = true, - /// Whether to show `Update Test` lens. Only applies when - /// `#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. + + /// Show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set. + lens_run_enable: bool = true, + + /// Show `Update Test` lens. Only applies when `#rust-analyzer.lens.enable#` and + /// `#rust-analyzer.lens.run.enable#` are set. lens_updateTest_enable: bool = true, - /// Disable project auto-discovery in favor of explicitly specified set - /// of projects. + /// Disable project auto-discovery in favor of explicitly specified set of projects. /// - /// Elements must be paths pointing to `Cargo.toml`, - /// `rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON - /// objects in `rust-project.json` format. + /// Elements must be paths pointing to `Cargo.toml`, `rust-project.json`, `.rs` files (which + /// will be treated as standalone files) or JSON objects in `rust-project.json` format. linkedProjects: Vec<ManifestOrProjectJson> = vec![], /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. - lru_capacity: Option<u16> = None, - /// Sets the LRU capacity of the specified queries. + lru_capacity: Option<u16> = None, + + /// The LRU capacity of the specified queries. lru_query_capacities: FxHashMap<Box<str>, u16> = FxHashMap::default(), - /// Whether to show `can't find Cargo.toml` error message. - notifications_cargoTomlNotFound: bool = true, + /// Show `can't find Cargo.toml` error message. + notifications_cargoTomlNotFound: bool = true, - /// How many worker threads in the main loop. The default `null` means to pick automatically. + /// The number of worker threads in the main loop. The default `null` means to pick + /// automatically. numThreads: Option<NumThreads> = None, /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. procMacro_attributes_enable: bool = true, + /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. - procMacro_enable: bool = true, + procMacro_enable: bool = true, + /// Internal config, path to proc-macro server executable. - procMacro_server: Option<Utf8PathBuf> = None, + procMacro_server: Option<Utf8PathBuf> = None, /// Exclude imports from find-all-references. references_excludeImports: bool = false, @@ -300,31 +379,41 @@ /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra /// doc links. semanticHighlighting_doc_comment_inject_enable: bool = true, - /// Whether the server is allowed to emit non-standard tokens and modifiers. + + /// Emit non-standard tokens and modifiers + /// + /// When enabled, rust-analyzer will emit tokens and modifiers that are not part of the + /// standard set of semantic tokens. semanticHighlighting_nonStandardTokens: bool = true, + /// Use semantic tokens for operators. /// /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when /// they are tagged with modifiers. semanticHighlighting_operator_enable: bool = true, + /// Use specialized semantic tokens for operators. /// /// When enabled, rust-analyzer will emit special token types for operator tokens instead /// of the generic `operator` token type. semanticHighlighting_operator_specialization_enable: bool = false, + /// Use semantic tokens for punctuation. /// /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when /// they are tagged with modifiers or have a special role. semanticHighlighting_punctuation_enable: bool = false, + /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro /// calls. semanticHighlighting_punctuation_separate_macro_bang: bool = false, + /// Use specialized semantic tokens for punctuation. /// /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead /// of the generic `punctuation` token type. semanticHighlighting_punctuation_specialization_enable: bool = false, + /// Use semantic tokens for strings. /// /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars. @@ -333,16 +422,21 @@ semanticHighlighting_strings_enable: bool = true, /// Show full signature of the callable. Only shows parameters if disabled. - signatureInfo_detail: SignatureDetail = SignatureDetail::Full, + signatureInfo_detail: SignatureDetail = SignatureDetail::Full, + /// Show documentation. - signatureInfo_documentation_enable: bool = true, + signatureInfo_documentation_enable: bool = true, /// Specify the characters allowed to invoke special on typing triggers. - /// - typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression + /// + /// - typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing + /// expression /// - typing `=` between two expressions adds `;` when in statement position - /// - typing `=` to turn an assignment into an equality comparison removes `;` when in expression position + /// - typing `=` to turn an assignment into an equality comparison removes `;` when in + /// expression position /// - typing `.` in a chain method call auto-indents - /// - typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression + /// - typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the + /// expression /// - typing `{` in a use item adds a closing `}` in the right place /// - typing `>` to complete a return type `->` will insert a whitespace after it /// - typing `<` in a path or type position inserts a closing `>` after the path or type. @@ -374,8 +468,8 @@ /// /// **Warning**: This format is provisional and subject to change. /// - /// [`DiscoverWorkspaceConfig::command`] *must* return a JSON object - /// corresponding to `DiscoverProjectData::Finished`: + /// [`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to + /// `DiscoverProjectData::Finished`: /// /// ```norun /// #[derive(Debug, Clone, Deserialize, Serialize)] @@ -405,12 +499,11 @@ /// } /// ``` /// - /// It is encouraged, but not required, to use the other variants on - /// `DiscoverProjectData` to provide a more polished end-user experience. + /// It is encouraged, but not required, to use the other variants on `DiscoverProjectData` + /// to provide a more polished end-user experience. /// - /// `DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, - /// which will be substituted with the JSON-serialized form of the following - /// enum: + /// `DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be + /// substituted with the JSON-serialized form of the following enum: /// /// ```norun /// #[derive(PartialEq, Clone, Debug, Serialize)] @@ -437,11 +530,10 @@ /// } /// ``` /// - /// `DiscoverArgument::Path` is used to find and generate a `rust-project.json`, - /// and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to - /// to update an existing workspace. As a reference for implementors, - /// buck2's `rust-project` will likely be useful: - /// https://github.com/facebook/buck2/tree/main/integrations/rust-project. + /// `DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and + /// therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an + /// existing workspace. As a reference for implementors, buck2's `rust-project` will likely + /// be useful: https://github.com/facebook/buck2/tree/main/integrations/rust-project. workspace_discoverConfig: Option<DiscoverWorkspaceConfig> = None, } } @@ -449,109 +541,154 @@ config_data! { /// Local configurations can be defined per `SourceRoot`. This almost always corresponds to a `Crate`. local: struct LocalDefaultConfigData <- LocalConfigInput -> { - /// Whether to insert #[must_use] when generating `as_` methods - /// for enum variants. - assist_emitMustUse: bool = false, + /// Insert #[must_use] when generating `as_` methods for enum variants. + assist_emitMustUse: bool = false, + /// Placeholder expression to use for missing expressions in assists. - assist_expressionFillDefault: ExprFillDefaultDef = ExprFillDefaultDef::Todo, - /// When inserting a type (e.g. in "fill match arms" assist), prefer to use `Self` over the type name where possible. + assist_expressionFillDefault: ExprFillDefaultDef = ExprFillDefaultDef::Todo, + + /// Prefer to use `Self` over the type name when inserting a type (e.g. in "fill match arms" assist). assist_preferSelf: bool = false, - /// Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check. + + /// Enable borrow checking for term search code assists. If set to false, also there will be + /// more suggestions, but some of them may not borrow-check. assist_termSearch_borrowcheck: bool = true, + /// Term search fuel in "units of work" for assists (Defaults to 1800). assist_termSearch_fuel: usize = 1800, - - /// Whether to automatically add a semicolon when completing unit-returning functions. + /// Automatically add a semicolon when completing unit-returning functions. /// /// In `match` arms it completes a comma instead. completion_addSemicolonToUnit: bool = true, - /// Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. - completion_autoAwait_enable: bool = true, - /// Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. - completion_autoIter_enable: bool = true, - /// Toggles the additional completions that automatically add imports when completed. - /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. - completion_autoimport_enable: bool = true, + + /// Show method calls and field accesses completions with `await` prefixed to them when + /// completing on a future. + completion_autoAwait_enable: bool = true, + + /// Show method call completions with `iter()` or `into_iter()` prefixed to them when + /// completing on a type that has them. + completion_autoIter_enable: bool = true, + + /// Show completions that automatically add imports when completed. + /// + /// Note that your client must specify the `additionalTextEdits` LSP client capability to + /// truly have this feature enabled. + completion_autoimport_enable: bool = true, + /// A list of full paths to items to exclude from auto-importing completions. /// /// Traits in this list won't have their methods suggested in completions unless the trait /// is in scope. /// - /// You can either specify a string path which defaults to type "always" or use the more verbose - /// form `{ "path": "path::to::item", type: "always" }`. + /// You can either specify a string path which defaults to type "always" or use the more + /// verbose form `{ "path": "path::to::item", type: "always" }`. /// - /// For traits the type "methods" can be used to only exclude the methods but not the trait itself. + /// For traits the type "methods" can be used to only exclude the methods but not the trait + /// itself. /// /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`. completion_autoimport_exclude: Vec<AutoImportExclusion> = vec![ AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods }, AutoImportExclusion::Verbose { path: "core::borrow::BorrowMut".to_owned(), r#type: AutoImportExclusionType::Methods }, ], - /// Toggles the additional completions that automatically show method calls and field accesses - /// with `self` prefixed to them when inside a method. - completion_autoself_enable: bool = true, - /// Whether to add parenthesis and argument snippets when completing function. - completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, + + /// Show method calls and field access completions with `self` prefixed to them when + /// inside a method. + completion_autoself_enable: bool = true, + + /// Add parenthesis and argument snippets when completing function. + completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, + /// A list of full paths to traits whose methods to exclude from completion. /// - /// Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. + /// Methods from these traits won't be completed, even if the trait is in scope. However, + /// they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or + /// `T where T: Trait`. /// /// Note that the trait themselves can still be completed. completion_excludeTraits: Vec<String> = Vec::new(), - /// Whether to show full function/method signatures in completion docs. + + /// Show full function / method signatures in completion docs. completion_fullFunctionSignatures_enable: bool = false, - /// Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. + + /// Omit deprecated items from completions. By default they are marked as deprecated but not + /// hidden. completion_hideDeprecated: bool = false, + /// Maximum number of completions to return. If `None`, the limit is infinite. completion_limit: Option<usize> = None, - /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. - completion_postfix_enable: bool = true, - /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. + + /// Show postfix snippets like `dbg`, `if`, `not`, etc. + completion_postfix_enable: bool = true, + + /// Show completions of private items and fields that are defined in the current workspace + /// even if they are not visible at the current position. completion_privateEditable_enable: bool = false, - /// Whether to enable term search based snippets like `Some(foo.bar().baz())`. + + /// Enable term search based snippets like `Some(foo.bar().baz())`. completion_termSearch_enable: bool = false, + /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). completion_termSearch_fuel: usize = 1000, /// List of rust-analyzer diagnostics to disable. diagnostics_disabled: FxHashSet<String> = FxHashSet::default(), - /// Whether to show native rust-analyzer diagnostics. - diagnostics_enable: bool = true, - /// Whether to show experimental rust-analyzer diagnostics that might - /// have more false positives than usual. - diagnostics_experimental_enable: bool = false, - /// Map of prefixes to be substituted when parsing diagnostic file paths. - /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. + + /// Show native rust-analyzer diagnostics. + diagnostics_enable: bool = true, + + /// Show experimental rust-analyzer diagnostics that might have more false positives than + /// usual. + diagnostics_experimental_enable: bool = false, + + /// Map of prefixes to be substituted when parsing diagnostic file paths. This should be the + /// reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. diagnostics_remapPrefix: FxHashMap<String, String> = FxHashMap::default(), - /// Whether to run additional style lints. - diagnostics_styleLints_enable: bool = false, + + /// Run additional style lints. + diagnostics_styleLints_enable: bool = false, + /// List of warnings that should be displayed with hint severity. /// - /// The warnings will be indicated by faded text or three dots in code - /// and will not show up in the `Problems Panel`. + /// The warnings will be indicated by faded text or three dots in code and will not show up + /// in the `Problems Panel`. diagnostics_warningsAsHint: Vec<String> = vec![], + /// List of warnings that should be displayed with info severity. /// - /// The warnings will be indicated by a blue squiggly underline in code - /// and a blue icon in the `Problems Panel`. + /// The warnings will be indicated by a blue squiggly underline in code and a blue icon in + /// the `Problems Panel`. diagnostics_warningsAsInfo: Vec<String> = vec![], - /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. - imports_granularity_enforce: bool = false, + /// Enforce the import granularity setting for all files. If set to false rust-analyzer will + /// try to keep import styles consistent per file. + imports_granularity_enforce: bool = false, + /// How imports should be grouped into use statements. - imports_granularity_group: ImportGranularityDef = ImportGranularityDef::Crate, - /// Group inserted imports by the [following order](https://rust-analyzer.github.io/book/features.html#auto-import). Groups are separated by newlines. - imports_group_enable: bool = true, - /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. - imports_merge_glob: bool = true, + imports_granularity_group: ImportGranularityDef = ImportGranularityDef::Crate, + + /// Group inserted imports by the [following + /// order](https://rust-analyzer.github.io/book/features.html#auto-import). Groups are + /// separated by newlines. + imports_group_enable: bool = true, + + /// Allow import insertion to merge new imports into single path glob imports like `use + /// std::fmt::*;`. + imports_merge_glob: bool = true, + /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate. imports_preferNoStd | imports_prefer_no_std: bool = false, - /// Whether to prefer import paths containing a `prelude` module. - imports_preferPrelude: bool = false, + + /// Prefer import paths containing a `prelude` module. + imports_preferPrelude: bool = false, + /// The path structure for newly inserted paths to use. - imports_prefix: ImportPrefixDef = ImportPrefixDef::ByCrate, - /// Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". + imports_prefix: ImportPrefixDef = ImportPrefixDef::ByCrate, + + /// Prefix external (including std, core) crate imports with `::`. + /// + /// E.g. `use ::std::io::Read;`. imports_prefixExternPrelude: bool = false, } } @@ -589,7 +726,9 @@ /// ```bash /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going /// ``` - /// . + /// + /// Note: The option must be specified as an array of command line arguments, with + /// the first argument being the name of the command to run. cargo_buildScripts_overrideCommand: Option<Vec<String>> = None, /// Rerun proc-macros building/build-scripts running when proc-macro /// or build-script sources change and are saved. @@ -703,7 +842,9 @@ /// ```bash /// cargo check --workspace --message-format=json --all-targets /// ``` - /// . + /// + /// Note: The option must be specified as an array of command line arguments, with + /// the first argument being the name of the command to run. check_overrideCommand | checkOnSave_overrideCommand: Option<Vec<String>> = None, /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. /// @@ -717,6 +858,9 @@ /// check will be performed. check_workspace: bool = true, + /// Exclude all locals from document symbol search. + document_symbol_search_excludeLocals: bool = true, + /// These proc-macros will be ignored when trying to expand them. /// /// This config takes a map of crate names with the exported proc-macro names to ignore as values. @@ -753,6 +897,9 @@ /// not that of `cargo fmt`. The file contents will be passed on the /// standard input and the formatted result will be read from the /// standard output. + /// + /// Note: The option must be specified as an array of command line arguments, with + /// the first argument being the name of the command to run. rustfmt_overrideCommand: Option<Vec<String>> = None, /// Enables the use of rustfmt's unstable range formatting command for the /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only @@ -1337,6 +1484,13 @@ Server, } +/// Configuration for document symbol search requests. +#[derive(Debug, Clone)] +pub struct DocumentSymbolConfig { + /// Should locals be excluded. + pub search_exclude_locals: bool, +} + #[derive(Debug, Clone)] pub struct NotificationsConfig { pub cargo_toml_not_found: bool, @@ -2294,6 +2448,12 @@ } } + pub fn document_symbol(&self, source_root: Option<SourceRootId>) -> DocumentSymbolConfig { + DocumentSymbolConfig { + search_exclude_locals: *self.document_symbol_search_excludeLocals(source_root), + } + } + pub fn workspace_symbol(&self, source_root: Option<SourceRootId>) -> WorkspaceSymbolConfig { WorkspaceSymbolConfig { search_exclude_imports: *self.workspace_symbol_search_excludeImports(source_root), @@ -2923,7 +3083,7 @@ }) => { /// Default config values for this grouping. #[allow(non_snake_case)] - #[derive(Debug, Clone )] + #[derive(Debug, Clone)] struct $name { $($field: $ty,)* } impl_for_config_data!{ @@ -3201,8 +3361,10 @@ .iter() .map(|(field, ty, doc, default)| { let name = field.replace('_', "."); - let category = - name.find('.').map(|end| String::from(&name[..end])).unwrap_or("general".into()); + let category = name + .split_once(".") + .map(|(category, _name)| to_title_case(category)) + .unwrap_or("rust-analyzer".into()); let name = format!("rust-analyzer.{name}"); let props = field_props(field, ty, doc, default); serde_json::json!({ @@ -3216,6 +3378,29 @@ map.into() } +/// Translate a field name to a title case string suitable for use in the category names on the +/// vscode settings page. +/// +/// First letter of word should be uppercase, if an uppercase letter is encountered, add a space +/// before it e.g. "fooBar" -> "Foo Bar", "fooBarBaz" -> "Foo Bar Baz", "foo" -> "Foo" +/// +/// This likely should be in stdx (or just use heck instead), but it doesn't handle any edge cases +/// and is intentionally simple. +fn to_title_case(s: &str) -> String { + let mut result = String::with_capacity(s.len()); + let mut chars = s.chars(); + if let Some(first) = chars.next() { + result.push(first.to_ascii_uppercase()); + for c in chars { + if c.is_uppercase() { + result.push(' '); + } + result.push(c); + } + } + result +} + fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value { let doc = doc_comment_to_string(doc); let doc = doc.trim_end_matches('\n'); @@ -3742,17 +3927,16 @@ for idx in url_offsets { let link = &schema[idx..]; // matching on whitespace to ignore normal links - if let Some(link_end) = link.find([' ', '[']) { - if link.chars().nth(link_end) == Some('[') { - if let Some(link_text_end) = link.find(']') { - let link_text = link[link_end..(link_text_end + 1)].to_string(); + if let Some(link_end) = link.find([' ', '[']) + && link.chars().nth(link_end) == Some('[') + && let Some(link_text_end) = link.find(']') + { + let link_text = link[link_end..(link_text_end + 1)].to_string(); - schema.replace_range((idx + link_end)..(idx + link_text_end + 1), ""); - schema.insert(idx, '('); - schema.insert(idx + link_end + 1, ')'); - schema.insert_str(idx, &link_text); - } - } + schema.replace_range((idx + link_end)..(idx + link_text_end + 1), ""); + schema.insert(idx, '('); + schema.insert(idx + link_end + 1, ')'); + schema.insert_str(idx, &link_text); } }
diff --git a/crates/rust-analyzer/src/config/patch_old_style.rs b/crates/rust-analyzer/src/config/patch_old_style.rs index 95857dd..389bb78 100644 --- a/crates/rust-analyzer/src/config/patch_old_style.rs +++ b/crates/rust-analyzer/src/config/patch_old_style.rs
@@ -73,19 +73,19 @@ } // completion.snippets -> completion.snippets.custom; - if let Some(Value::Object(obj)) = copy.pointer("/completion/snippets").cloned() { - if obj.len() != 1 || obj.get("custom").is_none() { - merge( - json, - json! {{ - "completion": { - "snippets": { - "custom": obj - }, + if let Some(Value::Object(obj)) = copy.pointer("/completion/snippets").cloned() + && (obj.len() != 1 || obj.get("custom").is_none()) + { + merge( + json, + json! {{ + "completion": { + "snippets": { + "custom": obj }, - }}, - ); - } + }, + }}, + ); } // callInfo_full -> signatureInfo_detail, signatureInfo_documentation_enable
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 438a2a0..30f5301 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -26,6 +26,19 @@ pub(crate) type DiagnosticsGeneration = usize; +#[derive(Debug, Clone)] +pub(crate) struct WorkspaceFlycheckDiagnostic { + pub(crate) generation: DiagnosticsGeneration, + pub(crate) per_package: + FxHashMap<Option<Arc<PackageId>>, FxHashMap<FileId, Vec<lsp_types::Diagnostic>>>, +} + +impl WorkspaceFlycheckDiagnostic { + fn new(generation: DiagnosticsGeneration) -> Self { + WorkspaceFlycheckDiagnostic { generation, per_package: Default::default() } + } +} + #[derive(Debug, Default, Clone)] pub(crate) struct DiagnosticCollection { // FIXME: should be FxHashMap<FileId, Vec<ra_id::Diagnostic>> @@ -33,9 +46,7 @@ FxHashMap<FileId, (DiagnosticsGeneration, Vec<lsp_types::Diagnostic>)>, pub(crate) native_semantic: FxHashMap<FileId, (DiagnosticsGeneration, Vec<lsp_types::Diagnostic>)>, - // FIXME: should be Vec<flycheck::Diagnostic> - pub(crate) check: - Vec<FxHashMap<Option<Arc<PackageId>>, FxHashMap<FileId, Vec<lsp_types::Diagnostic>>>>, + pub(crate) check: Vec<WorkspaceFlycheckDiagnostic>, pub(crate) check_fixes: CheckFixes, changes: FxHashSet<FileId>, /// Counter for supplying a new generation number for diagnostics. @@ -57,7 +68,7 @@ let Some(check) = self.check.get_mut(flycheck_id) else { return; }; - self.changes.extend(check.drain().flat_map(|(_, v)| v.into_keys())); + self.changes.extend(check.per_package.drain().flat_map(|(_, v)| v.into_keys())); if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { fixes.clear(); } @@ -66,7 +77,9 @@ pub(crate) fn clear_check_all(&mut self) { Arc::make_mut(&mut self.check_fixes).clear(); self.changes.extend( - self.check.iter_mut().flat_map(|it| it.drain().flat_map(|(_, v)| v.into_keys())), + self.check + .iter_mut() + .flat_map(|it| it.per_package.drain().flat_map(|(_, v)| v.into_keys())), ) } @@ -79,7 +92,7 @@ return; }; let package_id = Some(package_id); - if let Some(checks) = check.remove(&package_id) { + if let Some(checks) = check.per_package.remove(&package_id) { self.changes.extend(checks.into_keys()); } if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { @@ -87,6 +100,16 @@ } } + pub(crate) fn clear_check_older_than( + &mut self, + flycheck_id: usize, + generation: DiagnosticsGeneration, + ) { + if self.check.get(flycheck_id).is_some_and(|it| it.generation < generation) { + self.clear_check(flycheck_id); + } + } + pub(crate) fn clear_native_for(&mut self, file_id: FileId) { self.native_syntax.remove(&file_id); self.native_semantic.remove(&file_id); @@ -96,15 +119,24 @@ pub(crate) fn add_check_diagnostic( &mut self, flycheck_id: usize, + generation: DiagnosticsGeneration, package_id: &Option<Arc<PackageId>>, file_id: FileId, diagnostic: lsp_types::Diagnostic, fix: Option<Box<Fix>>, ) { if self.check.len() <= flycheck_id { - self.check.resize_with(flycheck_id + 1, Default::default); + self.check + .resize_with(flycheck_id + 1, || WorkspaceFlycheckDiagnostic::new(generation)); } + + // Getting message from old generation. Might happen in restarting checks. + if self.check[flycheck_id].generation > generation { + return; + } + self.check[flycheck_id].generation = generation; let diagnostics = self.check[flycheck_id] + .per_package .entry(package_id.clone()) .or_default() .entry(file_id) @@ -177,7 +209,7 @@ let check = self .check .iter() - .flat_map(|it| it.values()) + .flat_map(|it| it.per_package.values()) .filter_map(move |it| it.get(&file_id)) .flatten(); native_syntax.chain(native_semantic).chain(check)
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 79d8f67..3f64628 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -298,10 +298,10 @@ let mut source = String::from("rustc"); let mut code = rd.code.as_ref().map(|c| c.code.clone()); - if let Some(code_val) = &code { - if config.check_ignore.contains(code_val) { - return Vec::new(); - } + if let Some(code_val) = &code + && config.check_ignore.contains(code_val) + { + return Vec::new(); } if let Some(code_val) = &code { @@ -373,10 +373,8 @@ let primary_location = primary_location(config, workspace_root, primary_span, snap); let message = { let mut message = message.clone(); - if needs_primary_span_label { - if let Some(primary_span_label) = &primary_span.label { - format_to!(message, "\n{}", primary_span_label); - } + if needs_primary_span_label && let Some(primary_span_label) = &primary_span.label { + format_to!(message, "\n{}", primary_span_label); } message };
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index bec57a4..233bede 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs
@@ -1,7 +1,12 @@ //! Flycheck provides the functionality needed to run `cargo check` to provide //! LSP diagnostics based on the output of the command. -use std::{fmt, io, process::Command, time::Duration}; +use std::{ + fmt, io, + process::Command, + sync::atomic::{AtomicUsize, Ordering}, + time::Duration, +}; use cargo_metadata::PackageId; use crossbeam_channel::{Receiver, Sender, select_biased, unbounded}; @@ -18,7 +23,10 @@ use toolchain::Tool; use triomphe::Arc; -use crate::command::{CargoParser, CommandHandle}; +use crate::{ + command::{CargoParser, CommandHandle}, + diagnostics::DiagnosticsGeneration, +}; #[derive(Clone, Debug, Default, PartialEq, Eq)] pub(crate) enum InvocationStrategy { @@ -111,7 +119,18 @@ match self { FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {command}"), FlycheckConfig::CustomCommand { command, args, .. } => { - write!(f, "{command} {}", args.join(" ")) + // Don't show `my_custom_check --foo $saved_file` literally to the user, as it + // looks like we've forgotten to substitute $saved_file. + // + // Instead, show `my_custom_check --foo ...`. The + // actual path is often too long to be worth showing + // in the IDE (e.g. in the VS Code status bar). + let display_args = args + .iter() + .map(|arg| if arg == SAVED_FILE_PLACEHOLDER { "..." } else { arg }) + .collect::<Vec<_>>(); + + write!(f, "{command} {}", display_args.join(" ")) } } } @@ -127,36 +146,54 @@ sender: Sender<StateChange>, _thread: stdx::thread::JoinHandle, id: usize, + generation: AtomicUsize, } impl FlycheckHandle { pub(crate) fn spawn( id: usize, + generation: DiagnosticsGeneration, sender: Sender<FlycheckMessage>, config: FlycheckConfig, sysroot_root: Option<AbsPathBuf>, workspace_root: AbsPathBuf, manifest_path: Option<AbsPathBuf>, ) -> FlycheckHandle { - let actor = - FlycheckActor::new(id, sender, config, sysroot_root, workspace_root, manifest_path); + let actor = FlycheckActor::new( + id, + generation, + sender, + config, + sysroot_root, + workspace_root, + manifest_path, + ); let (sender, receiver) = unbounded::<StateChange>(); let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker, format!("Flycheck{id}")) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); - FlycheckHandle { id, sender, _thread: thread } + FlycheckHandle { id, generation: generation.into(), sender, _thread: thread } } /// Schedule a re-start of the cargo check worker to do a workspace wide check. pub(crate) fn restart_workspace(&self, saved_file: Option<AbsPathBuf>) { - self.sender.send(StateChange::Restart { package: None, saved_file, target: None }).unwrap(); + let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; + self.sender + .send(StateChange::Restart { generation, package: None, saved_file, target: None }) + .unwrap(); } /// Schedule a re-start of the cargo check worker to do a package wide check. pub(crate) fn restart_for_package(&self, package: String, target: Option<Target>) { + let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; self.sender - .send(StateChange::Restart { package: Some(package), saved_file: None, target }) + .send(StateChange::Restart { + generation, + package: Some(package), + saved_file: None, + target, + }) .unwrap(); } @@ -168,23 +205,31 @@ pub(crate) fn id(&self) -> usize { self.id } + + pub(crate) fn generation(&self) -> DiagnosticsGeneration { + self.generation.load(Ordering::Relaxed) + } +} + +#[derive(Debug)] +pub(crate) enum ClearDiagnosticsKind { + All, + OlderThan(DiagnosticsGeneration), + Package(Arc<PackageId>), } pub(crate) enum FlycheckMessage { /// Request adding a diagnostic with fixes included to a file AddDiagnostic { id: usize, + generation: DiagnosticsGeneration, workspace_root: Arc<AbsPathBuf>, diagnostic: Diagnostic, package_id: Option<Arc<PackageId>>, }, /// Request clearing all outdated diagnostics. - ClearDiagnostics { - id: usize, - /// The package whose diagnostics to clear, or if unspecified, all diagnostics. - package_id: Option<Arc<PackageId>>, - }, + ClearDiagnostics { id: usize, kind: ClearDiagnosticsKind }, /// Request check progress notification to client Progress { @@ -197,18 +242,23 @@ impl fmt::Debug for FlycheckMessage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic, package_id } => f + FlycheckMessage::AddDiagnostic { + id, + generation, + workspace_root, + diagnostic, + package_id, + } => f .debug_struct("AddDiagnostic") .field("id", id) + .field("generation", generation) .field("workspace_root", workspace_root) .field("package_id", package_id) .field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code)) .finish(), - FlycheckMessage::ClearDiagnostics { id, package_id } => f - .debug_struct("ClearDiagnostics") - .field("id", id) - .field("package_id", package_id) - .finish(), + FlycheckMessage::ClearDiagnostics { id, kind } => { + f.debug_struct("ClearDiagnostics").field("id", id).field("kind", kind).finish() + } FlycheckMessage::Progress { id, progress } => { f.debug_struct("Progress").field("id", id).field("progress", progress).finish() } @@ -226,7 +276,12 @@ } enum StateChange { - Restart { package: Option<String>, saved_file: Option<AbsPathBuf>, target: Option<Target> }, + Restart { + generation: DiagnosticsGeneration, + package: Option<String>, + saved_file: Option<AbsPathBuf>, + target: Option<Target>, + }, Cancel, } @@ -235,6 +290,7 @@ /// The workspace id of this flycheck instance. id: usize, + generation: DiagnosticsGeneration, sender: Sender<FlycheckMessage>, config: FlycheckConfig, manifest_path: Option<AbsPathBuf>, @@ -272,6 +328,7 @@ impl FlycheckActor { fn new( id: usize, + generation: DiagnosticsGeneration, sender: Sender<FlycheckMessage>, config: FlycheckConfig, sysroot_root: Option<AbsPathBuf>, @@ -281,6 +338,7 @@ tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { id, + generation, sender, config, sysroot_root, @@ -316,7 +374,12 @@ tracing::debug!(flycheck_id = self.id, "flycheck cancelled"); self.cancel_check_process(); } - Event::RequestStateChange(StateChange::Restart { package, saved_file, target }) => { + Event::RequestStateChange(StateChange::Restart { + generation, + package, + saved_file, + target, + }) => { // Cancel the previously spawned process self.cancel_check_process(); while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) { @@ -326,6 +389,8 @@ } } + self.generation = generation; + let Some(command) = self.check_command(package.as_deref(), saved_file.as_deref(), target) else { @@ -372,7 +437,16 @@ // Clear everything for good measure self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: None, + kind: ClearDiagnosticsKind::All, + }); + } else if res.is_ok() { + // We clear diagnostics for packages on + // `[CargoCheckMessage::CompilerArtifact]` but there seem to be setups where + // cargo may not report an artifact to our runner at all. To handle such + // cases, clear stale diagnostics when flycheck completes successfully. + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::OlderThan(self.generation), }); } self.clear_diagnostics_state(); @@ -401,7 +475,7 @@ ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: Some(package_id), + kind: ClearDiagnosticsKind::Package(package_id), }); } } @@ -424,7 +498,7 @@ ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: Some(package_id.clone()), + kind: ClearDiagnosticsKind::Package(package_id.clone()), }); } } else if self.diagnostics_received @@ -433,11 +507,12 @@ self.diagnostics_received = DiagnosticsReceived::YesAndClearedForAll; self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: None, + kind: ClearDiagnosticsKind::All, }); } self.send(FlycheckMessage::AddDiagnostic { id: self.id, + generation: self.generation, package_id, workspace_root: self.root.clone(), diagnostic, @@ -481,12 +556,11 @@ FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { let mut cmd = toolchain::command(Tool::Cargo.path(), &*self.root, &options.extra_env); - if let Some(sysroot_root) = &self.sysroot_root { - if !options.extra_env.contains_key("RUSTUP_TOOLCHAIN") - && std::env::var_os("RUSTUP_TOOLCHAIN").is_none() - { - cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root)); - } + if let Some(sysroot_root) = &self.sysroot_root + && !options.extra_env.contains_key("RUSTUP_TOOLCHAIN") + && std::env::var_os("RUSTUP_TOOLCHAIN").is_none() + { + cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root)); } cmd.arg(command);
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 62a28a1..2f1afba 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs
@@ -448,7 +448,7 @@ tracing::info!(%vfs_path, ?change_kind, "Processing rust-analyzer.toml changes"); if vfs_path.as_path() == user_config_abs_path { tracing::info!(%vfs_path, ?change_kind, "Use config rust-analyzer.toml changes"); - change.change_user_config(Some(db.file_text(file_id).text(db))); + change.change_user_config(Some(db.file_text(file_id).text(db).clone())); } // If change has been made to a ratoml file that @@ -462,14 +462,14 @@ change.change_workspace_ratoml( source_root_id, vfs_path.clone(), - Some(db.file_text(file_id).text(db)), + Some(db.file_text(file_id).text(db).clone()), ) } else { tracing::info!(%vfs_path, ?source_root_id, "crate rust-analyzer.toml changes"); change.change_ratoml( source_root_id, vfs_path.clone(), - Some(db.file_text(file_id).text(db)), + Some(db.file_text(file_id).text(db).clone()), ) }; @@ -591,10 +591,10 @@ pub(crate) fn respond(&mut self, response: lsp_server::Response) { if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) { - if let Some(err) = &response.error { - if err.message.starts_with("server panicked") { - self.poke_rust_analyzer_developer(format!("{}, check the log", err.message)); - } + if let Some(err) = &response.error + && err.message.starts_with("server panicked") + { + self.poke_rust_analyzer_developer(format!("{}, check the log", err.message)); } let duration = start.elapsed(); @@ -663,18 +663,18 @@ pub(crate) fn check_workspaces_msrv(&self) -> impl Iterator<Item = String> + '_ { self.workspaces.iter().filter_map(|ws| { - if let Some(toolchain) = &ws.toolchain { - if *toolchain < crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION { - return Some(format!( - "Workspace `{}` is using an outdated toolchain version `{}` but \ + if let Some(toolchain) = &ws.toolchain + && *toolchain < crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION + { + return Some(format!( + "Workspace `{}` is using an outdated toolchain version `{}` but \ rust-analyzer only supports `{}` and higher.\n\ Consider using the rust-analyzer rustup component for your toolchain or upgrade your toolchain to a supported version.\n\n", - ws.manifest_or_root(), - toolchain, - crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION, - )); - } + ws.manifest_or_root(), + toolchain, + crate::MINIMUM_SUPPORTED_TOOLCHAIN_VERSION, + )); } None })
diff --git a/crates/rust-analyzer/src/handlers/dispatch.rs b/crates/rust-analyzer/src/handlers/dispatch.rs index aea116e..b25245d 100644 --- a/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -433,10 +433,10 @@ } pub(crate) fn finish(&mut self) { - if let Some(not) = &self.not { - if !not.method.starts_with("$/") { - tracing::error!("unhandled notification: {:?}", not); - } + if let Some(not) = &self.not + && !not.method.starts_with("$/") + { + tracing::error!("unhandled notification: {:?}", not); } } }
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 200e972..e193ff7 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -39,14 +39,12 @@ state: &mut GlobalState, params: WorkDoneProgressCancelParams, ) -> anyhow::Result<()> { - if let lsp_types::NumberOrString::String(s) = ¶ms.token { - if let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") { - if let Ok(id) = id.parse::<u32>() { - if let Some(flycheck) = state.flycheck.get(id as usize) { - flycheck.cancel(); - } - } - } + if let lsp_types::NumberOrString::String(s) = ¶ms.token + && let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") + && let Ok(id) = id.parse::<u32>() + && let Some(flycheck) = state.flycheck.get(id as usize) + { + flycheck.cancel(); } // Just ignore this. It is OK to continue sending progress @@ -76,12 +74,12 @@ tracing::error!("duplicate DidOpenTextDocument: {}", path); } - if let Some(abs_path) = path.as_path() { - if state.config.excluded().any(|excluded| abs_path.starts_with(&excluded)) { - tracing::trace!("opened excluded file {abs_path}"); - state.vfs.write().0.insert_excluded_file(path); - return Ok(()); - } + if let Some(abs_path) = path.as_path() + && state.config.excluded().any(|excluded| abs_path.starts_with(&excluded)) + { + tracing::trace!("opened excluded file {abs_path}"); + state.vfs.write().0.insert_excluded_file(path); + return Ok(()); } let contents = params.text_document.text.into_bytes(); @@ -449,12 +447,11 @@ params: RunFlycheckParams, ) -> anyhow::Result<()> { let _p = tracing::info_span!("handle_run_flycheck").entered(); - if let Some(text_document) = params.text_document { - if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) { - if run_flycheck(state, vfs_path) { - return Ok(()); - } - } + if let Some(text_document) = params.text_document + && let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) + && run_flycheck(state, vfs_path) + { + return Ok(()); } // No specific flycheck was triggered, so let's trigger all of them. if state.config.flycheck_workspace(None) {
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index a76a652..6cb28ae 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -8,8 +8,9 @@ use base64::{Engine, prelude::BASE64_STANDARD}; use ide::{ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, - FilePosition, FileRange, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, - RangeInfo, ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, + FilePosition, FileRange, FileStructureConfig, HoverAction, HoverGotoTypeData, + InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, + SingleResolve, SourceChange, TextEdit, }; use ide_db::{FxHashMap, SymbolKind}; use itertools::Itertools; @@ -566,41 +567,47 @@ let file_id = try_default!(from_proto::file_id(&snap, ¶ms.text_document.uri)?); let line_index = snap.file_line_index(file_id)?; - let mut parents: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = Vec::new(); + let mut symbols: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = Vec::new(); - for symbol in snap.analysis.file_structure(file_id)? { + let config = snap.config.document_symbol(None); + + let structure_nodes = snap.analysis.file_structure( + &FileStructureConfig { exclude_locals: config.search_exclude_locals }, + file_id, + )?; + + for node in structure_nodes { let mut tags = Vec::new(); - if symbol.deprecated { + if node.deprecated { tags.push(SymbolTag::DEPRECATED) }; #[allow(deprecated)] - let doc_symbol = lsp_types::DocumentSymbol { - name: symbol.label, - detail: symbol.detail, - kind: to_proto::structure_node_kind(symbol.kind), + let symbol = lsp_types::DocumentSymbol { + name: node.label, + detail: node.detail, + kind: to_proto::structure_node_kind(node.kind), tags: Some(tags), - deprecated: Some(symbol.deprecated), - range: to_proto::range(&line_index, symbol.node_range), - selection_range: to_proto::range(&line_index, symbol.navigation_range), + deprecated: Some(node.deprecated), + range: to_proto::range(&line_index, node.node_range), + selection_range: to_proto::range(&line_index, node.navigation_range), children: None, }; - parents.push((doc_symbol, symbol.parent)); + symbols.push((symbol, node.parent)); } - // Builds hierarchy from a flat list, in reverse order (so that indices - // makes sense) + // Builds hierarchy from a flat list, in reverse order (so that the indices make sense) let document_symbols = { let mut acc = Vec::new(); - while let Some((mut node, parent_idx)) = parents.pop() { - if let Some(children) = &mut node.children { + while let Some((mut symbol, parent_idx)) = symbols.pop() { + if let Some(children) = &mut symbol.children { children.reverse(); } let parent = match parent_idx { None => &mut acc, - Some(i) => parents[i].0.children.get_or_insert_with(Vec::new), + Some(i) => symbols[i].0.children.get_or_insert_with(Vec::new), }; - parent.push(node); + parent.push(symbol); } acc.reverse(); acc @@ -610,7 +617,7 @@ document_symbols.into() } else { let url = to_proto::url(&snap, file_id); - let mut symbol_information = Vec::<SymbolInformation>::new(); + let mut symbol_information = Vec::new(); for symbol in document_symbols { flatten_document_symbol(&symbol, None, &url, &mut symbol_information); } @@ -647,7 +654,7 @@ let _p = tracing::info_span!("handle_workspace_symbol").entered(); let config = snap.config.workspace_symbol(None); - let (all_symbols, libs) = decide_search_scope_and_kind(¶ms, &config); + let (all_symbols, libs) = decide_search_kind_and_scope(¶ms, &config); let query = { let query: String = params.query.chars().filter(|&c| c != '#' && c != '*').collect(); @@ -670,7 +677,7 @@ return Ok(Some(lsp_types::WorkspaceSymbolResponse::Nested(res))); - fn decide_search_scope_and_kind( + fn decide_search_kind_and_scope( params: &WorkspaceSymbolParams, config: &WorkspaceSymbolConfig, ) -> (bool, bool) { @@ -973,14 +980,13 @@ res.push(runnable); } - if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args { - if let Some(TargetSpec::Cargo(CargoTargetSpec { + if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args + && let Some(TargetSpec::Cargo(CargoTargetSpec { sysroot_root: Some(sysroot_root), .. })) = &target_spec - { - r.environment.insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string()); - } + { + r.environment.insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string()); }; res.push(runnable); @@ -1034,25 +1040,25 @@ } Some(TargetSpec::ProjectJson(_)) => {} None => { - if !snap.config.linked_or_discovered_projects().is_empty() { - if let Some(path) = snap.file_id_to_file_path(file_id).parent() { - let mut cargo_args = vec!["check".to_owned(), "--workspace".to_owned()]; - cargo_args.extend(config.cargo_extra_args.iter().cloned()); - 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(), - override_cargo: config.override_cargo, - cargo_args, - executable_args: Vec::new(), - environment: Default::default(), - }), - }); - }; - } + if !snap.config.linked_or_discovered_projects().is_empty() + && let Some(path) = snap.file_id_to_file_path(file_id).parent() + { + let mut cargo_args = vec!["check".to_owned(), "--workspace".to_owned()]; + cargo_args.extend(config.cargo_extra_args.iter().cloned()); + 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(), + override_cargo: config.override_cargo, + cargo_args, + executable_args: Vec::new(), + environment: Default::default(), + }), + }); + }; } } Ok(res) @@ -1557,12 +1563,12 @@ code_action.edit = ca.edit; code_action.command = ca.command; - if let Some(edit) = code_action.edit.as_ref() { - if let Some(changes) = edit.document_changes.as_ref() { - for change in changes { - if let lsp_ext::SnippetDocumentChangeOperation::Op(res_op) = change { - resource_ops_supported(&snap.config, resolve_resource_op(res_op))? - } + if let Some(edit) = code_action.edit.as_ref() + && let Some(changes) = edit.document_changes.as_ref() + { + for change in changes { + if let lsp_ext::SnippetDocumentChangeOperation::Op(res_op) = change { + resource_ops_supported(&snap.config, resolve_resource_op(res_op))? } } } @@ -1958,12 +1964,11 @@ if let Some(cached_tokens @ lsp_types::SemanticTokens { result_id: Some(prev_id), .. }) = &cached_tokens + && *prev_id == params.previous_result_id { - if *prev_id == params.previous_result_id { - let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens); - snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens); - return Ok(Some(delta.into())); - } + let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens); + snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens); + return Ok(Some(delta.into())); } // Clone first to keep the lock short @@ -2122,24 +2127,25 @@ snap: &GlobalStateSnapshot, position: &FilePosition, ) -> Option<lsp_ext::CommandLinkGroup> { - if snap.config.hover_actions().implementations && snap.config.client_commands().show_reference { - if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) { - let uri = to_proto::url(snap, position.file_id); - let line_index = snap.file_line_index(position.file_id).ok()?; - let position = to_proto::position(&line_index, position.offset); - let locations: Vec<_> = nav_data - .info - .into_iter() - .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok()) - .collect(); - let title = to_proto::implementation_title(locations.len()); - let command = to_proto::command::show_references(title, &uri, position, locations); + if snap.config.hover_actions().implementations + && snap.config.client_commands().show_reference + && let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) + { + let uri = to_proto::url(snap, position.file_id); + let line_index = snap.file_line_index(position.file_id).ok()?; + let position = to_proto::position(&line_index, position.offset); + let locations: Vec<_> = nav_data + .info + .into_iter() + .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok()) + .collect(); + let title = to_proto::implementation_title(locations.len()); + let command = to_proto::command::show_references(title, &uri, position, locations); - return Some(lsp_ext::CommandLinkGroup { - commands: vec![to_command_link(command, "Go to implementations".into())], - ..Default::default() - }); - } + return Some(lsp_ext::CommandLinkGroup { + commands: vec![to_command_link(command, "Go to implementations".into())], + ..Default::default() + }); } None } @@ -2148,28 +2154,29 @@ snap: &GlobalStateSnapshot, position: &FilePosition, ) -> Option<lsp_ext::CommandLinkGroup> { - if snap.config.hover_actions().references && snap.config.client_commands().show_reference { - if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) { - let uri = to_proto::url(snap, position.file_id); - let line_index = snap.file_line_index(position.file_id).ok()?; - let position = to_proto::position(&line_index, position.offset); - let locations: Vec<_> = ref_search_res - .into_iter() - .flat_map(|res| res.references) - .flat_map(|(file_id, ranges)| { - ranges.into_iter().map(move |(range, _)| FileRange { file_id, range }) - }) - .unique() - .filter_map(|range| to_proto::location(snap, range).ok()) - .collect(); - let title = to_proto::reference_title(locations.len()); - let command = to_proto::command::show_references(title, &uri, position, locations); + if snap.config.hover_actions().references + && snap.config.client_commands().show_reference + && let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) + { + let uri = to_proto::url(snap, position.file_id); + let line_index = snap.file_line_index(position.file_id).ok()?; + let position = to_proto::position(&line_index, position.offset); + let locations: Vec<_> = ref_search_res + .into_iter() + .flat_map(|res| res.references) + .flat_map(|(file_id, ranges)| { + ranges.into_iter().map(move |(range, _)| FileRange { file_id, range }) + }) + .unique() + .filter_map(|range| to_proto::location(snap, range).ok()) + .collect(); + let title = to_proto::reference_title(locations.len()); + let command = to_proto::command::show_references(title, &uri, position, locations); - return Some(lsp_ext::CommandLinkGroup { - commands: vec![to_command_link(command, "Go to references".into())], - ..Default::default() - }); - } + return Some(lsp_ext::CommandLinkGroup { + commands: vec![to_command_link(command, "Go to references".into())], + ..Default::default() + }); } None }
diff --git a/crates/rust-analyzer/src/lsp/from_proto.rs b/crates/rust-analyzer/src/lsp/from_proto.rs index 0275761..333826a 100644 --- a/crates/rust-analyzer/src/lsp/from_proto.rs +++ b/crates/rust-analyzer/src/lsp/from_proto.rs
@@ -40,12 +40,13 @@ })?; let col = TextSize::from(line_col.col); let clamped_len = col.min(line_range.len()); - if clamped_len < col { - tracing::error!( - "Position {line_col:?} column exceeds line length {}, clamping it", - u32::from(line_range.len()), - ); - } + // FIXME: The cause for this is likely our request retrying. Commented out as this log is just too chatty and very easy to trigger. + // if clamped_len < col { + // tracing::error!( + // "Position {line_col:?} column exceeds line length {}, clamping it", + // u32::from(line_range.len()), + // ); + // } Ok(line_range.start() + clamped_len) }
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index 292be1d..d51ddb8 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -61,7 +61,7 @@ SymbolKind::Struct => lsp_types::SymbolKind::STRUCT, SymbolKind::Enum => lsp_types::SymbolKind::ENUM, SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER, - SymbolKind::Trait | SymbolKind::TraitAlias => lsp_types::SymbolKind::INTERFACE, + SymbolKind::Trait => lsp_types::SymbolKind::INTERFACE, SymbolKind::Macro | SymbolKind::ProcMacro | SymbolKind::BuiltinAttr @@ -156,7 +156,6 @@ SymbolKind::Static => lsp_types::CompletionItemKind::VALUE, SymbolKind::Struct => lsp_types::CompletionItemKind::STRUCT, SymbolKind::Trait => lsp_types::CompletionItemKind::INTERFACE, - SymbolKind::TraitAlias => lsp_types::CompletionItemKind::INTERFACE, SymbolKind::TypeAlias => lsp_types::CompletionItemKind::STRUCT, SymbolKind::TypeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, SymbolKind::Union => lsp_types::CompletionItemKind::STRUCT, @@ -817,7 +816,6 @@ SymbolKind::Union => types::UNION, SymbolKind::TypeAlias => types::TYPE_ALIAS, SymbolKind::Trait => types::INTERFACE, - SymbolKind::TraitAlias => types::INTERFACE, SymbolKind::Macro => types::MACRO, SymbolKind::ProcMacro => types::PROC_MACRO, SymbolKind::BuiltinAttr => types::BUILTIN_ATTRIBUTE, @@ -909,7 +907,6 @@ | FoldKind::WhereClause | FoldKind::ReturnType | FoldKind::Array - | FoldKind::TraitAliases | FoldKind::ExternCrates | FoldKind::MatchArm | FoldKind::Function => None,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 00cf890..c6762f3 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs
@@ -20,7 +20,7 @@ config::Config, diagnostics::{DiagnosticsGeneration, NativeDiagnosticsFetchKind, fetch_native_diagnostics}, discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage}, - flycheck::{self, FlycheckMessage}, + flycheck::{self, ClearDiagnosticsKind, FlycheckMessage}, global_state::{ FetchBuildDataResponse, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState, file_id_to_url, url_to_file_id, @@ -501,14 +501,12 @@ } } - if self.config.cargo_autoreload_config(None) - || self.config.discover_workspace_config().is_some() - { - if let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) = + if (self.config.cargo_autoreload_config(None) + || self.config.discover_workspace_config().is_some()) + && let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) = self.fetch_workspaces_queue.should_start_op() - { - self.fetch_workspaces(cause, path, force_crate_graph_reload); - } + { + self.fetch_workspaces(cause, path, force_crate_graph_reload); } if !self.fetch_workspaces_queue.op_in_progress() { @@ -765,33 +763,33 @@ self.report_progress("Fetching", state, msg, None, None); } Task::DiscoverLinkedProjects(arg) => { - if let Some(cfg) = self.config.discover_workspace_config() { - if !self.discover_workspace_queue.op_in_progress() { - // the clone is unfortunately necessary to avoid a borrowck error when - // `self.report_progress` is called later - let title = &cfg.progress_label.clone(); - let command = cfg.command.clone(); - let discover = DiscoverCommand::new(self.discover_sender.clone(), command); + if let Some(cfg) = self.config.discover_workspace_config() + && !self.discover_workspace_queue.op_in_progress() + { + // the clone is unfortunately necessary to avoid a borrowck error when + // `self.report_progress` is called later + let title = &cfg.progress_label.clone(); + let command = cfg.command.clone(); + let discover = DiscoverCommand::new(self.discover_sender.clone(), command); - self.report_progress(title, Progress::Begin, None, None, None); - self.discover_workspace_queue - .request_op("Discovering workspace".to_owned(), ()); - let _ = self.discover_workspace_queue.should_start_op(); + self.report_progress(title, Progress::Begin, None, None, None); + self.discover_workspace_queue + .request_op("Discovering workspace".to_owned(), ()); + let _ = self.discover_workspace_queue.should_start_op(); - let arg = match arg { - DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it), - DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), - }; + let arg = match arg { + DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it), + DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), + }; - let handle = discover.spawn( - arg, - &std::env::current_dir() - .expect("Failed to get cwd during project discovery"), - ); - self.discover_handle = Some(handle.unwrap_or_else(|e| { - panic!("Failed to spawn project discovery command: {e}") - })); - } + let handle = discover.spawn( + arg, + &std::env::current_dir() + .expect("Failed to get cwd during project discovery"), + ); + self.discover_handle = Some(handle.unwrap_or_else(|e| { + panic!("Failed to spawn project discovery command: {e}") + })); } } Task::FetchBuildData(progress) => { @@ -814,7 +812,7 @@ }; if let Some(state) = state { - self.report_progress("Building build-artifacts", state, msg, None, None); + self.report_progress("Building compile-time-deps", state, msg, None, None); } } Task::LoadProcMacros(progress) => { @@ -1010,7 +1008,13 @@ fn handle_flycheck_msg(&mut self, message: FlycheckMessage) { match message { - FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic, package_id } => { + FlycheckMessage::AddDiagnostic { + id, + generation, + workspace_root, + diagnostic, + package_id, + } => { let snap = self.snapshot(); let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( &self.config.diagnostics_map(None), @@ -1022,6 +1026,7 @@ match url_to_file_id(&self.vfs.read().0, &diag.url) { Ok(Some(file_id)) => self.diagnostics.add_check_diagnostic( id, + generation, &package_id, file_id, diag.diagnostic, @@ -1037,12 +1042,17 @@ }; } } - FlycheckMessage::ClearDiagnostics { id, package_id: None } => { + FlycheckMessage::ClearDiagnostics { id, kind: ClearDiagnosticsKind::All } => { self.diagnostics.clear_check(id) } - FlycheckMessage::ClearDiagnostics { id, package_id: Some(package_id) } => { - self.diagnostics.clear_check_for_package(id, package_id) - } + FlycheckMessage::ClearDiagnostics { + id, + kind: ClearDiagnosticsKind::OlderThan(generation), + } => self.diagnostics.clear_check_older_than(id, generation), + FlycheckMessage::ClearDiagnostics { + id, + kind: ClearDiagnosticsKind::Package(package_id), + } => self.diagnostics.clear_check_for_package(id, package_id), FlycheckMessage::Progress { id, progress } => { let (state, message) = match progress { flycheck::Progress::DidStart => (Progress::Begin, None),
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index e798aa6..f8f29ee 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs
@@ -306,13 +306,13 @@ _ => None, }); - if let Some(build) = build { - if is_quiescent { - let path = AbsPathBuf::try_from(build.build_file) - .expect("Unable to convert to an AbsPath"); - let arg = DiscoverProjectParam::Buildfile(path); - sender.send(Task::DiscoverLinkedProjects(arg)).unwrap(); - } + if let Some(build) = build + && is_quiescent + { + let path = AbsPathBuf::try_from(build.build_file) + .expect("Unable to convert to an AbsPath"); + let arg = DiscoverProjectParam::Buildfile(path); + sender.send(Task::DiscoverLinkedProjects(arg)).unwrap(); } } @@ -855,11 +855,13 @@ invocation_strategy.clone() } }; + let next_gen = self.flycheck.iter().map(|f| f.generation() + 1).max().unwrap_or_default(); self.flycheck = match invocation_strategy { crate::flycheck::InvocationStrategy::Once => { vec![FlycheckHandle::spawn( 0, + next_gen, sender, config, None, @@ -898,6 +900,7 @@ .map(|(id, (root, manifest_path), sysroot_root)| { FlycheckHandle::spawn( id, + next_gen, sender.clone(), config.clone(), sysroot_root,
diff --git a/crates/span/src/ast_id.rs b/crates/span/src/ast_id.rs index a9288ec..e803747 100644 --- a/crates/span/src/ast_id.rs +++ b/crates/span/src/ast_id.rs
@@ -485,8 +485,7 @@ MacroRules = name, Module = name, Static = name, - Trait = name, - TraitAlias = name + Trait = name } macro_rules! register_assoc_item_ast_id {
diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs index b81d08e..ae9e038 100644 --- a/crates/span/src/lib.rs +++ b/crates/span/src/lib.rs
@@ -203,15 +203,3 @@ /// `println!("Hello, {}", world)`. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MacroCallId(pub salsa::Id); - -/// Legacy span type, only defined here as it is still used by the proc-macro server. -/// While rust-analyzer doesn't use this anymore at all, RustRover relies on the legacy type for -/// proc-macro expansion. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct TokenId(pub u32); - -impl std::fmt::Debug for TokenId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -}
diff --git a/crates/span/src/map.rs b/crates/span/src/map.rs index f582017..bb09933 100644 --- a/crates/span/src/map.rs +++ b/crates/span/src/map.rs
@@ -41,13 +41,13 @@ /// Pushes a new span onto the [`SpanMap`]. pub fn push(&mut self, offset: TextSize, span: SpanData<S>) { - if cfg!(debug_assertions) { - if let Some(&(last_offset, _)) = self.spans.last() { - assert!( - last_offset < offset, - "last_offset({last_offset:?}) must be smaller than offset({offset:?})" - ); - } + if cfg!(debug_assertions) + && let Some(&(last_offset, _)) = self.spans.last() + { + assert!( + last_offset < offset, + "last_offset({last_offset:?}) must be smaller than offset({offset:?})" + ); } self.spans.push((offset, span)); }
diff --git a/crates/syntax-bridge/src/lib.rs b/crates/syntax-bridge/src/lib.rs index d592299..4e525be3 100644 --- a/crates/syntax-bridge/src/lib.rs +++ b/crates/syntax-bridge/src/lib.rs
@@ -127,11 +127,11 @@ // The following items are what `rustc` macro can be parsed into : // link: https://github.com/rust-lang/rust/blob/9ebf47851a357faa4cd97f4b1dc7835f6376e639/src/libsyntax/ext/expand.rs#L141 -// * Expr(P<ast::Expr>) -> token_tree_to_expr -// * Pat(P<ast::Pat>) -> token_tree_to_pat -// * Ty(P<ast::Ty>) -> token_tree_to_ty +// * Expr(Box<ast::Expr>) -> token_tree_to_expr +// * Pat(Box<ast::Pat>) -> token_tree_to_pat +// * Ty(Box<ast::Ty>) -> token_tree_to_ty // * Stmts(SmallVec<[ast::Stmt; 1]>) -> token_tree_to_stmts -// * Items(SmallVec<[P<ast::Item>; 1]>) -> token_tree_to_items +// * Items(SmallVec<[Box<ast::Item>; 1]>) -> token_tree_to_items // // * TraitItems(SmallVec<[ast::TraitItem; 1]>) // * AssocItems(SmallVec<[ast::AssocItem; 1]>) @@ -768,17 +768,17 @@ } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { - if let Some((punct, offset)) = self.punct_offset.clone() { - if usize::from(offset) + 1 < punct.text().len() { - let offset = offset + TextSize::of('.'); - let range = punct.text_range(); - self.punct_offset = Some((punct.clone(), offset)); - let range = TextRange::at(range.start() + offset, TextSize::of('.')); - return Some(( - SynToken::Punct { token: punct, offset: u32::from(offset) as usize }, - range, - )); - } + if let Some((punct, offset)) = self.punct_offset.clone() + && usize::from(offset) + 1 < punct.text().len() + { + let offset = offset + TextSize::of('.'); + let range = punct.text_range(); + self.punct_offset = Some((punct.clone(), offset)); + let range = TextRange::at(range.start() + offset, TextSize::of('.')); + return Some(( + SynToken::Punct { token: punct, offset: u32::from(offset) as usize }, + range, + )); } if let Some(leaf) = self.current_leaves.pop() {
diff --git a/crates/syntax-bridge/src/prettify_macro_expansion.rs b/crates/syntax-bridge/src/prettify_macro_expansion.rs index 0a5c8df..2f932e0 100644 --- a/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/crates/syntax-bridge/src/prettify_macro_expansion.rs
@@ -61,10 +61,11 @@ } _ => continue, }; - if token.kind() == SyntaxKind::IDENT && token.text() == "$crate" { - if let Some(replacement) = dollar_crate_replacement(&token) { - dollar_crate_replacements.push((token.clone(), replacement)); - } + if token.kind() == SyntaxKind::IDENT + && token.text() == "$crate" + && let Some(replacement) = dollar_crate_replacement(&token) + { + dollar_crate_replacements.push((token.clone(), replacement)); } let tok = &token;
diff --git a/crates/syntax-bridge/src/tests.rs b/crates/syntax-bridge/src/tests.rs index 8871bf5..c8dc313 100644 --- a/crates/syntax-bridge/src/tests.rs +++ b/crates/syntax-bridge/src/tests.rs
@@ -34,14 +34,11 @@ while !cursor.eof() { while let Some(token_tree) = cursor.token_tree() { if let tt::TokenTree::Leaf(Leaf::Punct(Punct { - spacing, - span: Span { range, .. }, - .. + spacing, span: Span { range, .. }, .. })) = token_tree + && let Some(expected) = annotations.remove(range) { - if let Some(expected) = annotations.remove(range) { - assert_eq!(expected, *spacing); - } + assert_eq!(expected, *spacing); } cursor.bump(); }
diff --git a/crates/syntax-bridge/src/to_parser_input.rs b/crates/syntax-bridge/src/to_parser_input.rs index 021dc65..c0ff8e1 100644 --- a/crates/syntax-bridge/src/to_parser_input.rs +++ b/crates/syntax-bridge/src/to_parser_input.rs
@@ -21,17 +21,17 @@ let tt = current.token_tree(); // Check if it is lifetime - if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tt { - if punct.char == '\'' { - current.bump(); - match current.token_tree() { - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(_ident))) => { - res.push(LIFETIME_IDENT); - current.bump(); - continue; - } - _ => panic!("Next token must be ident"), + if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tt + && punct.char == '\'' + { + current.bump(); + match current.token_tree() { + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(_ident))) => { + res.push(LIFETIME_IDENT); + current.bump(); + continue; } + _ => panic!("Next token must be ident"), } }
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 6d8a360..d73d60c 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram
@@ -154,7 +154,6 @@ | Static | Struct | Trait -| TraitAlias | TypeAlias | Union | Use @@ -306,11 +305,8 @@ Attr* Visibility? 'unsafe'? 'auto'? 'trait' Name GenericParamList? - (':' TypeBoundList?)? WhereClause? AssocItemList - -TraitAlias = - Attr* Visibility? - 'trait' Name GenericParamList? '=' TypeBoundList? WhereClause? ';' + (((':' TypeBoundList?)? WhereClause? AssocItemList) | + ('=' TypeBoundList? WhereClause? ';')) AssocItemList = '{' Attr* AssocItem* '}'
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index a9aeeed..19c1c5e 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs
@@ -26,8 +26,7 @@ generated::{nodes::*, tokens::*}, node_ext::{ AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind, - SlicePatComponents, StructKind, TraitOrAlias, TypeBoundKind, TypeOrConstParam, - VisibilityKind, + SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam, VisibilityKind, }, operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}, token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix},
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index d97fdec..9b30642 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs
@@ -89,11 +89,11 @@ _ => None, }); for token in tokens { - if let Some(ws) = ast::Whitespace::cast(token) { - if ws.text().contains('\n') { - let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax())); - ted::replace(ws.syntax(), &new_ws); - } + 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); } } } @@ -122,13 +122,13 @@ _ => None, }); for token in tokens { - if let Some(ws) = ast::Whitespace::cast(token) { - if ws.text().contains('\n') { - let new_ws = make::tokens::whitespace( - &ws.syntax().text().replace(&format!("\n{self}"), "\n"), - ); - ted::replace(ws.syntax(), &new_ws); - } + 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); } } }
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 28b543e..1cd8146f 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -9,11 +9,11 @@ SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, algo::{self, neighbor}, - ast::{self, HasGenericArgs, HasGenericParams, edit::IndentLevel, make}, + ast::{self, HasGenericParams, edit::IndentLevel, make}, ted::{self, Position}, }; -use super::{GenericParam, HasArgList, HasName}; +use super::{GenericParam, HasName}; pub trait GenericParamsOwnerEdit: ast::HasGenericParams { fn get_or_create_generic_param_list(&self) -> ast::GenericParamList; @@ -99,38 +99,10 @@ fn get_or_create_where_clause(&self) -> ast::WhereClause { if self.where_clause().is_none() { - let position = match self.assoc_item_list() { - Some(items) => Position::before(items.syntax()), - None => Position::last_child_of(self.syntax()), - }; - create_where_clause(position); - } - self.where_clause().unwrap() - } -} - -impl GenericParamsOwnerEdit for ast::TraitAlias { - fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { - match self.generic_param_list() { - Some(it) => it, - None => { - let position = if let Some(name) = self.name() { - Position::after(name.syntax) - } else if let Some(trait_token) = self.trait_token() { - Position::after(trait_token) - } else { - Position::last_child_of(self.syntax()) - }; - create_generic_param_list(position) - } - } - } - - fn get_or_create_where_clause(&self) -> ast::WhereClause { - if self.where_clause().is_none() { - let position = match self.semicolon_token() { - Some(tok) => Position::before(tok), - None => Position::last_child_of(self.syntax()), + let position = match (self.assoc_item_list(), self.semicolon_token()) { + (Some(items), _) => Position::before(items.syntax()), + (_, Some(tok)) => Position::before(tok), + (None, None) => Position::last_child_of(self.syntax()), }; create_where_clause(position); } @@ -273,28 +245,6 @@ } } } - - fn add_attr(&self, attr: ast::Attr) { - add_attr(self.syntax(), attr); - - fn add_attr(node: &SyntaxNode, attr: ast::Attr) { - let indent = IndentLevel::from_node(node); - attr.reindent_to(indent); - - let after_attrs_and_comments = node - .children_with_tokens() - .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) - .map_or(Position::first_child_of(node), Position::before); - - ted::insert_all( - after_attrs_and_comments, - vec![ - attr.syntax().clone().into(), - make::tokens::whitespace(&format!("\n{indent}")).into(), - ], - ) - } - } } impl<T: ast::HasAttrs> AttrsOwnerEdit for T {} @@ -383,10 +333,10 @@ impl ast::WhereClause { pub fn add_predicate(&self, predicate: ast::WherePred) { - if let Some(pred) = self.predicates().last() { - if !pred.syntax().siblings_with_tokens(Direction::Next).any(|it| it.kind() == T![,]) { - ted::append_child_raw(self.syntax(), make::token(T![,])); - } + 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()); } @@ -419,34 +369,6 @@ } } -impl ast::PathSegment { - pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList { - if self.generic_arg_list().is_none() { - let arg_list = make::generic_arg_list(empty()).clone_for_update(); - ted::append_child(self.syntax(), arg_list.syntax()); - } - self.generic_arg_list().unwrap() - } -} - -impl ast::MethodCallExpr { - pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList { - if self.generic_arg_list().is_none() { - let generic_arg_list = make::turbofish_generic_arg_list(empty()).clone_for_update(); - - if let Some(arg_list) = self.arg_list() { - ted::insert_raw( - ted::Position::before(arg_list.syntax()), - generic_arg_list.syntax(), - ); - } else { - ted::append_child(self.syntax(), generic_arg_list.syntax()); - } - } - self.generic_arg_list().unwrap() - } -} - impl Removable for ast::UseTree { fn remove(&self) { for dir in [Direction::Next, Direction::Prev] { @@ -677,106 +599,6 @@ ]; ted::insert_all(position, elements); } - - /// Adds a new associated item at the start of the associated item list. - /// - /// 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_at_start(&self, item: ast::AssocItem) { - match self.assoc_items().next() { - Some(first_item) => { - let indent = IndentLevel::from_node(first_item.syntax()); - let before = Position::before(first_item.syntax()); - - ted::insert_all( - before, - vec![ - item.syntax().clone().into(), - make::tokens::whitespace(&format!("\n\n{indent}")).into(), - ], - ) - } - None => { - let (indent, position, whitespace) = match self.l_curly_token() { - Some(l_curly) => { - normalize_ws_between_braces(self.syntax()); - (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") - } - None => (IndentLevel::single(), Position::first_child_of(self.syntax()), ""), - }; - - let mut elements = vec![]; - - // Avoid pushing an empty whitespace token - if !indent.is_zero() || !whitespace.is_empty() { - elements.push(make::tokens::whitespace(&format!("{whitespace}{indent}")).into()) - } - elements.push(item.syntax().clone().into()); - - ted::insert_all(position, elements) - } - }; - } -} - -impl ast::Fn { - pub fn get_or_create_body(&self) -> ast::BlockExpr { - if self.body().is_none() { - let body = make::ext::empty_block_expr().clone_for_update(); - match self.semicolon_token() { - Some(semi) => { - ted::replace(semi, body.syntax()); - ted::insert(Position::before(body.syntax), make::tokens::single_space()); - } - None => ted::append_child(self.syntax(), body.syntax()), - } - } - self.body().unwrap() - } -} - -impl ast::LetStmt { - pub fn set_ty(&self, ty: Option<ast::Type>) { - match ty { - None => { - if let Some(colon_token) = self.colon_token() { - ted::remove(colon_token); - } - - if let Some(existing_ty) = self.ty() { - if let Some(sibling) = existing_ty.syntax().prev_sibling_or_token() { - if sibling.kind() == SyntaxKind::WHITESPACE { - ted::remove(sibling); - } - } - - ted::remove(existing_ty.syntax()); - } - - // Remove any trailing ws - if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) - { - last.detach(); - } - } - Some(new_ty) => { - if self.colon_token().is_none() { - ted::insert_raw( - Position::after( - self.pat().expect("let stmt should have a pattern").syntax(), - ), - make::token(T![:]), - ); - } - - if let Some(old_ty) = self.ty() { - ted::replace(old_ty.syntax(), new_ty.syntax()); - } else { - ted::insert(Position::after(self.colon_token().unwrap()), new_ty.syntax()); - } - } - } - } } impl ast::RecordExprFieldList { @@ -823,19 +645,18 @@ return; } // this is a shorthand - if let Some(ast::Expr::PathExpr(path_expr)) = self.expr() { - if let Some(path) = path_expr.path() { - if 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(Position::last_child_of(self.syntax()), children); - } - } + 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(Position::last_child_of(self.syntax()), children); } } } @@ -1092,35 +913,4 @@ check("let a @ ()", "let a", None); check("let a @ ", "let a", None); } - - #[test] - fn test_let_stmt_set_ty() { - #[track_caller] - fn check(before: &str, expected: &str, ty: Option<ast::Type>) { - let ty = ty.map(|it| it.clone_for_update()); - - let let_stmt = ast_mut_from_text::<ast::LetStmt>(&format!("fn f() {{ {before} }}")); - let_stmt.set_ty(ty); - - let after = ast_mut_from_text::<ast::LetStmt>(&format!("fn f() {{ {expected} }}")); - assert_eq!(let_stmt.to_string(), after.to_string(), "{let_stmt:#?}\n!=\n{after:#?}"); - } - - // adding - check("let a;", "let a: ();", Some(make::ty_tuple([]))); - // no semicolon due to it being eaten during error recovery - check("let a:", "let a: ()", Some(make::ty_tuple([]))); - - // replacing - check("let a: u8;", "let a: ();", Some(make::ty_tuple([]))); - check("let a: u8 = 3;", "let a: () = 3;", Some(make::ty_tuple([]))); - check("let a: = 3;", "let a: () = 3;", Some(make::ty_tuple([]))); - - // removing - check("let a: u8;", "let a;", None); - check("let a:;", "let a;", None); - - check("let a: u8 = 3;", "let a = 3;", None); - check("let a: = 3;", "let a = 3;", None); - } }
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index ceb2866..d60196d 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1614,30 +1614,16 @@ #[inline] pub fn assoc_item_list(&self) -> Option<AssocItemList> { support::child(&self.syntax) } #[inline] + pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } + #[inline] + pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } + #[inline] pub fn auto_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![auto]) } #[inline] pub fn trait_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![trait]) } #[inline] pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) } } -pub struct TraitAlias { - pub(crate) syntax: SyntaxNode, -} -impl ast::HasAttrs for TraitAlias {} -impl ast::HasDocComments for TraitAlias {} -impl ast::HasGenericParams for TraitAlias {} -impl ast::HasName for TraitAlias {} -impl ast::HasVisibility for TraitAlias {} -impl TraitAlias { - #[inline] - pub fn type_bound_list(&self) -> Option<TypeBoundList> { support::child(&self.syntax) } - #[inline] - pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } - #[inline] - pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } - #[inline] - pub fn trait_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![trait]) } -} pub struct TryExpr { pub(crate) syntax: SyntaxNode, } @@ -2107,7 +2093,6 @@ Static(Static), Struct(Struct), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), Union(Union), Use(Use), @@ -6801,42 +6786,6 @@ f.debug_struct("Trait").field("syntax", &self.syntax).finish() } } -impl AstNode for TraitAlias { - #[inline] - fn kind() -> SyntaxKind - where - Self: Sized, - { - TRAIT_ALIAS - } - #[inline] - fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT_ALIAS } - #[inline] - fn cast(syntax: SyntaxNode) -> Option<Self> { - if Self::can_cast(syntax.kind()) { - Some(Self { syntax }) - } else { - None - } - } - #[inline] - fn syntax(&self) -> &SyntaxNode { &self.syntax } -} -impl hash::Hash for TraitAlias { - fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } -} -impl Eq for TraitAlias {} -impl PartialEq for TraitAlias { - fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } -} -impl Clone for TraitAlias { - fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } -} -impl fmt::Debug for TraitAlias { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TraitAlias").field("syntax", &self.syntax).finish() - } -} impl AstNode for TryExpr { #[inline] fn kind() -> SyntaxKind @@ -8471,10 +8420,6 @@ #[inline] fn from(node: Trait) -> Item { Item::Trait(node) } } -impl From<TraitAlias> for Item { - #[inline] - fn from(node: TraitAlias) -> Item { Item::TraitAlias(node) } -} impl From<TypeAlias> for Item { #[inline] fn from(node: TypeAlias) -> Item { Item::TypeAlias(node) } @@ -8506,7 +8451,6 @@ | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TYPE_ALIAS | UNION | USE @@ -8529,7 +8473,6 @@ STATIC => Item::Static(Static { syntax }), STRUCT => Item::Struct(Struct { syntax }), TRAIT => Item::Trait(Trait { syntax }), - TRAIT_ALIAS => Item::TraitAlias(TraitAlias { syntax }), TYPE_ALIAS => Item::TypeAlias(TypeAlias { syntax }), UNION => Item::Union(Union { syntax }), USE => Item::Use(Use { syntax }), @@ -8554,7 +8497,6 @@ Item::Static(it) => &it.syntax, Item::Struct(it) => &it.syntax, Item::Trait(it) => &it.syntax, - Item::TraitAlias(it) => &it.syntax, Item::TypeAlias(it) => &it.syntax, Item::Union(it) => &it.syntax, Item::Use(it) => &it.syntax, @@ -8984,7 +8926,6 @@ | STMT_LIST | STRUCT | TRAIT - | TRAIT_ALIAS | TRY_EXPR | TUPLE_EXPR | TUPLE_FIELD @@ -9257,10 +9198,6 @@ #[inline] fn from(node: Trait) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } } } -impl From<TraitAlias> for AnyHasAttrs { - #[inline] - fn from(node: TraitAlias) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } } -} impl From<TryExpr> for AnyHasAttrs { #[inline] fn from(node: TryExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } } @@ -9330,7 +9267,6 @@ | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TUPLE_FIELD | TYPE_ALIAS | UNION @@ -9420,10 +9356,6 @@ #[inline] fn from(node: Trait) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } } -impl From<TraitAlias> for AnyHasDocComments { - #[inline] - fn from(node: TraitAlias) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} impl From<TupleField> for AnyHasDocComments { #[inline] fn from(node: TupleField) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } @@ -9488,7 +9420,7 @@ impl AstNode for AnyHasGenericParams { #[inline] fn can_cast(kind: SyntaxKind) -> bool { - matches!(kind, CONST | ENUM | FN | IMPL | STRUCT | TRAIT | TRAIT_ALIAS | TYPE_ALIAS | UNION) + matches!(kind, CONST | ENUM | FN | IMPL | STRUCT | TRAIT | TYPE_ALIAS | UNION) } #[inline] fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -9536,10 +9468,6 @@ #[inline] fn from(node: Trait) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } } } -impl From<TraitAlias> for AnyHasGenericParams { - #[inline] - fn from(node: TraitAlias) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } } -} impl From<TypeAlias> for AnyHasGenericParams { #[inline] fn from(node: TypeAlias) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } } @@ -9646,7 +9574,6 @@ | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TYPE_ALIAS | TYPE_PARAM | UNION @@ -9739,10 +9666,6 @@ #[inline] fn from(node: Trait) -> AnyHasName { AnyHasName { syntax: node.syntax } } } -impl From<TraitAlias> for AnyHasName { - #[inline] - fn from(node: TraitAlias) -> AnyHasName { AnyHasName { syntax: node.syntax } } -} impl From<TypeAlias> for AnyHasName { #[inline] fn from(node: TypeAlias) -> AnyHasName { AnyHasName { syntax: node.syntax } } @@ -9832,7 +9755,6 @@ | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TUPLE_FIELD | TYPE_ALIAS | UNION @@ -9910,10 +9832,6 @@ #[inline] fn from(node: Trait) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } } } -impl From<TraitAlias> for AnyHasVisibility { - #[inline] - fn from(node: TraitAlias) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } } -} impl From<TupleField> for AnyHasVisibility { #[inline] fn from(node: TupleField) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } } @@ -10639,11 +10557,6 @@ std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for TraitAlias { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self.syntax(), f) - } -} impl std::fmt::Display for TryExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 66aae10..9897fd0 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs
@@ -190,6 +190,7 @@ } pub fn ty_alias( + attrs: impl IntoIterator<Item = ast::Attr>, ident: &str, generic_param_list: Option<ast::GenericParamList>, type_param_bounds: Option<ast::TypeParam>, @@ -200,6 +201,7 @@ let assignment_where = assignment_where.flatten(); quote! { TypeAlias { + #(#attrs "\n")* [type] " " Name { [IDENT ident] } #generic_param_list @@ -229,15 +231,30 @@ } } -pub fn assoc_item_list( - body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>, -) -> ast::AssocItemList { +pub fn item_list(body: Option<Vec<ast::Item>>) -> ast::ItemList { + let is_break_braces = body.is_some(); + let body_newline = if is_break_braces { "\n" } else { "" }; + let body_indent = if is_break_braces { " " } else { "" }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join("\n\n "), + None => String::new(), + }; + ast_from_text(&format!("mod C {{{body_newline}{body_indent}{body}{body_newline}}}")) +} + +pub fn mod_(name: ast::Name, body: Option<ast::ItemList>) -> ast::Module { + let body = body.map_or(";".to_owned(), |body| format!(" {body}")); + ast_from_text(&format!("mod {name}{body}")) +} + +pub fn assoc_item_list(body: Option<Vec<ast::AssocItem>>) -> ast::AssocItemList { let is_break_braces = body.is_some(); let body_newline = if is_break_braces { "\n".to_owned() } else { String::new() }; let body_indent = if is_break_braces { " ".to_owned() } else { String::new() }; let body = match body { - Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), + Some(bd) => bd.iter().map(|elem| elem.to_string()).join("\n\n "), None => String::new(), }; ast_from_text(&format!("impl C for D {{{body_newline}{body_indent}{body}{body_newline}}}")) @@ -279,12 +296,16 @@ } pub fn impl_( + attrs: impl IntoIterator<Item = ast::Attr>, generic_params: Option<ast::GenericParamList>, generic_args: Option<ast::GenericArgList>, path_type: ast::Type, where_clause: Option<ast::WhereClause>, body: Option<ast::AssocItemList>, ) -> ast::Impl { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); + let gen_args = generic_args.map_or_else(String::new, |it| it.to_string()); let gen_params = generic_params.map_or_else(String::new, |it| it.to_string()); @@ -297,10 +318,11 @@ }; let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string()); - ast_from_text(&format!("impl{gen_params} {path_type}{gen_args}{where_clause}{body}")) + ast_from_text(&format!("{attrs}impl{gen_params} {path_type}{gen_args}{where_clause}{body}")) } pub fn impl_trait( + attrs: impl IntoIterator<Item = ast::Attr>, is_unsafe: bool, trait_gen_params: Option<ast::GenericParamList>, trait_gen_args: Option<ast::GenericArgList>, @@ -313,6 +335,8 @@ ty_where_clause: Option<ast::WhereClause>, body: Option<ast::AssocItemList>, ) -> ast::Impl { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let is_unsafe = if is_unsafe { "unsafe " } else { "" }; let trait_gen_args = trait_gen_args.map(|args| args.to_string()).unwrap_or_default(); @@ -336,7 +360,7 @@ let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string()); ast_from_text(&format!( - "{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}" + "{attrs}{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}" )) } @@ -454,12 +478,18 @@ ast_from_text(&format!("use {{{use_trees}}};")) } -pub fn use_(visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use { +pub fn use_( + attrs: impl IntoIterator<Item = ast::Attr>, + visibility: Option<ast::Visibility>, + use_tree: ast::UseTree, +) -> ast::Use { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), }; - ast_from_text(&format!("{visibility}use {use_tree};")) + ast_from_text(&format!("{attrs}{visibility}use {use_tree};")) } pub fn record_expr(path: ast::Path, fields: ast::RecordExprFieldList) -> ast::RecordExpr { @@ -948,16 +978,19 @@ } pub fn item_const( + attrs: impl IntoIterator<Item = ast::Attr>, visibility: Option<ast::Visibility>, name: ast::Name, ty: ast::Type, expr: ast::Expr, ) -> ast::Const { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), }; - ast_from_text(&format!("{visibility}const {name}: {ty} = {expr};")) + ast_from_text(&format!("{attrs}{visibility}const {name}: {ty} = {expr};")) } pub fn item_static( @@ -1164,6 +1197,7 @@ } pub fn fn_( + attrs: impl IntoIterator<Item = ast::Attr>, visibility: Option<ast::Visibility>, fn_name: ast::Name, type_params: Option<ast::GenericParamList>, @@ -1176,6 +1210,8 @@ is_unsafe: bool, is_gen: bool, ) -> ast::Fn { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let type_params = match type_params { Some(type_params) => format!("{type_params}"), None => "".into(), @@ -1199,7 +1235,7 @@ let gen_literal = if is_gen { "gen " } else { "" }; ast_from_text(&format!( - "{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", + "{attrs}{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", )) } pub fn struct_( @@ -1219,12 +1255,15 @@ } pub fn enum_( + attrs: impl IntoIterator<Item = ast::Attr>, visibility: Option<ast::Visibility>, enum_name: ast::Name, generic_param_list: Option<ast::GenericParamList>, where_clause: Option<ast::WhereClause>, variant_list: ast::VariantList, ) -> ast::Enum { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), @@ -1234,7 +1273,7 @@ let where_clause = where_clause.map(|it| format!(" {it}")).unwrap_or_default(); ast_from_text(&format!( - "{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}" + "{attrs}{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}" )) }
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 62a7d4d..42b0f5c 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs
@@ -880,51 +880,6 @@ impl HasAttrs for TypeOrConstParam {} -#[derive(Debug, Clone)] -pub enum TraitOrAlias { - Trait(ast::Trait), - TraitAlias(ast::TraitAlias), -} - -impl TraitOrAlias { - pub fn name(&self) -> Option<ast::Name> { - match self { - TraitOrAlias::Trait(x) => x.name(), - TraitOrAlias::TraitAlias(x) => x.name(), - } - } -} - -impl AstNode for TraitOrAlias { - fn can_cast(kind: SyntaxKind) -> bool - where - Self: Sized, - { - matches!(kind, SyntaxKind::TRAIT | SyntaxKind::TRAIT_ALIAS) - } - - fn cast(syntax: SyntaxNode) -> Option<Self> - where - Self: Sized, - { - let res = match syntax.kind() { - SyntaxKind::TRAIT => TraitOrAlias::Trait(ast::Trait { syntax }), - SyntaxKind::TRAIT_ALIAS => TraitOrAlias::TraitAlias(ast::TraitAlias { syntax }), - _ => return None, - }; - Some(res) - } - - fn syntax(&self) -> &SyntaxNode { - match self { - TraitOrAlias::Trait(it) => it.syntax(), - TraitOrAlias::TraitAlias(it) => it.syntax(), - } - } -} - -impl HasAttrs for TraitOrAlias {} - pub enum VisibilityKind { In(ast::Path), PubCrate,
diff --git a/crates/syntax/src/ast/prec.rs b/crates/syntax/src/ast/prec.rs index 00750bf..1364adb 100644 --- a/crates/syntax/src/ast/prec.rs +++ b/crates/syntax/src/ast/prec.rs
@@ -276,19 +276,19 @@ } // Not every expression can be followed by `else` in the `let-else` - if let Some(ast::Stmt::LetStmt(e)) = stmt { - if e.let_else().is_some() { - match self { - BinExpr(e) - if e.op_kind() - .map(|op| matches!(op, BinaryOp::LogicOp(_))) - .unwrap_or(false) => - { - return true; - } - _ if self.clone().trailing_brace().is_some() => return true, - _ => {} + if let Some(ast::Stmt::LetStmt(e)) = stmt + && e.let_else().is_some() + { + match self { + BinExpr(e) + if e.op_kind() + .map(|op| matches!(op, BinaryOp::LogicOp(_))) + .unwrap_or(false) => + { + return true; } + _ if self.clone().trailing_brace().is_some() => return true, + _ => {} } }
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 1ba6107..8bf27f9 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -2,8 +2,8 @@ use crate::{ AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, ast::{ - self, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, HasTypeBounds, - HasVisibility, RangeItem, make, + self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, + HasTypeBounds, HasVisibility, RangeItem, make, }, syntax_editor::SyntaxMappingBuilder, }; @@ -107,8 +107,13 @@ ast } - pub fn use_(&self, visibility: Option<ast::Visibility>, use_tree: ast::UseTree) -> ast::Use { - make::use_(visibility, use_tree).clone_for_update() + pub fn use_( + &self, + attrs: impl IntoIterator<Item = ast::Attr>, + visibility: Option<ast::Visibility>, + use_tree: ast::UseTree, + ) -> ast::Use { + make::use_(attrs, visibility, use_tree).clone_for_update() } pub fn use_tree( @@ -840,16 +845,20 @@ pub fn item_const( &self, + attrs: impl IntoIterator<Item = ast::Attr>, visibility: Option<ast::Visibility>, name: ast::Name, ty: ast::Type, expr: ast::Expr, ) -> ast::Const { - let ast = make::item_const(visibility.clone(), name.clone(), ty.clone(), expr.clone()) - .clone_for_update(); + let (attrs, attrs_input) = iterator_input(attrs); + let ast = + make::item_const(attrs, visibility.clone(), name.clone(), ty.clone(), expr.clone()) + .clone_for_update(); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(attrs_input, ast.attrs().map(|attr| attr.syntax().clone())); if let Some(visibility) = visibility { builder.map_node( visibility.syntax().clone(), @@ -939,6 +948,24 @@ ast } + pub fn record_expr( + &self, + path: ast::Path, + fields: ast::RecordExprFieldList, + ) -> ast::RecordExpr { + let ast = make::record_expr(path.clone(), fields.clone()).clone_for_update(); + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone()); + builder.map_node( + fields.syntax().clone(), + ast.record_expr_field_list().unwrap().syntax().clone(), + ); + builder.finish(&mut mapping); + } + ast + } + pub fn record_expr_field( &self, name: ast::NameRef, @@ -1049,6 +1076,7 @@ pub fn item_enum( &self, + attrs: impl IntoIterator<Item = ast::Attr>, visibility: Option<ast::Visibility>, name: ast::Name, generic_param_list: Option<ast::GenericParamList>, @@ -1056,6 +1084,7 @@ variant_list: ast::VariantList, ) -> ast::Enum { let ast = make::enum_( + attrs, visibility.clone(), name.clone(), generic_param_list.clone(), @@ -1164,6 +1193,7 @@ pub fn fn_( &self, + attrs: impl IntoIterator<Item = ast::Attr>, visibility: Option<ast::Visibility>, fn_name: ast::Name, type_params: Option<ast::GenericParamList>, @@ -1176,7 +1206,9 @@ is_unsafe: bool, is_gen: bool, ) -> ast::Fn { + let (attrs, input) = iterator_input(attrs); let ast = make::fn_( + attrs, visibility.clone(), fn_name.clone(), type_params.clone(), @@ -1192,6 +1224,7 @@ if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input, ast.attrs().map(|attr| attr.syntax().clone())); if let Some(visibility) = visibility { builder.map_node(
diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index 5107754..0b35887 100644 --- a/crates/syntax/src/syntax_editor.rs +++ b/crates/syntax/src/syntax_editor.rs
@@ -83,6 +83,16 @@ self.changes.push(Change::Replace(element.syntax_element(), None)); } + pub fn delete_all(&mut self, range: RangeInclusive<SyntaxElement>) { + if range.start() == range.end() { + self.delete(range.start()); + return; + } + + debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root)); + self.changes.push(Change::ReplaceAll(range, Vec::new())) + } + pub fn replace(&mut self, old: impl Element, new: impl Element) { let old = old.syntax_element(); debug_assert!(is_ancestor_or_self_of_element(&old, &self.root)); @@ -609,6 +619,7 @@ fn test_replace_token_in_parent() { let parent_fn = make::fn_( None, + None, make::name("it"), None, None, @@ -626,10 +637,10 @@ if let Some(ret_ty) = parent_fn.ret_type() { editor.delete(ret_ty.syntax().clone()); - if let Some(SyntaxElement::Token(token)) = ret_ty.syntax().next_sibling_or_token() { - if token.kind().is_trivia() { - editor.delete(token); - } + if let Some(SyntaxElement::Token(token)) = ret_ty.syntax().next_sibling_or_token() + && token.kind().is_trivia() + { + editor.delete(token); } }
diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs index d66ea8a..9090f7c 100644 --- a/crates/syntax/src/syntax_editor/edits.rs +++ b/crates/syntax/src/syntax_editor/edits.rs
@@ -92,6 +92,42 @@ } } +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_items(&self, editor: &mut SyntaxEditor, items: Vec<ast::AssocItem>) { + let (indent, position, whitespace) = match self.assoc_items().last() { + Some(last_item) => ( + IndentLevel::from_node(last_item.syntax()), + Position::after(last_item.syntax()), + "\n\n", + ), + None => match self.l_curly_token() { + Some(l_curly) => { + normalize_ws_between_braces(editor, self.syntax()); + (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") + } + None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), + }, + }; + + let elements: Vec<SyntaxElement> = items + .into_iter() + .enumerate() + .flat_map(|(i, item)| { + let whitespace = if i != 0 { "\n\n" } else { whitespace }; + vec![ + make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), + item.syntax().clone().into(), + ] + }) + .collect(); + editor.insert_all(position, elements); + } +} + impl ast::VariantList { pub fn add_variant(&self, editor: &mut SyntaxEditor, variant: &ast::Variant) { let make = SyntaxFactory::without_mappings(); @@ -117,6 +153,23 @@ } } +impl ast::Fn { + pub fn replace_or_insert_body(&self, editor: &mut SyntaxEditor, body: ast::BlockExpr) { + if let Some(old_body) = self.body() { + editor.replace(old_body.syntax(), body.syntax()); + } else { + let single_space = make::tokens::single_space(); + let elements = vec![single_space.into(), body.syntax().clone().into()]; + + if let Some(semicolon) = self.semicolon_token() { + editor.replace_with_many(semicolon, elements); + } else { + editor.insert_all(Position::last_child_of(self.syntax()), elements); + } + } + } +} + fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> Option<()> { let make = SyntaxFactory::without_mappings(); let l = node @@ -148,6 +201,15 @@ fn remove(&self, editor: &mut SyntaxEditor); } +impl Removable for ast::TypeBoundList { + fn remove(&self, editor: &mut SyntaxEditor) { + match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) { + Some(colon) => editor.delete_all(colon..=self.syntax().clone().into()), + None => editor.delete(self.syntax()), + } + } +} + impl Removable for ast::Use { fn remove(&self, editor: &mut SyntaxEditor) { let make = SyntaxFactory::without_mappings();
diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs index 6fcbdd0..5c28647 100644 --- a/crates/syntax/src/ted.rs +++ b/crates/syntax/src/ted.rs
@@ -90,15 +90,15 @@ insert_all_raw(position, vec![elem.syntax_element()]); } pub fn insert_all(position: Position, mut elements: Vec<SyntaxElement>) { - if let Some(first) = elements.first() { - if let Some(ws) = ws_before(&position, first) { - elements.insert(0, ws.into()); - } + if let Some(first) = elements.first() + && let Some(ws) = ws_before(&position, first) + { + elements.insert(0, ws.into()); } - if let Some(last) = elements.last() { - if let Some(ws) = ws_after(&position, last) { - elements.push(ws.into()); - } + if let Some(last) = elements.last() + && let Some(ws) = ws_after(&position, last) + { + elements.push(ws.into()); } insert_all_raw(position, elements); } @@ -165,20 +165,22 @@ PositionRepr::After(it) => it, }; - if prev.kind() == T!['{'] && new.kind() == SyntaxKind::USE { - if let Some(item_list) = prev.parent().and_then(ast::ItemList::cast) { - let mut indent = IndentLevel::from_element(&item_list.syntax().clone().into()); - indent.0 += 1; - return Some(make::tokens::whitespace(&format!("\n{indent}"))); - } + if prev.kind() == T!['{'] + && new.kind() == SyntaxKind::USE + && let Some(item_list) = prev.parent().and_then(ast::ItemList::cast) + { + let mut indent = IndentLevel::from_element(&item_list.syntax().clone().into()); + indent.0 += 1; + return Some(make::tokens::whitespace(&format!("\n{indent}"))); } - if prev.kind() == T!['{'] && ast::Stmt::can_cast(new.kind()) { - if let Some(stmt_list) = prev.parent().and_then(ast::StmtList::cast) { - let mut indent = IndentLevel::from_element(&stmt_list.syntax().clone().into()); - indent.0 += 1; - return Some(make::tokens::whitespace(&format!("\n{indent}"))); - } + if prev.kind() == T!['{'] + && ast::Stmt::can_cast(new.kind()) + && let Some(stmt_list) = prev.parent().and_then(ast::StmtList::cast) + { + let mut indent = IndentLevel::from_element(&stmt_list.syntax().clone().into()); + indent.0 += 1; + return Some(make::tokens::whitespace(&format!("\n{indent}"))); } ws_between(prev, new)
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index 4180f9c..485140b 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs
@@ -142,50 +142,50 @@ match literal.kind() { ast::LiteralKind::String(s) => { - if !s.is_raw() { - if let Some(without_quotes) = unquote(text, 1, '"') { - unescape_str(without_quotes, |range, char| { - if let Err(err) = char { - push_err(1, range.start, err); - } - }); - } + if !s.is_raw() + && let Some(without_quotes) = unquote(text, 1, '"') + { + unescape_str(without_quotes, |range, char| { + if let Err(err) = char { + push_err(1, range.start, err); + } + }); } } ast::LiteralKind::ByteString(s) => { - if !s.is_raw() { - if let Some(without_quotes) = unquote(text, 2, '"') { - unescape_byte_str(without_quotes, |range, char| { - if let Err(err) = char { - push_err(1, range.start, err); - } - }); - } + if !s.is_raw() + && let Some(without_quotes) = unquote(text, 2, '"') + { + unescape_byte_str(without_quotes, |range, char| { + if let Err(err) = char { + push_err(1, range.start, err); + } + }); } } ast::LiteralKind::CString(s) => { - if !s.is_raw() { - if let Some(without_quotes) = unquote(text, 2, '"') { - unescape_c_str(without_quotes, |range, char| { - if let Err(err) = char { - push_err(1, range.start, err); - } - }); - } + if !s.is_raw() + && let Some(without_quotes) = unquote(text, 2, '"') + { + unescape_c_str(without_quotes, |range, char| { + if let Err(err) = char { + push_err(1, range.start, err); + } + }); } } ast::LiteralKind::Char(_) => { - if let Some(without_quotes) = unquote(text, 1, '\'') { - if let Err(err) = unescape_char(without_quotes) { - push_err(1, 0, err); - } + if let Some(without_quotes) = unquote(text, 1, '\'') + && let Err(err) = unescape_char(without_quotes) + { + push_err(1, 0, err); } } ast::LiteralKind::Byte(_) => { - if let Some(without_quotes) = unquote(text, 2, '\'') { - if let Err(err) = unescape_byte(without_quotes) { - push_err(2, 0, err); - } + if let Some(without_quotes) = unquote(text, 2, '\'') + && let Err(err) = unescape_byte(without_quotes) + { + push_err(2, 0, err); } } ast::LiteralKind::IntNumber(_) @@ -224,14 +224,14 @@ } fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<SyntaxError>) { - if let Some(int_token) = int_token(name_ref) { - if int_token.text().chars().any(|c| !c.is_ascii_digit()) { - errors.push(SyntaxError::new( - "Tuple (struct) field access is only allowed through \ + if let Some(int_token) = int_token(name_ref) + && int_token.text().chars().any(|c| !c.is_ascii_digit()) + { + errors.push(SyntaxError::new( + "Tuple (struct) field access is only allowed through \ decimal integers with no underscores or suffix", - int_token.text_range(), - )); - } + int_token.text_range(), + )); } fn int_token(name_ref: Option<ast::NameRef>) -> Option<SyntaxToken> { @@ -285,13 +285,13 @@ token.text_range(), )); } - } else if let Some(token) = segment.crate_token() { - if !is_path_start || use_prefix(path).is_some() { - errors.push(SyntaxError::new( - "The `crate` keyword is only allowed as the first segment of a path", - token.text_range(), - )); - } + } else if let Some(token) = segment.crate_token() + && (!is_path_start || use_prefix(path).is_some()) + { + errors.push(SyntaxError::new( + "The `crate` keyword is only allowed as the first segment of a path", + token.text_range(), + )); } fn use_prefix(mut path: ast::Path) -> Option<ast::Path> {
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs index 8937e53..4413d2f 100644 --- a/crates/test-fixture/src/lib.rs +++ b/crates/test-fixture/src/lib.rs
@@ -955,12 +955,12 @@ _: String, ) -> Result<TopSubtree, ProcMacroExpansionError> { for tt in subtree.token_trees().flat_tokens() { - if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt { - if ident.sym == sym::cfg || ident.sym == sym::cfg_attr { - return Err(ProcMacroExpansionError::Panic( - "cfg or cfg_attr found in DisallowCfgProcMacroExpander".to_owned(), - )); - } + if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt + && (ident.sym == sym::cfg || ident.sym == sym::cfg_attr) + { + return Err(ProcMacroExpansionError::Panic( + "cfg or cfg_attr found in DisallowCfgProcMacroExpander".to_owned(), + )); } } Ok(subtree.clone())
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 7b719b5..26e0f46 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs
@@ -35,7 +35,7 @@ //! error: fmt //! fmt: option, result, transmute, coerce_unsized, copy, clone, derive //! fmt_before_1_89_0: fmt -//! fn: tuple +//! fn: sized, tuple //! from: sized, result //! future: pin //! coroutine: pin @@ -70,6 +70,7 @@ //! tuple: //! unpin: sized //! unsize: sized +//! write: fmt //! todo: panic //! unimplemented: panic //! column: @@ -325,6 +326,18 @@ *self } } + + impl<T: Clone> Clone for [T; 0] { + fn clone(&self) -> Self { + [] + } + } + + impl<T: Clone> Clone for [T; 1] { + fn clone(&self) -> Self { + [self[0].clone()] + } + } // endregion:builtin_impls // region:derive @@ -1035,6 +1048,7 @@ #[lang = "coroutine"] pub trait Coroutine<R = ()> { + #[lang = "coroutine_yield"] type Yield; #[lang = "coroutine_return"] type Return; @@ -1769,6 +1783,26 @@ } // endregion:panic + // region:write + #[macro_export] + macro_rules! write { + ($dst:expr, $($arg:tt)*) => { + $dst.write_fmt($crate::format_args!($($arg)*)) + }; + } + + #[macro_export] + #[allow_internal_unstable(format_args_nl)] + macro_rules! writeln { + ($dst:expr $(,)?) => { + $crate::write!($dst, "\n") + }; + ($dst:expr, $($arg:tt)*) => { + $dst.write_fmt($crate::format_args_nl!($($arg)*)) + }; + } + // endregion:write + // region:assert #[macro_export] #[rustc_builtin_macro]
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index 4412338..243a27b 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs
@@ -357,10 +357,10 @@ } pub fn try_into_subtree(self) -> Option<SubtreeView<'a, S>> { - if let Some(TokenTree::Subtree(subtree)) = self.0.first() { - if subtree.usize_len() == (self.0.len() - 1) { - return Some(SubtreeView::new(self.0)); - } + if let Some(TokenTree::Subtree(subtree)) = self.0.first() + && subtree.usize_len() == (self.0.len() - 1) + { + return Some(SubtreeView::new(self.0)); } None } @@ -1028,10 +1028,10 @@ tkns = rest; last = [last, tokentree_to_text(tkn, &mut tkns)].join(if last_to_joint { "" } else { " " }); last_to_joint = false; - if let TokenTree::Leaf(Leaf::Punct(punct)) = tkn { - if punct.spacing == Spacing::Joint { - last_to_joint = true; - } + if let TokenTree::Leaf(Leaf::Punct(punct)) = tkn + && punct.spacing == Spacing::Joint + { + last_to_joint = true; } } last
diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index a03337d..c6393cc 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs
@@ -194,52 +194,49 @@ } }, Event::NotifyEvent(event) => { - if let Some(event) = log_notify_error(event) { - if let EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) = + if let Some(event) = log_notify_error(event) + && let EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) = event.kind - { - let files = event - .paths - .into_iter() - .filter_map(|path| { - Some( - AbsPathBuf::try_from( - Utf8PathBuf::from_path_buf(path).ok()?, - ) + { + let files = event + .paths + .into_iter() + .filter_map(|path| { + Some( + AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).ok()?) .expect("path is absolute"), - ) - }) - .filter_map(|path| -> Option<(AbsPathBuf, Option<Vec<u8>>)> { - let meta = fs::metadata(&path).ok()?; - if meta.file_type().is_dir() - && self - .watched_dir_entries - .iter() - .any(|dir| dir.contains_dir(&path)) - { - self.watch(path.as_ref()); - return None; - } + ) + }) + .filter_map(|path| -> Option<(AbsPathBuf, Option<Vec<u8>>)> { + let meta = fs::metadata(&path).ok()?; + if meta.file_type().is_dir() + && self + .watched_dir_entries + .iter() + .any(|dir| dir.contains_dir(&path)) + { + self.watch(path.as_ref()); + return None; + } - if !meta.file_type().is_file() { - return None; - } + if !meta.file_type().is_file() { + return None; + } - if !(self.watched_file_entries.contains(&path) - || self - .watched_dir_entries - .iter() - .any(|dir| dir.contains_file(&path))) - { - return None; - } + if !(self.watched_file_entries.contains(&path) + || self + .watched_dir_entries + .iter() + .any(|dir| dir.contains_file(&path))) + { + return None; + } - let contents = read(&path); - Some((path, contents)) - }) - .collect(); - self.send(loader::Message::Changed { files }); - } + let contents = read(&path); + Some((path, contents)) + }) + .collect(); + self.send(loader::Message::Changed { files }); } } }
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md index ebac26e..6ee956f 100644 --- a/docs/book/src/configuration_generated.md +++ b/docs/book/src/configuration_generated.md
@@ -2,8 +2,7 @@ Default: `false` -Whether to insert #[must_use] when generating `as_` methods -for enum variants. +Insert #[must_use] when generating `as_` methods for enum variants. ## rust-analyzer.assist.expressionFillDefault {#assist.expressionFillDefault} @@ -17,14 +16,15 @@ Default: `false` -When inserting a type (e.g. in "fill match arms" assist), prefer to use `Self` over the type name where possible. +Prefer to use `Self` over the type name when inserting a type (e.g. in "fill match arms" assist). ## rust-analyzer.assist.termSearch.borrowcheck {#assist.termSearch.borrowcheck} Default: `true` -Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check. +Enable borrow checking for term search code assists. If set to false, also there will be +more suggestions, but some of them may not borrow-check. ## rust-analyzer.assist.termSearch.fuel {#assist.termSearch.fuel} @@ -45,7 +45,8 @@ Default: `"physical"` -How many worker threads to handle priming caches. The default `0` means to pick automatically. +How many worker threads to handle priming caches. The default `0` means to pick +automatically. ## rust-analyzer.cargo.allTargets {#cargo.allTargets} @@ -103,7 +104,9 @@ ```bash cargo check --quiet --workspace --message-format=json --all-targets --keep-going ``` -. + +Note: The option must be specified as an array of command line arguments, with +the first argument being the name of the command to run. ## rust-analyzer.cargo.buildScripts.rebuildOnSave {#cargo.buildScripts.rebuildOnSave} @@ -330,7 +333,9 @@ ```bash cargo check --workspace --message-format=json --all-targets ``` -. + +Note: The option must be specified as an array of command line arguments, with +the first argument being the name of the command to run. ## rust-analyzer.check.targets {#check.targets} @@ -358,7 +363,7 @@ Default: `true` -Whether to automatically add a semicolon when completing unit-returning functions. +Automatically add a semicolon when completing unit-returning functions. In `match` arms it completes a comma instead. @@ -367,22 +372,26 @@ Default: `true` -Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. +Show method calls and field accesses completions with `await` prefixed to them when +completing on a future. ## rust-analyzer.completion.autoIter.enable {#completion.autoIter.enable} Default: `true` -Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. +Show method call completions with `iter()` or `into_iter()` prefixed to them when +completing on a type that has them. ## rust-analyzer.completion.autoimport.enable {#completion.autoimport.enable} Default: `true` -Toggles the additional completions that automatically add imports when completed. -Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. +Show completions that automatically add imports when completed. + +Note that your client must specify the `additionalTextEdits` LSP client capability to +truly have this feature enabled. ## rust-analyzer.completion.autoimport.exclude {#completion.autoimport.exclude} @@ -406,10 +415,11 @@ Traits in this list won't have their methods suggested in completions unless the trait is in scope. -You can either specify a string path which defaults to type "always" or use the more verbose -form `{ "path": "path::to::item", type: "always" }`. +You can either specify a string path which defaults to type "always" or use the more +verbose form `{ "path": "path::to::item", type: "always" }`. -For traits the type "methods" can be used to only exclude the methods but not the trait itself. +For traits the type "methods" can be used to only exclude the methods but not the trait +itself. This setting also inherits `#rust-analyzer.completion.excludeTraits#`. @@ -418,15 +428,15 @@ Default: `true` -Toggles the additional completions that automatically show method calls and field accesses -with `self` prefixed to them when inside a method. +Show method calls and field access completions with `self` prefixed to them when +inside a method. ## rust-analyzer.completion.callable.snippets {#completion.callable.snippets} Default: `"fill_arguments"` -Whether to add parenthesis and argument snippets when completing function. +Add parenthesis and argument snippets when completing function. ## rust-analyzer.completion.excludeTraits {#completion.excludeTraits} @@ -435,7 +445,9 @@ A list of full paths to traits whose methods to exclude from completion. -Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. +Methods from these traits won't be completed, even if the trait is in scope. However, +they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or +`T where T: Trait`. Note that the trait themselves can still be completed. @@ -444,14 +456,15 @@ Default: `false` -Whether to show full function/method signatures in completion docs. +Show full function / method signatures in completion docs. ## rust-analyzer.completion.hideDeprecated {#completion.hideDeprecated} Default: `false` -Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. +Omit deprecated items from completions. By default they are marked as deprecated but not +hidden. ## rust-analyzer.completion.limit {#completion.limit} @@ -465,14 +478,15 @@ Default: `true` -Whether to show postfix snippets like `dbg`, `if`, `not`, etc. +Show postfix snippets like `dbg`, `if`, `not`, etc. ## rust-analyzer.completion.privateEditable.enable {#completion.privateEditable.enable} Default: `false` -Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. +Show completions of private items and fields that are defined in the current workspace +even if they are not visible at the current position. ## rust-analyzer.completion.snippets.custom {#completion.snippets.custom} @@ -529,7 +543,7 @@ Default: `false` -Whether to enable term search based snippets like `Some(foo.bar().baz())`. +Enable term search based snippets like `Some(foo.bar().baz())`. ## rust-analyzer.completion.termSearch.fuel {#completion.termSearch.fuel} @@ -550,30 +564,30 @@ Default: `true` -Whether to show native rust-analyzer diagnostics. +Show native rust-analyzer diagnostics. ## rust-analyzer.diagnostics.experimental.enable {#diagnostics.experimental.enable} Default: `false` -Whether to show experimental rust-analyzer diagnostics that might -have more false positives than usual. +Show experimental rust-analyzer diagnostics that might have more false positives than +usual. ## rust-analyzer.diagnostics.remapPrefix {#diagnostics.remapPrefix} Default: `{}` -Map of prefixes to be substituted when parsing diagnostic file paths. -This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. +Map of prefixes to be substituted when parsing diagnostic file paths. This should be the +reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. ## rust-analyzer.diagnostics.styleLints.enable {#diagnostics.styleLints.enable} Default: `false` -Whether to run additional style lints. +Run additional style lints. ## rust-analyzer.diagnostics.warningsAsHint {#diagnostics.warningsAsHint} @@ -582,8 +596,8 @@ List of warnings that should be displayed with hint severity. -The warnings will be indicated by faded text or three dots in code -and will not show up in the `Problems Panel`. +The warnings will be indicated by faded text or three dots in code and will not show up +in the `Problems Panel`. ## rust-analyzer.diagnostics.warningsAsInfo {#diagnostics.warningsAsInfo} @@ -592,17 +606,26 @@ List of warnings that should be displayed with info severity. -The warnings will be indicated by a blue squiggly underline in code -and a blue icon in the `Problems Panel`. +The warnings will be indicated by a blue squiggly underline in code and a blue icon in +the `Problems Panel`. + + +## rust-analyzer.document.symbol.search.excludeLocals {#document.symbol.search.excludeLocals} + +Default: `true` + +Exclude all locals from document symbol search. ## rust-analyzer.files.exclude {#files.exclude} Default: `[]` -These paths (file/directories) will be ignored by rust-analyzer. They are -relative to the workspace root, and globs are not supported. You may -also need to add the folders to Code's `files.watcherExclude`. +List of files to ignore + +These paths (file/directories) will be ignored by rust-analyzer. They are relative to +the workspace root, and globs are not supported. You may also need to add the folders to +Code's `files.watcherExclude`. ## rust-analyzer.files.watcher {#files.watcher} @@ -616,64 +639,67 @@ Default: `true` -Enables highlighting of related return values while the cursor is on any `match`, `if`, or match arm arrow (`=>`). +Highlight related return values while the cursor is on any `match`, `if`, or match arm +arrow (`=>`). ## rust-analyzer.highlightRelated.breakPoints.enable {#highlightRelated.breakPoints.enable} Default: `true` -Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. +Highlight related references while the cursor is on `break`, `loop`, `while`, or `for` +keywords. ## rust-analyzer.highlightRelated.closureCaptures.enable {#highlightRelated.closureCaptures.enable} Default: `true` -Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. +Highlight all captures of a closure while the cursor is on the `|` or move keyword of a closure. ## rust-analyzer.highlightRelated.exitPoints.enable {#highlightRelated.exitPoints.enable} Default: `true` -Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). +Highlight all exit points while the cursor is on any `return`, `?`, `fn`, or return type +arrow (`->`). ## rust-analyzer.highlightRelated.references.enable {#highlightRelated.references.enable} Default: `true` -Enables highlighting of related references while the cursor is on any identifier. +Highlight related references while the cursor is on any identifier. ## rust-analyzer.highlightRelated.yieldPoints.enable {#highlightRelated.yieldPoints.enable} Default: `true` -Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. +Highlight all break points for a loop or block context while the cursor is on any +`async` or `await` keywords. ## rust-analyzer.hover.actions.debug.enable {#hover.actions.debug.enable} Default: `true` -Whether to show `Debug` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. +Show `Debug` action. Only applies when `#rust-analyzer.hover.actions.enable#` is set. ## rust-analyzer.hover.actions.enable {#hover.actions.enable} Default: `true` -Whether to show HoverActions in Rust files. +Show HoverActions in Rust files. ## rust-analyzer.hover.actions.gotoTypeDef.enable {#hover.actions.gotoTypeDef.enable} Default: `true` -Whether to show `Go to Type Definition` action. Only applies when +Show `Go to Type Definition` action. Only applies when `#rust-analyzer.hover.actions.enable#` is set. @@ -681,46 +707,45 @@ Default: `true` -Whether to show `Implementations` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. +Show `Implementations` action. Only applies when `#rust-analyzer.hover.actions.enable#` +is set. ## rust-analyzer.hover.actions.references.enable {#hover.actions.references.enable} Default: `false` -Whether to show `References` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. +Show `References` action. Only applies when `#rust-analyzer.hover.actions.enable#` is +set. ## rust-analyzer.hover.actions.run.enable {#hover.actions.run.enable} Default: `true` -Whether to show `Run` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. +Show `Run` action. Only applies when `#rust-analyzer.hover.actions.enable#` is set. ## rust-analyzer.hover.actions.updateTest.enable {#hover.actions.updateTest.enable} Default: `true` -Whether to show `Update Test` action. Only applies when -`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. +Show `Update Test` action. Only applies when `#rust-analyzer.hover.actions.enable#` and +`#rust-analyzer.hover.actions.run.enable#` are set. ## rust-analyzer.hover.documentation.enable {#hover.documentation.enable} Default: `true` -Whether to show documentation on hover. +Show documentation on hover. ## rust-analyzer.hover.documentation.keywords.enable {#hover.documentation.keywords.enable} Default: `true` -Whether to show keyword hover popups. Only applies when +Show keyword hover popups. Only applies when `#rust-analyzer.hover.documentation.enable#` is set. @@ -728,7 +753,7 @@ Default: `true` -Whether to show drop glue information on hover. +Show drop glue information on hover. ## rust-analyzer.hover.links.enable {#hover.links.enable} @@ -742,9 +767,11 @@ Default: `20` -Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. +Show what types are used as generic arguments in calls etc. on hover, and limit the max +length to show such types, beyond which they will be shown with ellipsis. -This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. +This can take three values: `null` means "unlimited", the string `"hide"` means to not +show generic substitutions at all, and a number means to limit them to X characters. The default is 20 characters. @@ -760,7 +787,7 @@ Default: `true` -Whether to show memory layout data on hover. +Show memory layout data on hover. ## rust-analyzer.hover.memoryLayout.niches {#hover.memoryLayout.niches} @@ -802,7 +829,8 @@ Default: `5` -How many fields of a struct, variant or union to display when hovering on. Show none if empty. +How many fields of a struct, variant or union to display when hovering on. Show none if +empty. ## rust-analyzer.hover.show.traitAssocItems {#hover.show.traitAssocItems} @@ -816,7 +844,8 @@ Default: `false` -Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. +Enforce the import granularity setting for all files. If set to false rust-analyzer will +try to keep import styles consistent per file. ## rust-analyzer.imports.granularity.group {#imports.granularity.group} @@ -830,14 +859,17 @@ Default: `true` -Group inserted imports by the [following order](https://rust-analyzer.github.io/book/features.html#auto-import). Groups are separated by newlines. +Group inserted imports by the [following +order](https://rust-analyzer.github.io/book/features.html#auto-import). Groups are +separated by newlines. ## rust-analyzer.imports.merge.glob {#imports.merge.glob} Default: `true` -Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. +Allow import insertion to merge new imports into single path glob imports like `use +std::fmt::*;`. ## rust-analyzer.imports.preferNoStd {#imports.preferNoStd} @@ -851,7 +883,7 @@ Default: `false` -Whether to prefer import paths containing a `prelude` module. +Prefer import paths containing a `prelude` module. ## rust-analyzer.imports.prefix {#imports.prefix} @@ -865,28 +897,30 @@ Default: `false` -Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". +Prefix external (including std, core) crate imports with `::`. + +E.g. `use ::std::io::Read;`. ## rust-analyzer.inlayHints.bindingModeHints.enable {#inlayHints.bindingModeHints.enable} Default: `false` -Whether to show inlay type hints for binding modes. +Show inlay type hints for binding modes. ## rust-analyzer.inlayHints.chainingHints.enable {#inlayHints.chainingHints.enable} Default: `true` -Whether to show inlay type hints for method chains. +Show inlay type hints for method chains. ## rust-analyzer.inlayHints.closingBraceHints.enable {#inlayHints.closingBraceHints.enable} Default: `true` -Whether to show inlay hints after a closing `}` to indicate what item it belongs to. +Show inlay hints after a closing `}` to indicate what item it belongs to. ## rust-analyzer.inlayHints.closingBraceHints.minLines {#inlayHints.closingBraceHints.minLines} @@ -901,14 +935,14 @@ Default: `false` -Whether to show inlay hints for closure captures. +Show inlay hints for closure captures. ## rust-analyzer.inlayHints.closureReturnTypeHints.enable {#inlayHints.closureReturnTypeHints.enable} Default: `"never"` -Whether to show inlay type hints for return types of closures. +Show inlay type hints for return types of closures. ## rust-analyzer.inlayHints.closureStyle {#inlayHints.closureStyle} @@ -922,77 +956,77 @@ Default: `"never"` -Whether to show enum variant discriminant hints. +Show enum variant discriminant hints. ## rust-analyzer.inlayHints.expressionAdjustmentHints.enable {#inlayHints.expressionAdjustmentHints.enable} Default: `"never"` -Whether to show inlay hints for type adjustments. +Show inlay hints for type adjustments. ## rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe {#inlayHints.expressionAdjustmentHints.hideOutsideUnsafe} Default: `false` -Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. +Hide inlay hints for type adjustments outside of `unsafe` blocks. ## rust-analyzer.inlayHints.expressionAdjustmentHints.mode {#inlayHints.expressionAdjustmentHints.mode} Default: `"prefix"` -Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). +Show inlay hints as postfix ops (`.*` instead of `*`, etc). ## rust-analyzer.inlayHints.genericParameterHints.const.enable {#inlayHints.genericParameterHints.const.enable} Default: `true` -Whether to show const generic parameter name inlay hints. +Show const generic parameter name inlay hints. ## rust-analyzer.inlayHints.genericParameterHints.lifetime.enable {#inlayHints.genericParameterHints.lifetime.enable} Default: `false` -Whether to show generic lifetime parameter name inlay hints. +Show generic lifetime parameter name inlay hints. ## rust-analyzer.inlayHints.genericParameterHints.type.enable {#inlayHints.genericParameterHints.type.enable} Default: `false` -Whether to show generic type parameter name inlay hints. +Show generic type parameter name inlay hints. ## rust-analyzer.inlayHints.implicitDrops.enable {#inlayHints.implicitDrops.enable} Default: `false` -Whether to show implicit drop hints. +Show implicit drop hints. ## rust-analyzer.inlayHints.implicitSizedBoundHints.enable {#inlayHints.implicitSizedBoundHints.enable} Default: `false` -Whether to show inlay hints for the implied type parameter `Sized` bound. +Show inlay hints for the implied type parameter `Sized` bound. ## rust-analyzer.inlayHints.lifetimeElisionHints.enable {#inlayHints.lifetimeElisionHints.enable} Default: `"never"` -Whether to show inlay type hints for elided lifetimes in function signatures. +Show inlay type hints for elided lifetimes in function signatures. ## rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames {#inlayHints.lifetimeElisionHints.useParameterNames} Default: `false` -Whether to prefer using parameter names as the name for elided lifetime hints if possible. +Prefer using parameter names as the name for elided lifetime hints if possible. ## rust-analyzer.inlayHints.maxLength {#inlayHints.maxLength} @@ -1006,23 +1040,24 @@ Default: `true` -Whether to show function parameter name inlay hints at the call -site. +Show function parameter name inlay hints at the call site. ## rust-analyzer.inlayHints.rangeExclusiveHints.enable {#inlayHints.rangeExclusiveHints.enable} Default: `false` -Whether to show exclusive range inlay hints. +Show exclusive range inlay hints. ## rust-analyzer.inlayHints.reborrowHints.enable {#inlayHints.reborrowHints.enable} Default: `"never"` -Whether to show inlay hints for compiler inserted reborrows. -This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. +Show inlay hints for compiler inserted reborrows. + +This setting is deprecated in favor of +#rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. ## rust-analyzer.inlayHints.renderColons {#inlayHints.renderColons} @@ -1036,36 +1071,38 @@ Default: `true` -Whether to show inlay type hints for variables. +Show inlay type hints for variables. ## rust-analyzer.inlayHints.typeHints.hideClosureInitialization {#inlayHints.typeHints.hideClosureInitialization} Default: `false` -Whether to hide inlay type hints for `let` statements that initialize to a closure. -Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. +Hide inlay type hints for `let` statements that initialize to a closure. + +Only applies to closures with blocks, same as +`#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. ## rust-analyzer.inlayHints.typeHints.hideClosureParameter {#inlayHints.typeHints.hideClosureParameter} Default: `false` -Whether to hide inlay parameter type hints for closures. +Hide inlay parameter type hints for closures. ## rust-analyzer.inlayHints.typeHints.hideNamedConstructor {#inlayHints.typeHints.hideNamedConstructor} Default: `false` -Whether to hide inlay type hints for constructors. +Hide inlay type hints for constructors. ## rust-analyzer.interpret.tests {#interpret.tests} Default: `false` -Enables the experimental support for interpreting tests. +Enable the experimental support for interpreting tests. ## rust-analyzer.joinLines.joinAssignments {#joinLines.joinAssignments} @@ -1100,23 +1137,21 @@ Default: `true` -Whether to show `Debug` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. +Show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set. ## rust-analyzer.lens.enable {#lens.enable} Default: `true` -Whether to show CodeLens in Rust files. +Show CodeLens in Rust files. ## rust-analyzer.lens.implementations.enable {#lens.implementations.enable} Default: `true` -Whether to show `Implementations` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. +Show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set. ## rust-analyzer.lens.location {#lens.location} @@ -1130,60 +1165,56 @@ Default: `false` -Whether to show `References` lens for Struct, Enum, and Union. -Only applies when `#rust-analyzer.lens.enable#` is set. +Show `References` lens for Struct, Enum, and Union. Only applies when +`#rust-analyzer.lens.enable#` is set. ## rust-analyzer.lens.references.enumVariant.enable {#lens.references.enumVariant.enable} Default: `false` -Whether to show `References` lens for Enum Variants. -Only applies when `#rust-analyzer.lens.enable#` is set. +Show `References` lens for Enum Variants. Only applies when +`#rust-analyzer.lens.enable#` is set. ## rust-analyzer.lens.references.method.enable {#lens.references.method.enable} Default: `false` -Whether to show `Method References` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. +Show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. ## rust-analyzer.lens.references.trait.enable {#lens.references.trait.enable} Default: `false` -Whether to show `References` lens for Trait. -Only applies when `#rust-analyzer.lens.enable#` is set. +Show `References` lens for Trait. Only applies when `#rust-analyzer.lens.enable#` is +set. ## rust-analyzer.lens.run.enable {#lens.run.enable} Default: `true` -Whether to show `Run` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. +Show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set. ## rust-analyzer.lens.updateTest.enable {#lens.updateTest.enable} Default: `true` -Whether to show `Update Test` lens. Only applies when -`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. +Show `Update Test` lens. Only applies when `#rust-analyzer.lens.enable#` and +`#rust-analyzer.lens.run.enable#` are set. ## rust-analyzer.linkedProjects {#linkedProjects} Default: `[]` -Disable project auto-discovery in favor of explicitly specified set -of projects. +Disable project auto-discovery in favor of explicitly specified set of projects. -Elements must be paths pointing to `Cargo.toml`, -`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON -objects in `rust-project.json` format. +Elements must be paths pointing to `Cargo.toml`, `rust-project.json`, `.rs` files (which +will be treated as standalone files) or JSON objects in `rust-project.json` format. ## rust-analyzer.lru.capacity {#lru.capacity} @@ -1197,21 +1228,22 @@ Default: `{}` -Sets the LRU capacity of the specified queries. +The LRU capacity of the specified queries. ## rust-analyzer.notifications.cargoTomlNotFound {#notifications.cargoTomlNotFound} Default: `true` -Whether to show `can't find Cargo.toml` error message. +Show `can't find Cargo.toml` error message. ## rust-analyzer.numThreads {#numThreads} Default: `null` -How many worker threads in the main loop. The default `null` means to pick automatically. +The number of worker threads in the main loop. The default `null` means to pick +automatically. ## rust-analyzer.procMacro.attributes.enable {#procMacro.attributes.enable} @@ -1322,6 +1354,9 @@ standard input and the formatted result will be read from the standard output. +Note: The option must be specified as an array of command line arguments, with +the first argument being the name of the command to run. + ## rust-analyzer.rustfmt.rangeFormatting.enable {#rustfmt.rangeFormatting.enable} @@ -1346,7 +1381,10 @@ Default: `true` -Whether the server is allowed to emit non-standard tokens and modifiers. +Emit non-standard tokens and modifiers + +When enabled, rust-analyzer will emit tokens and modifiers that are not part of the +standard set of semantic tokens. ## rust-analyzer.semanticHighlighting.operator.enable {#semanticHighlighting.operator.enable} @@ -1427,11 +1465,15 @@ Default: `"=."` Specify the characters allowed to invoke special on typing triggers. -- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression + +- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing + expression - typing `=` between two expressions adds `;` when in statement position -- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position +- typing `=` to turn an assignment into an equality comparison removes `;` when in + expression position - typing `.` in a chain method call auto-indents -- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression +- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the + expression - typing `{` in a use item adds a closing `}` in the right place - typing `>` to complete a return type `->` will insert a whitespace after it - typing `<` in a path or type position inserts a closing `>` after the path or type. @@ -1475,8 +1517,8 @@ **Warning**: This format is provisional and subject to change. -[`DiscoverWorkspaceConfig::command`] *must* return a JSON object -corresponding to `DiscoverProjectData::Finished`: +[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to +`DiscoverProjectData::Finished`: ```norun #[derive(Debug, Clone, Deserialize, Serialize)] @@ -1506,12 +1548,11 @@ } ``` -It is encouraged, but not required, to use the other variants on -`DiscoverProjectData` to provide a more polished end-user experience. +It is encouraged, but not required, to use the other variants on `DiscoverProjectData` +to provide a more polished end-user experience. -`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, -which will be substituted with the JSON-serialized form of the following -enum: +`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be +substituted with the JSON-serialized form of the following enum: ```norun #[derive(PartialEq, Clone, Debug, Serialize)] @@ -1538,11 +1579,10 @@ } ``` -`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, -and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to -to update an existing workspace. As a reference for implementors, -buck2's `rust-project` will likely be useful: -https://github.com/facebook/buck2/tree/main/integrations/rust-project. +`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and +therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an +existing workspace. As a reference for implementors, buck2's `rust-project` will likely +be useful: https://github.com/facebook/buck2/tree/main/integrations/rust-project. ## rust-analyzer.workspace.symbol.search.excludeImports {#workspace.symbol.search.excludeImports}
diff --git a/docs/book/src/contributing/style.md b/docs/book/src/contributing/style.md index 5654e37..fe09fb6 100644 --- a/docs/book/src/contributing/style.md +++ b/docs/book/src/contributing/style.md
@@ -49,8 +49,8 @@ Changes of the third group should be pretty rare, so we don't specify any specific process for them. That said, adding an innocent-looking `pub use` is a very simple way to break encapsulation, keep an eye on it! -Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate -https://www.tedinski.com/2018/02/06/system-boundaries.html +Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate [this post](https://www.tedinski.com/2018/02/06/system-boundaries.html). + ## Crates.io Dependencies @@ -101,7 +101,7 @@ ## Clippy -We use Clippy to improve the code, but if some lints annoy you, allow them in the [Cargo.toml](../../Cargo.toml) [workspace.lints.clippy] section. +We use Clippy to improve the code, but if some lints annoy you, allow them in the [Cargo.toml](https://github.com/rust-lang/rust-analyzer/blob/master/Cargo.toml) [workspace.lints.clippy] section. # Code @@ -231,7 +231,7 @@ } ``` -In the "Not as good" version, the precondition that `1` is a valid char boundary is checked in `is_string_literal` and used in `foo`. +In the "Bad" version, the precondition that `1` and `s.len() - 1` are valid string literal boundaries is checked in `is_string_literal` but used in `main`. In the "Good" version, the precondition check and usage are checked in the same block, and then encoded in the types. **Rationale:** non-local code properties degrade under change. @@ -271,6 +271,8 @@ } ``` +See also [this post](https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-down.html) + ## Assertions Assert liberally. @@ -608,7 +610,7 @@ ```rust // GOOD -fn frobnicate(f: impl FnMut()) { +fn frobnicate(mut f: impl FnMut()) { frobnicate_impl(&mut f) } fn frobnicate_impl(f: &mut dyn FnMut()) { @@ -616,7 +618,7 @@ } // BAD -fn frobnicate(f: impl FnMut()) { +fn frobnicate(mut f: impl FnMut()) { // lots of code } ``` @@ -975,7 +977,7 @@ **Rationale:** consistency & simplicity. `ref` was required before [match ergonomics](https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md). Today, it is redundant. -Between `ref` and mach ergonomics, the latter is more ergonomic in most cases, and is simpler (does not require a keyword). +Between `ref` and match ergonomics, the latter is more ergonomic in most cases, and is simpler (does not require a keyword). ## Empty Match Arms
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 534c24b..ad8708e 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json
@@ -26,7 +26,7 @@ "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-electron": "^2.4.1", - "@vscode/vsce": "^3.2.2", + "@vscode/vsce": "^3.6.0", "esbuild": "^0.25.0", "eslint": "^9.21.0", "eslint-config-prettier": "^10.0.2", @@ -41,6 +41,23 @@ "vscode": "^1.93.0" } }, + "node_modules/@azu/format-text": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, "node_modules/@azure/abort-controller": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", @@ -212,6 +229,31 @@ "node": ">=16" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", @@ -947,6 +989,217 @@ "node": ">= 8" } }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@secretlint/config-loader/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "@textlint/linter-formatter": "^15.2.0", + "@textlint/module-interop": "^15.2.0", + "@textlint/types": "^15.2.0", + "chalk": "^5.4.1", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^7.1.0", + "table": "^6.9.0", + "terminal-link": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", + "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-loader": "^10.2.2", + "@secretlint/core": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "@secretlint/source-creator": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@stylistic/eslint-plugin": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.1.0.tgz", @@ -984,6 +1237,136 @@ "eslint": ">=9.0.0" } }, + "node_modules/@textlint/ast-node-types": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.2.1.tgz", + "integrity": "sha512-20fEcLPsXg81yWpApv4FQxrZmlFF/Ta7/kz1HGIL+pJo5cSTmkc+eCki3GpOPZIoZk0tbJU8hrlwUb91F+3SNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.2.1.tgz", + "integrity": "sha512-oollG/BHa07+mMt372amxHohteASC+Zxgollc1sZgiyxo4S6EuureV3a4QIQB0NecA+Ak3d0cl0WI/8nou38jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.2.1", + "@textlint/resolver": "15.2.1", + "@textlint/types": "15.2.1", + "chalk": "^4.1.2", + "debug": "^4.4.1", + "js-yaml": "^3.14.1", + "lodash": "^4.17.21", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.2.1.tgz", + "integrity": "sha512-b/C/ZNrm05n1ypymDknIcpkBle30V2ZgE3JVqQlA9PnQV46Ky510qrZk6s9yfKgA3m1YRnAw04m8xdVtqjq1qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.2.1.tgz", + "integrity": "sha512-FY3aK4tElEcOJVUsaMj4Zro4jCtKEEwUMIkDL0tcn6ljNcgOF7Em+KskRRk/xowFWayqDtdz5T3u7w/6fjjuJQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.2.1.tgz", + "integrity": "sha512-zyqNhSatK1cwxDUgosEEN43hFh3WCty9Zm2Vm3ogU566IYegifwqN54ey/CiRy/DiO4vMcFHykuQnh2Zwp6LLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "15.2.1" + } + }, "node_modules/@tsconfig/strictest": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@tsconfig/strictest/-/strictest-2.0.5.tgz", @@ -1015,6 +1398,20 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/vscode": { "version": "1.93.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.93.0.tgz", @@ -1220,16 +1617,20 @@ } }, "node_modules/@vscode/vsce": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.2.tgz", - "integrity": "sha512-4TqdUq/yKlQTHcQMk/DamR632bq/+IJDomSbexOMee/UAYWqYm0XHWA6scGslsCpzY+sCWEhhl0nqdOB0XW1kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.6.0.tgz", + "integrity": "sha512-u2ZoMfymRNJb14aHNawnXJtXHLXDVKc1oKZaH4VELKT/9iWKRVgtQOdwxCgtwSxJoqYvuK4hGlBWQJ05wxADhg==", "dev": true, "license": "MIT", "dependencies": { "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.1", + "@secretlint/secretlint-formatter-sarif": "^10.1.1", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.1", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.1", "@vscode/vsce-sign": "^2.0.0", "azure-devops-node-api": "^12.5.0", - "chalk": "^2.4.2", + "chalk": "^4.1.2", "cheerio": "^1.0.0-rc.9", "cockatiel": "^3.1.2", "commander": "^12.1.0", @@ -1243,6 +1644,7 @@ "minimatch": "^3.0.3", "parse-semver": "^1.1.1", "read": "^1.0.7", + "secretlint": "^10.1.1", "semver": "^7.5.2", "tmp": "^0.2.3", "typed-rest-client": "^1.8.4", @@ -1486,6 +1888,22 @@ "integrity": "sha512-PMqBCBvrOVDRqLGooQb+z+t1Q0PiPyurUQeZRR5uHBOVZcW8B04KMmnT12USnhpNX2wCPagWzLVppQMUG3u0Dw==", "license": "MIT" }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -1500,16 +1918,18 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { @@ -1519,6 +1939,16 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1564,6 +1994,22 @@ ], "license": "MIT" }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -1598,6 +2044,13 @@ "dev": true, "license": "ISC" }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -1720,18 +2173,20 @@ } }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/cheerio": { @@ -1845,39 +2300,6 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/cliui/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1938,20 +2360,21 @@ } }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/combined-stream": { @@ -2469,9 +2892,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2685,6 +3108,23 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -2730,6 +3170,19 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2829,16 +3282,6 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/eslint": { "version": "9.21.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz", @@ -2964,22 +3407,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2991,43 +3418,6 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3041,16 +3431,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3064,19 +3444,6 @@ "node": "*" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -3095,6 +3462,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -3213,6 +3594,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", @@ -3360,6 +3758,21 @@ "license": "MIT", "optional": true }, + "node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3492,6 +3905,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -3505,6 +3949,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3513,13 +3964,13 @@ "license": "MIT" }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/has-symbols": { @@ -3702,6 +4153,19 @@ "node": ">=0.8.19" } }, + "node_modules/index-to-position": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", + "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3872,6 +4336,24 @@ "dev": true, "license": "ISC" }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/jackspeak": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", @@ -3897,6 +4379,13 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3931,6 +4420,19 @@ "dev": true, "license": "MIT" }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", @@ -3938,6 +4440,19 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -4103,6 +4618,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -4159,6 +4681,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", @@ -4430,6 +4959,55 @@ "license": "MIT", "optional": true }, + "node_modules/node-sarif-builder": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.2.0.tgz", + "integrity": "sha512-kVIOdynrF2CRodHZeP/97Rh1syTUHBNiw17hUCIVhlhEsWlfJm19MuO56s4MdKbr22xWx6mzMnNAgXzVlIYM9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -4661,6 +5239,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -4688,6 +5279,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse-semver": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", @@ -4795,6 +5404,19 @@ "node": "20 || >=22" } }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -4802,6 +5424,13 @@ "dev": true, "license": "MIT" }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", @@ -4815,6 +5444,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -4962,6 +5601,19 @@ "rc": "cli.js" } }, + "node_modules/rc-config-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.3.tgz", + "integrity": "sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "js-yaml": "^4.1.0", + "json5": "^2.2.2", + "require-from-string": "^2.0.2" + } + }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -4986,6 +5638,39 @@ "node": ">=0.8" } }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -5018,6 +5703,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -5146,6 +5841,28 @@ "dev": true, "license": "ISC" }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-creator": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/node": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^9.0.1" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", @@ -5326,6 +6043,80 @@ "simple-concat": "^1.0.0" } }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/stdin-discarder": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", @@ -5487,17 +6278,130 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/tar-fs": { @@ -5587,10 +6491,50 @@ "node": ">= 6" } }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", + "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", "dev": true, "license": "MIT", "engines": { @@ -5667,6 +6611,19 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typed-rest-client": { "version": "1.8.11", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", @@ -5747,6 +6704,29 @@ "dev": true, "license": "MIT" }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5781,6 +6761,30 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", @@ -5928,42 +6932,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json index 3cb4c21..4975ca8 100644 --- a/editors/code/package.json +++ b/editors/code/package.json
@@ -63,7 +63,7 @@ "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-electron": "^2.4.1", - "@vscode/vsce": "^3.2.2", + "@vscode/vsce": "^3.6.0", "esbuild": "^0.25.0", "eslint": "^9.21.0", "eslint-config-prettier": "^10.0.2", @@ -354,35 +354,122 @@ ], "configuration": [ { - "title": "general", + "title": "Rust Analyzer" + }, + { + "title": "Assist" + }, + { + "title": "Cache Priming" + }, + { + "title": "Cargo" + }, + { + "title": "Cfg" + }, + { + "title": "Check" + }, + { + "title": "Completion" + }, + { + "title": "Debug" + }, + { + "title": "Diagnostics" + }, + { + "title": "Files" + }, + { + "title": "Highlight Related" + }, + { + "title": "Hover" + }, + { + "title": "Imports" + }, + { + "title": "Inlay Hints" + }, + { + "title": "Interpret" + }, + { + "title": "Join Lines" + }, + { + "title": "Lens" + }, + { + "title": "Lru" + }, + { + "title": "Notifications" + }, + { + "title": "Proc Macro" + }, + { + "title": "References" + }, + { + "title": "Runnables" + }, + { + "title": "Rustc" + }, + { + "title": "Rustfmt" + }, + { + "title": "Semantic Highlighting" + }, + { + "title": "Signature Info" + }, + { + "title": "Typing" + }, + { + "title": "Vfs" + }, + { + "title": "Workspace" + }, + { + "title": "rust-analyzer", "properties": { "rust-analyzer.restartServerOnConfigChange": { - "markdownDescription": "Whether to restart the server automatically when certain settings that require a restart are changed.", + "description": "Restart the server automatically when settings that require a restart are changed.", "default": false, "type": "boolean" }, "rust-analyzer.showUnlinkedFileNotification": { - "markdownDescription": "Whether to show a notification for unlinked files asking the user to add the corresponding Cargo.toml to the linked projects setting.", + "description": "Show a notification for unlinked files, prompting the user to add the corresponding Cargo.toml to the linked projects setting.", "default": true, "type": "boolean" }, "rust-analyzer.showRequestFailedErrorNotification": { - "markdownDescription": "Whether to show error notifications for failing requests.", + "description": "Show error notifications when requests fail.", "default": true, "type": "boolean" }, "rust-analyzer.showDependenciesExplorer": { - "markdownDescription": "Whether to show the dependencies view.", + "description": "Show Rust Dependencies in the Explorer view.", "default": true, "type": "boolean" }, "rust-analyzer.showSyntaxTree": { - "markdownDescription": "Whether to show the syntax tree view.", + "description": "Show Syntax Tree in the Explorer view.", "default": false, "type": "boolean" }, "rust-analyzer.testExplorer": { - "markdownDescription": "Whether to show the test explorer.", + "description": "Show the Test Explorer view.", "default": false, "type": "boolean" }, @@ -394,7 +481,7 @@ } }, { - "title": "runnables", + "title": "Runnables", "properties": { "rust-analyzer.runnables.extraEnv": { "anyOf": [ @@ -452,7 +539,7 @@ } }, { - "title": "statusBar", + "title": "Status Bar", "properties": { "rust-analyzer.statusBar.clickAction": { "type": "string", @@ -524,7 +611,7 @@ } }, { - "title": "server", + "title": "Server", "properties": { "rust-analyzer.server.path": { "type": [ @@ -553,7 +640,7 @@ } }, { - "title": "trace", + "title": "Trace", "properties": { "rust-analyzer.trace.server": { "type": "string", @@ -580,7 +667,7 @@ } }, { - "title": "debug", + "title": "Debug", "properties": { "rust-analyzer.debug.engine": { "type": "string", @@ -625,7 +712,7 @@ } }, { - "title": "typing", + "title": "Typing", "properties": { "rust-analyzer.typing.continueCommentsOnNewline": { "markdownDescription": "Whether to prefix newlines after comments with the corresponding comment prefix.", @@ -635,7 +722,7 @@ } }, { - "title": "diagnostics", + "title": "Diagnostics", "properties": { "rust-analyzer.diagnostics.previewRustcOutput": { "markdownDescription": "Whether to show the main part of the rendered rustc output of a diagnostic message.", @@ -653,17 +740,17 @@ "title": "$generated-start" }, { - "title": "assist", + "title": "Assist", "properties": { "rust-analyzer.assist.emitMustUse": { - "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.", + "markdownDescription": "Insert #[must_use] when generating `as_` methods for enum variants.", "default": false, "type": "boolean" } } }, { - "title": "assist", + "title": "Assist", "properties": { "rust-analyzer.assist.expressionFillDefault": { "markdownDescription": "Placeholder expression to use for missing expressions in assists.", @@ -681,27 +768,27 @@ } }, { - "title": "assist", + "title": "Assist", "properties": { "rust-analyzer.assist.preferSelf": { - "markdownDescription": "When inserting a type (e.g. in \"fill match arms\" assist), prefer to use `Self` over the type name where possible.", + "markdownDescription": "Prefer to use `Self` over the type name when inserting a type (e.g. in \"fill match arms\" assist).", "default": false, "type": "boolean" } } }, { - "title": "assist", + "title": "Assist", "properties": { "rust-analyzer.assist.termSearch.borrowcheck": { - "markdownDescription": "Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check.", + "markdownDescription": "Enable borrow checking for term search code assists. If set to false, also there will be\nmore suggestions, but some of them may not borrow-check.", "default": true, "type": "boolean" } } }, { - "title": "assist", + "title": "Assist", "properties": { "rust-analyzer.assist.termSearch.fuel": { "markdownDescription": "Term search fuel in \"units of work\" for assists (Defaults to 1800).", @@ -712,7 +799,7 @@ } }, { - "title": "cachePriming", + "title": "Cache Priming", "properties": { "rust-analyzer.cachePriming.enable": { "markdownDescription": "Warm up caches on project load.", @@ -722,10 +809,10 @@ } }, { - "title": "cachePriming", + "title": "Cache Priming", "properties": { "rust-analyzer.cachePriming.numThreads": { - "markdownDescription": "How many worker threads to handle priming caches. The default `0` means to pick automatically.", + "markdownDescription": "How many worker threads to handle priming caches. The default `0` means to pick\nautomatically.", "default": "physical", "anyOf": [ { @@ -749,7 +836,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.allTargets": { "markdownDescription": "Pass `--all-targets` to cargo invocation.", @@ -759,7 +846,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.autoreload": { "markdownDescription": "Automatically refresh project info via `cargo metadata` on\n`Cargo.toml` or `.cargo/config.toml` changes.", @@ -769,7 +856,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.buildScripts.enable": { "markdownDescription": "Run build scripts (`build.rs`) for more precise code analysis.", @@ -779,7 +866,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.buildScripts.invocationStrategy": { "markdownDescription": "Specifies the invocation strategy to use when running the build scripts command.\nIf `per_workspace` is set, the command will be executed for each Rust workspace with the\nworkspace as the working directory.\nIf `once` is set, the command will be executed once with the opened project as the\nworking directory.\nThis config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`\nis set.", @@ -797,10 +884,10 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.buildScripts.overrideCommand": { - "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets --keep-going\n```\n.", + "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets --keep-going\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.", "default": null, "type": [ "null", @@ -813,7 +900,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.buildScripts.rebuildOnSave": { "markdownDescription": "Rerun proc-macros building/build-scripts running when proc-macro\nor build-script sources change and are saved.", @@ -823,7 +910,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.buildScripts.useRustcWrapper": { "markdownDescription": "Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to\navoid checking unnecessary things.", @@ -833,7 +920,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.cfgs": { "markdownDescription": "List of cfg options to enable with the given values.\n\nTo enable a name without a value, use `\"key\"`.\nTo enable a name with a value, use `\"key=value\"`.\nTo disable, prefix the entry with a `!`.", @@ -849,7 +936,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.extraArgs": { "markdownDescription": "Extra arguments that are passed to every cargo invocation.", @@ -862,7 +949,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.extraEnv": { "markdownDescription": "Extra environment variables that will be set when running cargo, rustc\nor other commands within the workspace. Useful for setting RUSTFLAGS.", @@ -872,7 +959,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.features": { "markdownDescription": "List of features to activate.\n\nSet this to `\"all\"` to pass `--all-features` to cargo.", @@ -898,7 +985,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.noDefaultFeatures": { "markdownDescription": "Whether to pass `--no-default-features` to cargo.", @@ -908,7 +995,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.noDeps": { "markdownDescription": "Whether to skip fetching dependencies. If set to \"true\", the analysis is performed\nentirely offline, and Cargo metadata for dependencies is not fetched.", @@ -918,7 +1005,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.sysroot": { "markdownDescription": "Relative path to the sysroot, or \"discover\" to try to automatically find it via\n\"rustc --print sysroot\".\n\nUnsetting this disables sysroot loading.\n\nThis option does not take effect until rust-analyzer is restarted.", @@ -931,7 +1018,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.sysrootSrc": { "markdownDescription": "Relative path to the sysroot library sources. If left unset, this will default to\n`{cargo.sysroot}/lib/rustlib/src/rust/library`.\n\nThis option does not take effect until rust-analyzer is restarted.", @@ -944,7 +1031,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.target": { "markdownDescription": "Compilation target override (target tuple).", @@ -957,7 +1044,7 @@ } }, { - "title": "cargo", + "title": "Cargo", "properties": { "rust-analyzer.cargo.targetDir": { "markdownDescription": "Optional path to a rust-analyzer specific target directory.\nThis prevents rust-analyzer's `cargo check` and initial build-script and proc-macro\nbuilding from locking the `Cargo.lock` at the expense of duplicating build artifacts.\n\nSet to `true` to use a subdirectory of the existing target directory or\nset to a path relative to the workspace to use that path.", @@ -977,7 +1064,7 @@ } }, { - "title": "cfg", + "title": "Cfg", "properties": { "rust-analyzer.cfg.setTest": { "markdownDescription": "Set `cfg(test)` for local crates. Defaults to true.", @@ -987,7 +1074,7 @@ } }, { - "title": "general", + "title": "rust-analyzer", "properties": { "rust-analyzer.checkOnSave": { "markdownDescription": "Run the check command for diagnostics on save.", @@ -997,7 +1084,7 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.allTargets": { "markdownDescription": "Check all targets and tests (`--all-targets`). Defaults to\n`#rust-analyzer.cargo.allTargets#`.", @@ -1010,7 +1097,7 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.command": { "markdownDescription": "Cargo command to use for `cargo check`.", @@ -1020,7 +1107,7 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.extraArgs": { "markdownDescription": "Extra arguments for `cargo check`.", @@ -1033,7 +1120,7 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.extraEnv": { "markdownDescription": "Extra environment variables that will be set when running `cargo check`.\nExtends `#rust-analyzer.cargo.extraEnv#`.", @@ -1043,7 +1130,7 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.features": { "markdownDescription": "List of features to activate. Defaults to\n`#rust-analyzer.cargo.features#`.\n\nSet to `\"all\"` to pass `--all-features` to Cargo.", @@ -1072,7 +1159,7 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.ignore": { "markdownDescription": "List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.\n\nFor example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...", @@ -1086,7 +1173,7 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.invocationStrategy": { "markdownDescription": "Specifies the invocation strategy to use when running the check command.\nIf `per_workspace` is set, the command will be executed for each workspace.\nIf `once` is set, the command will be executed once.\nThis config only has an effect when `#rust-analyzer.check.overrideCommand#`\nis set.", @@ -1104,7 +1191,7 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.noDefaultFeatures": { "markdownDescription": "Whether to pass `--no-default-features` to Cargo. Defaults to\n`#rust-analyzer.cargo.noDefaultFeatures#`.", @@ -1117,10 +1204,10 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.overrideCommand": { - "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.", + "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.", "default": null, "type": [ "null", @@ -1133,7 +1220,7 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.targets": { "markdownDescription": "Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.\n\nCan be a single target, e.g. `\"x86_64-unknown-linux-gnu\"` or a list of targets, e.g.\n`[\"aarch64-apple-darwin\", \"x86_64-apple-darwin\"]`.\n\nAliased as `\"checkOnSave.targets\"`.", @@ -1156,7 +1243,7 @@ } }, { - "title": "check", + "title": "Check", "properties": { "rust-analyzer.check.workspace": { "markdownDescription": "Whether `--workspace` should be passed to `cargo check`.\nIf false, `-p <package>` will be passed instead if applicable. In case it is not, no\ncheck will be performed.", @@ -1166,50 +1253,50 @@ } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.addSemicolonToUnit": { - "markdownDescription": "Whether to automatically add a semicolon when completing unit-returning functions.\n\nIn `match` arms it completes a comma instead.", + "markdownDescription": "Automatically add a semicolon when completing unit-returning functions.\n\nIn `match` arms it completes a comma instead.", "default": true, "type": "boolean" } } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.autoAwait.enable": { - "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future.", + "markdownDescription": "Show method calls and field accesses completions with `await` prefixed to them when\ncompleting on a future.", "default": true, "type": "boolean" } } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.autoIter.enable": { - "markdownDescription": "Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them.", + "markdownDescription": "Show method call completions with `iter()` or `into_iter()` prefixed to them when\ncompleting on a type that has them.", "default": true, "type": "boolean" } } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.autoimport.enable": { - "markdownDescription": "Toggles the additional completions that automatically add imports when completed.\nNote that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.", + "markdownDescription": "Show completions that automatically add imports when completed.\n\nNote that your client must specify the `additionalTextEdits` LSP client capability to\ntruly have this feature enabled.", "default": true, "type": "boolean" } } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.autoimport.exclude": { - "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more verbose\nform `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait itself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", + "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", "default": [ { "path": "core::borrow::Borrow", @@ -1251,20 +1338,20 @@ } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.autoself.enable": { - "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses\nwith `self` prefixed to them when inside a method.", + "markdownDescription": "Show method calls and field access completions with `self` prefixed to them when\ninside a method.", "default": true, "type": "boolean" } } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.callable.snippets": { - "markdownDescription": "Whether to add parenthesis and argument snippets when completing function.", + "markdownDescription": "Add parenthesis and argument snippets when completing function.", "default": "fill_arguments", "type": "string", "enum": [ @@ -1281,10 +1368,10 @@ } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.excludeTraits": { - "markdownDescription": "A list of full paths to traits whose methods to exclude from completion.\n\nMethods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`.\n\nNote that the trait themselves can still be completed.", + "markdownDescription": "A list of full paths to traits whose methods to exclude from completion.\n\nMethods from these traits won't be completed, even if the trait is in scope. However,\nthey will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or\n`T where T: Trait`.\n\nNote that the trait themselves can still be completed.", "default": [], "type": "array", "items": { @@ -1294,27 +1381,27 @@ } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.fullFunctionSignatures.enable": { - "markdownDescription": "Whether to show full function/method signatures in completion docs.", + "markdownDescription": "Show full function / method signatures in completion docs.", "default": false, "type": "boolean" } } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.hideDeprecated": { - "markdownDescription": "Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden.", + "markdownDescription": "Omit deprecated items from completions. By default they are marked as deprecated but not\nhidden.", "default": false, "type": "boolean" } } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.limit": { "markdownDescription": "Maximum number of completions to return. If `None`, the limit is infinite.", @@ -1328,27 +1415,27 @@ } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.postfix.enable": { - "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc.", + "markdownDescription": "Show postfix snippets like `dbg`, `if`, `not`, etc.", "default": true, "type": "boolean" } } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.privateEditable.enable": { - "markdownDescription": "Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.", + "markdownDescription": "Show completions of private items and fields that are defined in the current workspace\neven if they are not visible at the current position.", "default": false, "type": "boolean" } } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.snippets.custom": { "markdownDescription": "Custom completion snippets.", @@ -1398,17 +1485,17 @@ } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.termSearch.enable": { - "markdownDescription": "Whether to enable term search based snippets like `Some(foo.bar().baz())`.", + "markdownDescription": "Enable term search based snippets like `Some(foo.bar().baz())`.", "default": false, "type": "boolean" } } }, { - "title": "completion", + "title": "Completion", "properties": { "rust-analyzer.completion.termSearch.fuel": { "markdownDescription": "Term search fuel in \"units of work\" for autocompletion (Defaults to 1000).", @@ -1419,7 +1506,7 @@ } }, { - "title": "diagnostics", + "title": "Diagnostics", "properties": { "rust-analyzer.diagnostics.disabled": { "markdownDescription": "List of rust-analyzer diagnostics to disable.", @@ -1433,50 +1520,50 @@ } }, { - "title": "diagnostics", + "title": "Diagnostics", "properties": { "rust-analyzer.diagnostics.enable": { - "markdownDescription": "Whether to show native rust-analyzer diagnostics.", + "markdownDescription": "Show native rust-analyzer diagnostics.", "default": true, "type": "boolean" } } }, { - "title": "diagnostics", + "title": "Diagnostics", "properties": { "rust-analyzer.diagnostics.experimental.enable": { - "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might\nhave more false positives than usual.", + "markdownDescription": "Show experimental rust-analyzer diagnostics that might have more false positives than\nusual.", "default": false, "type": "boolean" } } }, { - "title": "diagnostics", + "title": "Diagnostics", "properties": { "rust-analyzer.diagnostics.remapPrefix": { - "markdownDescription": "Map of prefixes to be substituted when parsing diagnostic file paths.\nThis should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.", + "markdownDescription": "Map of prefixes to be substituted when parsing diagnostic file paths. This should be the\nreverse mapping of what is passed to `rustc` as `--remap-path-prefix`.", "default": {}, "type": "object" } } }, { - "title": "diagnostics", + "title": "Diagnostics", "properties": { "rust-analyzer.diagnostics.styleLints.enable": { - "markdownDescription": "Whether to run additional style lints.", + "markdownDescription": "Run additional style lints.", "default": false, "type": "boolean" } } }, { - "title": "diagnostics", + "title": "Diagnostics", "properties": { "rust-analyzer.diagnostics.warningsAsHint": { - "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.", + "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code and will not show up\nin the `Problems Panel`.", "default": [], "type": "array", "items": { @@ -1486,10 +1573,10 @@ } }, { - "title": "diagnostics", + "title": "Diagnostics", "properties": { "rust-analyzer.diagnostics.warningsAsInfo": { - "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.", + "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in\nthe `Problems Panel`.", "default": [], "type": "array", "items": { @@ -1499,10 +1586,20 @@ } }, { - "title": "files", + "title": "Document", + "properties": { + "rust-analyzer.document.symbol.search.excludeLocals": { + "markdownDescription": "Exclude all locals from document symbol search.", + "default": true, + "type": "boolean" + } + } + }, + { + "title": "Files", "properties": { "rust-analyzer.files.exclude": { - "markdownDescription": "These paths (file/directories) will be ignored by rust-analyzer. They are\nrelative to the workspace root, and globs are not supported. You may\nalso need to add the folders to Code's `files.watcherExclude`.", + "markdownDescription": "List of files to ignore\n\nThese paths (file/directories) will be ignored by rust-analyzer. They are relative to\nthe workspace root, and globs are not supported. You may also need to add the folders to\nCode's `files.watcherExclude`.", "default": [], "type": "array", "items": { @@ -1512,7 +1609,7 @@ } }, { - "title": "files", + "title": "Files", "properties": { "rust-analyzer.files.watcher": { "markdownDescription": "Controls file watching implementation.", @@ -1530,167 +1627,167 @@ } }, { - "title": "highlightRelated", + "title": "Highlight Related", "properties": { "rust-analyzer.highlightRelated.branchExitPoints.enable": { - "markdownDescription": "Enables highlighting of related return values while the cursor is on any `match`, `if`, or match arm arrow (`=>`).", + "markdownDescription": "Highlight related return values while the cursor is on any `match`, `if`, or match arm\narrow (`=>`).", "default": true, "type": "boolean" } } }, { - "title": "highlightRelated", + "title": "Highlight Related", "properties": { "rust-analyzer.highlightRelated.breakPoints.enable": { - "markdownDescription": "Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.", + "markdownDescription": "Highlight related references while the cursor is on `break`, `loop`, `while`, or `for`\nkeywords.", "default": true, "type": "boolean" } } }, { - "title": "highlightRelated", + "title": "Highlight Related", "properties": { "rust-analyzer.highlightRelated.closureCaptures.enable": { - "markdownDescription": "Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.", + "markdownDescription": "Highlight all captures of a closure while the cursor is on the `|` or move keyword of a closure.", "default": true, "type": "boolean" } } }, { - "title": "highlightRelated", + "title": "Highlight Related", "properties": { "rust-analyzer.highlightRelated.exitPoints.enable": { - "markdownDescription": "Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).", + "markdownDescription": "Highlight all exit points while the cursor is on any `return`, `?`, `fn`, or return type\narrow (`->`).", "default": true, "type": "boolean" } } }, { - "title": "highlightRelated", + "title": "Highlight Related", "properties": { "rust-analyzer.highlightRelated.references.enable": { - "markdownDescription": "Enables highlighting of related references while the cursor is on any identifier.", + "markdownDescription": "Highlight related references while the cursor is on any identifier.", "default": true, "type": "boolean" } } }, { - "title": "highlightRelated", + "title": "Highlight Related", "properties": { "rust-analyzer.highlightRelated.yieldPoints.enable": { - "markdownDescription": "Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.", + "markdownDescription": "Highlight all break points for a loop or block context while the cursor is on any\n`async` or `await` keywords.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.actions.debug.enable": { - "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", + "markdownDescription": "Show `Debug` action. Only applies when `#rust-analyzer.hover.actions.enable#` is set.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.actions.enable": { - "markdownDescription": "Whether to show HoverActions in Rust files.", + "markdownDescription": "Show HoverActions in Rust files.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.actions.gotoTypeDef.enable": { - "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", + "markdownDescription": "Show `Go to Type Definition` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.actions.implementations.enable": { - "markdownDescription": "Whether to show `Implementations` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", + "markdownDescription": "Show `Implementations` action. Only applies when `#rust-analyzer.hover.actions.enable#`\nis set.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.actions.references.enable": { - "markdownDescription": "Whether to show `References` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", + "markdownDescription": "Show `References` action. Only applies when `#rust-analyzer.hover.actions.enable#` is\nset.", "default": false, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.actions.run.enable": { - "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` is set.", + "markdownDescription": "Show `Run` action. Only applies when `#rust-analyzer.hover.actions.enable#` is set.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.actions.updateTest.enable": { - "markdownDescription": "Whether to show `Update Test` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set.", + "markdownDescription": "Show `Update Test` action. Only applies when `#rust-analyzer.hover.actions.enable#` and\n`#rust-analyzer.hover.actions.run.enable#` are set.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.documentation.enable": { - "markdownDescription": "Whether to show documentation on hover.", + "markdownDescription": "Show documentation on hover.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.documentation.keywords.enable": { - "markdownDescription": "Whether to show keyword hover popups. Only applies when\n`#rust-analyzer.hover.documentation.enable#` is set.", + "markdownDescription": "Show keyword hover popups. Only applies when\n`#rust-analyzer.hover.documentation.enable#` is set.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.dropGlue.enable": { - "markdownDescription": "Whether to show drop glue information on hover.", + "markdownDescription": "Show drop glue information on hover.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.links.enable": { "markdownDescription": "Use markdown syntax for links on hover.", @@ -1700,10 +1797,10 @@ } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.maxSubstitutionLength": { - "markdownDescription": "Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.\n\nThis can take three values: `null` means \"unlimited\", the string `\"hide\"` means to not show generic substitutions at all, and a number means to limit them to X characters.\n\nThe default is 20 characters.", + "markdownDescription": "Show what types are used as generic arguments in calls etc. on hover, and limit the max\nlength to show such types, beyond which they will be shown with ellipsis.\n\nThis can take three values: `null` means \"unlimited\", the string `\"hide\"` means to not\nshow generic substitutions at all, and a number means to limit them to X characters.\n\nThe default is 20 characters.", "default": 20, "anyOf": [ { @@ -1723,7 +1820,7 @@ } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.memoryLayout.alignment": { "markdownDescription": "How to render the align information in a memory layout hover.", @@ -1750,17 +1847,17 @@ } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.memoryLayout.enable": { - "markdownDescription": "Whether to show memory layout data on hover.", + "markdownDescription": "Show memory layout data on hover.", "default": true, "type": "boolean" } } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.memoryLayout.niches": { "markdownDescription": "How to render the niche information in a memory layout hover.", @@ -1773,7 +1870,7 @@ } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.memoryLayout.offset": { "markdownDescription": "How to render the offset information in a memory layout hover.", @@ -1800,7 +1897,7 @@ } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.memoryLayout.padding": { "markdownDescription": "How to render the padding information in a memory layout hover.", @@ -1827,7 +1924,7 @@ } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.memoryLayout.size": { "markdownDescription": "How to render the size information in a memory layout hover.", @@ -1854,7 +1951,7 @@ } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.show.enumVariants": { "markdownDescription": "How many variants of an enum to display when hovering on. Show none if empty.", @@ -1868,10 +1965,10 @@ } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.show.fields": { - "markdownDescription": "How many fields of a struct, variant or union to display when hovering on. Show none if empty.", + "markdownDescription": "How many fields of a struct, variant or union to display when hovering on. Show none if\nempty.", "default": 5, "type": [ "null", @@ -1882,7 +1979,7 @@ } }, { - "title": "hover", + "title": "Hover", "properties": { "rust-analyzer.hover.show.traitAssocItems": { "markdownDescription": "How many associated items of a trait to display when hovering a trait.", @@ -1896,17 +1993,17 @@ } }, { - "title": "imports", + "title": "Imports", "properties": { "rust-analyzer.imports.granularity.enforce": { - "markdownDescription": "Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.", + "markdownDescription": "Enforce the import granularity setting for all files. If set to false rust-analyzer will\ntry to keep import styles consistent per file.", "default": false, "type": "boolean" } } }, { - "title": "imports", + "title": "Imports", "properties": { "rust-analyzer.imports.granularity.group": { "markdownDescription": "How imports should be grouped into use statements.", @@ -1930,27 +2027,27 @@ } }, { - "title": "imports", + "title": "Imports", "properties": { "rust-analyzer.imports.group.enable": { - "markdownDescription": "Group inserted imports by the [following order](https://rust-analyzer.github.io/book/features.html#auto-import). Groups are separated by newlines.", + "markdownDescription": "Group inserted imports by the [following\norder](https://rust-analyzer.github.io/book/features.html#auto-import). Groups are\nseparated by newlines.", "default": true, "type": "boolean" } } }, { - "title": "imports", + "title": "Imports", "properties": { "rust-analyzer.imports.merge.glob": { - "markdownDescription": "Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.", + "markdownDescription": "Allow import insertion to merge new imports into single path glob imports like `use\nstd::fmt::*;`.", "default": true, "type": "boolean" } } }, { - "title": "imports", + "title": "Imports", "properties": { "rust-analyzer.imports.preferNoStd": { "markdownDescription": "Prefer to unconditionally use imports of the core and alloc crate, over the std crate.", @@ -1960,17 +2057,17 @@ } }, { - "title": "imports", + "title": "Imports", "properties": { "rust-analyzer.imports.preferPrelude": { - "markdownDescription": "Whether to prefer import paths containing a `prelude` module.", + "markdownDescription": "Prefer import paths containing a `prelude` module.", "default": false, "type": "boolean" } } }, { - "title": "imports", + "title": "Imports", "properties": { "rust-analyzer.imports.prefix": { "markdownDescription": "The path structure for newly inserted paths to use.", @@ -1990,47 +2087,47 @@ } }, { - "title": "imports", + "title": "Imports", "properties": { "rust-analyzer.imports.prefixExternPrelude": { - "markdownDescription": "Whether to prefix external (including std, core) crate imports with `::`. e.g. \"use ::std::io::Read;\".", + "markdownDescription": "Prefix external (including std, core) crate imports with `::`.\n\nE.g. `use ::std::io::Read;`.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.bindingModeHints.enable": { - "markdownDescription": "Whether to show inlay type hints for binding modes.", + "markdownDescription": "Show inlay type hints for binding modes.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.chainingHints.enable": { - "markdownDescription": "Whether to show inlay type hints for method chains.", + "markdownDescription": "Show inlay type hints for method chains.", "default": true, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.closingBraceHints.enable": { - "markdownDescription": "Whether to show inlay hints after a closing `}` to indicate what item it belongs to.", + "markdownDescription": "Show inlay hints after a closing `}` to indicate what item it belongs to.", "default": true, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.closingBraceHints.minLines": { "markdownDescription": "Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1\nto always show them).", @@ -2041,20 +2138,20 @@ } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.closureCaptureHints.enable": { - "markdownDescription": "Whether to show inlay hints for closure captures.", + "markdownDescription": "Show inlay hints for closure captures.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.closureReturnTypeHints.enable": { - "markdownDescription": "Whether to show inlay type hints for return types of closures.", + "markdownDescription": "Show inlay type hints for return types of closures.", "default": "never", "type": "string", "enum": [ @@ -2071,7 +2168,7 @@ } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.closureStyle": { "markdownDescription": "Closure notation in type and chaining inlay hints.", @@ -2093,10 +2190,10 @@ } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.discriminantHints.enable": { - "markdownDescription": "Whether to show enum variant discriminant hints.", + "markdownDescription": "Show enum variant discriminant hints.", "default": "never", "type": "string", "enum": [ @@ -2113,10 +2210,10 @@ } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.expressionAdjustmentHints.enable": { - "markdownDescription": "Whether to show inlay hints for type adjustments.", + "markdownDescription": "Show inlay hints for type adjustments.", "default": "never", "type": "string", "enum": [ @@ -2133,20 +2230,20 @@ } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe": { - "markdownDescription": "Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.", + "markdownDescription": "Hide inlay hints for type adjustments outside of `unsafe` blocks.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.expressionAdjustmentHints.mode": { - "markdownDescription": "Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).", + "markdownDescription": "Show inlay hints as postfix ops (`.*` instead of `*`, etc).", "default": "prefix", "type": "string", "enum": [ @@ -2165,60 +2262,60 @@ } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.genericParameterHints.const.enable": { - "markdownDescription": "Whether to show const generic parameter name inlay hints.", + "markdownDescription": "Show const generic parameter name inlay hints.", "default": true, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.genericParameterHints.lifetime.enable": { - "markdownDescription": "Whether to show generic lifetime parameter name inlay hints.", + "markdownDescription": "Show generic lifetime parameter name inlay hints.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.genericParameterHints.type.enable": { - "markdownDescription": "Whether to show generic type parameter name inlay hints.", + "markdownDescription": "Show generic type parameter name inlay hints.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.implicitDrops.enable": { - "markdownDescription": "Whether to show implicit drop hints.", + "markdownDescription": "Show implicit drop hints.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.implicitSizedBoundHints.enable": { - "markdownDescription": "Whether to show inlay hints for the implied type parameter `Sized` bound.", + "markdownDescription": "Show inlay hints for the implied type parameter `Sized` bound.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.lifetimeElisionHints.enable": { - "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.", + "markdownDescription": "Show inlay type hints for elided lifetimes in function signatures.", "default": "never", "type": "string", "enum": [ @@ -2235,17 +2332,17 @@ } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames": { - "markdownDescription": "Whether to prefer using parameter names as the name for elided lifetime hints if possible.", + "markdownDescription": "Prefer using parameter names as the name for elided lifetime hints if possible.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.maxLength": { "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.", @@ -2259,30 +2356,30 @@ } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.parameterHints.enable": { - "markdownDescription": "Whether to show function parameter name inlay hints at the call\nsite.", + "markdownDescription": "Show function parameter name inlay hints at the call site.", "default": true, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.rangeExclusiveHints.enable": { - "markdownDescription": "Whether to show exclusive range inlay hints.", + "markdownDescription": "Show exclusive range inlay hints.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.reborrowHints.enable": { - "markdownDescription": "Whether to show inlay hints for compiler inserted reborrows.\nThis setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.", + "markdownDescription": "Show inlay hints for compiler inserted reborrows.\n\nThis setting is deprecated in favor of\n#rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.", "default": "never", "type": "string", "enum": [ @@ -2299,7 +2396,7 @@ } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.renderColons": { "markdownDescription": "Whether to render leading colons for type hints, and trailing colons for parameter hints.", @@ -2309,57 +2406,57 @@ } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.typeHints.enable": { - "markdownDescription": "Whether to show inlay type hints for variables.", + "markdownDescription": "Show inlay type hints for variables.", "default": true, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.typeHints.hideClosureInitialization": { - "markdownDescription": "Whether to hide inlay type hints for `let` statements that initialize to a closure.\nOnly applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.", + "markdownDescription": "Hide inlay type hints for `let` statements that initialize to a closure.\n\nOnly applies to closures with blocks, same as\n`#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.typeHints.hideClosureParameter": { - "markdownDescription": "Whether to hide inlay parameter type hints for closures.", + "markdownDescription": "Hide inlay parameter type hints for closures.", "default": false, "type": "boolean" } } }, { - "title": "inlayHints", + "title": "Inlay Hints", "properties": { "rust-analyzer.inlayHints.typeHints.hideNamedConstructor": { - "markdownDescription": "Whether to hide inlay type hints for constructors.", + "markdownDescription": "Hide inlay type hints for constructors.", "default": false, "type": "boolean" } } }, { - "title": "interpret", + "title": "Interpret", "properties": { "rust-analyzer.interpret.tests": { - "markdownDescription": "Enables the experimental support for interpreting tests.", + "markdownDescription": "Enable the experimental support for interpreting tests.", "default": false, "type": "boolean" } } }, { - "title": "joinLines", + "title": "Join Lines", "properties": { "rust-analyzer.joinLines.joinAssignments": { "markdownDescription": "Join lines merges consecutive declaration and initialization of an assignment.", @@ -2369,7 +2466,7 @@ } }, { - "title": "joinLines", + "title": "Join Lines", "properties": { "rust-analyzer.joinLines.joinElseIf": { "markdownDescription": "Join lines inserts else between consecutive ifs.", @@ -2379,7 +2476,7 @@ } }, { - "title": "joinLines", + "title": "Join Lines", "properties": { "rust-analyzer.joinLines.removeTrailingComma": { "markdownDescription": "Join lines removes trailing commas.", @@ -2389,7 +2486,7 @@ } }, { - "title": "joinLines", + "title": "Join Lines", "properties": { "rust-analyzer.joinLines.unwrapTrivialBlock": { "markdownDescription": "Join lines unwraps trivial blocks.", @@ -2399,37 +2496,37 @@ } }, { - "title": "lens", + "title": "Lens", "properties": { "rust-analyzer.lens.debug.enable": { - "markdownDescription": "Whether to show `Debug` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", + "markdownDescription": "Show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", "default": true, "type": "boolean" } } }, { - "title": "lens", + "title": "Lens", "properties": { "rust-analyzer.lens.enable": { - "markdownDescription": "Whether to show CodeLens in Rust files.", + "markdownDescription": "Show CodeLens in Rust files.", "default": true, "type": "boolean" } } }, { - "title": "lens", + "title": "Lens", "properties": { "rust-analyzer.lens.implementations.enable": { - "markdownDescription": "Whether to show `Implementations` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", + "markdownDescription": "Show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", "default": true, "type": "boolean" } } }, { - "title": "lens", + "title": "Lens", "properties": { "rust-analyzer.lens.location": { "markdownDescription": "Where to render annotations.", @@ -2447,70 +2544,70 @@ } }, { - "title": "lens", + "title": "Lens", "properties": { "rust-analyzer.lens.references.adt.enable": { - "markdownDescription": "Whether to show `References` lens for Struct, Enum, and Union.\nOnly applies when `#rust-analyzer.lens.enable#` is set.", + "markdownDescription": "Show `References` lens for Struct, Enum, and Union. Only applies when\n`#rust-analyzer.lens.enable#` is set.", "default": false, "type": "boolean" } } }, { - "title": "lens", + "title": "Lens", "properties": { "rust-analyzer.lens.references.enumVariant.enable": { - "markdownDescription": "Whether to show `References` lens for Enum Variants.\nOnly applies when `#rust-analyzer.lens.enable#` is set.", + "markdownDescription": "Show `References` lens for Enum Variants. Only applies when\n`#rust-analyzer.lens.enable#` is set.", "default": false, "type": "boolean" } } }, { - "title": "lens", + "title": "Lens", "properties": { "rust-analyzer.lens.references.method.enable": { - "markdownDescription": "Whether to show `Method References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", + "markdownDescription": "Show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", "default": false, "type": "boolean" } } }, { - "title": "lens", + "title": "Lens", "properties": { "rust-analyzer.lens.references.trait.enable": { - "markdownDescription": "Whether to show `References` lens for Trait.\nOnly applies when `#rust-analyzer.lens.enable#` is set.", + "markdownDescription": "Show `References` lens for Trait. Only applies when `#rust-analyzer.lens.enable#` is\nset.", "default": false, "type": "boolean" } } }, { - "title": "lens", + "title": "Lens", "properties": { "rust-analyzer.lens.run.enable": { - "markdownDescription": "Whether to show `Run` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", + "markdownDescription": "Show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", "default": true, "type": "boolean" } } }, { - "title": "lens", + "title": "Lens", "properties": { "rust-analyzer.lens.updateTest.enable": { - "markdownDescription": "Whether to show `Update Test` lens. Only applies when\n`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set.", + "markdownDescription": "Show `Update Test` lens. Only applies when `#rust-analyzer.lens.enable#` and\n`#rust-analyzer.lens.run.enable#` are set.", "default": true, "type": "boolean" } } }, { - "title": "general", + "title": "rust-analyzer", "properties": { "rust-analyzer.linkedProjects": { - "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON\nobjects in `rust-project.json` format.", + "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects.\n\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, `.rs` files (which\nwill be treated as standalone files) or JSON objects in `rust-project.json` format.", "default": [], "type": "array", "items": { @@ -2523,7 +2620,7 @@ } }, { - "title": "lru", + "title": "Lru", "properties": { "rust-analyzer.lru.capacity": { "markdownDescription": "Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.", @@ -2538,30 +2635,30 @@ } }, { - "title": "lru", + "title": "Lru", "properties": { "rust-analyzer.lru.query.capacities": { - "markdownDescription": "Sets the LRU capacity of the specified queries.", + "markdownDescription": "The LRU capacity of the specified queries.", "default": {}, "type": "object" } } }, { - "title": "notifications", + "title": "Notifications", "properties": { "rust-analyzer.notifications.cargoTomlNotFound": { - "markdownDescription": "Whether to show `can't find Cargo.toml` error message.", + "markdownDescription": "Show `can't find Cargo.toml` error message.", "default": true, "type": "boolean" } } }, { - "title": "general", + "title": "rust-analyzer", "properties": { "rust-analyzer.numThreads": { - "markdownDescription": "How many worker threads in the main loop. The default `null` means to pick automatically.", + "markdownDescription": "The number of worker threads in the main loop. The default `null` means to pick\nautomatically.", "default": null, "anyOf": [ { @@ -2588,7 +2685,7 @@ } }, { - "title": "procMacro", + "title": "Proc Macro", "properties": { "rust-analyzer.procMacro.attributes.enable": { "markdownDescription": "Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.", @@ -2598,7 +2695,7 @@ } }, { - "title": "procMacro", + "title": "Proc Macro", "properties": { "rust-analyzer.procMacro.enable": { "markdownDescription": "Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.", @@ -2608,7 +2705,7 @@ } }, { - "title": "procMacro", + "title": "Proc Macro", "properties": { "rust-analyzer.procMacro.ignored": { "markdownDescription": "These proc-macros will be ignored when trying to expand them.\n\nThis config takes a map of crate names with the exported proc-macro names to ignore as values.", @@ -2618,7 +2715,7 @@ } }, { - "title": "procMacro", + "title": "Proc Macro", "properties": { "rust-analyzer.procMacro.server": { "markdownDescription": "Internal config, path to proc-macro server executable.", @@ -2631,7 +2728,7 @@ } }, { - "title": "references", + "title": "References", "properties": { "rust-analyzer.references.excludeImports": { "markdownDescription": "Exclude imports from find-all-references.", @@ -2641,7 +2738,7 @@ } }, { - "title": "references", + "title": "References", "properties": { "rust-analyzer.references.excludeTests": { "markdownDescription": "Exclude tests from find-all-references and call-hierarchy.", @@ -2651,7 +2748,7 @@ } }, { - "title": "runnables", + "title": "Runnables", "properties": { "rust-analyzer.runnables.command": { "markdownDescription": "Command to be executed instead of 'cargo' for runnables.", @@ -2664,7 +2761,7 @@ } }, { - "title": "runnables", + "title": "Runnables", "properties": { "rust-analyzer.runnables.extraArgs": { "markdownDescription": "Additional arguments to be passed to cargo for runnables such as\ntests or binaries. For example, it may be `--release`.", @@ -2677,7 +2774,7 @@ } }, { - "title": "runnables", + "title": "Runnables", "properties": { "rust-analyzer.runnables.extraTestBinaryArgs": { "markdownDescription": "Additional arguments to be passed through Cargo to launched tests, benchmarks, or\ndoc-tests.\n\nUnless the launched target uses a\n[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),\nthey will end up being interpreted as options to\n[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).", @@ -2692,7 +2789,7 @@ } }, { - "title": "rustc", + "title": "Rustc", "properties": { "rust-analyzer.rustc.source": { "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it if the `rustc-dev` component\nis installed.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option does not take effect until rust-analyzer is restarted.", @@ -2705,7 +2802,7 @@ } }, { - "title": "rustfmt", + "title": "Rustfmt", "properties": { "rust-analyzer.rustfmt.extraArgs": { "markdownDescription": "Additional arguments to `rustfmt`.", @@ -2718,10 +2815,10 @@ } }, { - "title": "rustfmt", + "title": "Rustfmt", "properties": { "rust-analyzer.rustfmt.overrideCommand": { - "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.", + "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.", "default": null, "type": [ "null", @@ -2734,7 +2831,7 @@ } }, { - "title": "rustfmt", + "title": "Rustfmt", "properties": { "rust-analyzer.rustfmt.rangeFormatting.enable": { "markdownDescription": "Enables the use of rustfmt's unstable range formatting command for the\n`textDocument/rangeFormatting` request. The rustfmt option is unstable and only\navailable on a nightly build.", @@ -2744,7 +2841,7 @@ } }, { - "title": "semanticHighlighting", + "title": "Semantic Highlighting", "properties": { "rust-analyzer.semanticHighlighting.doc.comment.inject.enable": { "markdownDescription": "Inject additional highlighting into doc comments.\n\nWhen enabled, rust-analyzer will highlight rust source in doc comments as well as intra\ndoc links.", @@ -2754,17 +2851,17 @@ } }, { - "title": "semanticHighlighting", + "title": "Semantic Highlighting", "properties": { "rust-analyzer.semanticHighlighting.nonStandardTokens": { - "markdownDescription": "Whether the server is allowed to emit non-standard tokens and modifiers.", + "markdownDescription": "Emit non-standard tokens and modifiers\n\nWhen enabled, rust-analyzer will emit tokens and modifiers that are not part of the\nstandard set of semantic tokens.", "default": true, "type": "boolean" } } }, { - "title": "semanticHighlighting", + "title": "Semantic Highlighting", "properties": { "rust-analyzer.semanticHighlighting.operator.enable": { "markdownDescription": "Use semantic tokens for operators.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for operator tokens when\nthey are tagged with modifiers.", @@ -2774,7 +2871,7 @@ } }, { - "title": "semanticHighlighting", + "title": "Semantic Highlighting", "properties": { "rust-analyzer.semanticHighlighting.operator.specialization.enable": { "markdownDescription": "Use specialized semantic tokens for operators.\n\nWhen enabled, rust-analyzer will emit special token types for operator tokens instead\nof the generic `operator` token type.", @@ -2784,7 +2881,7 @@ } }, { - "title": "semanticHighlighting", + "title": "Semantic Highlighting", "properties": { "rust-analyzer.semanticHighlighting.punctuation.enable": { "markdownDescription": "Use semantic tokens for punctuation.\n\nWhen disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when\nthey are tagged with modifiers or have a special role.", @@ -2794,7 +2891,7 @@ } }, { - "title": "semanticHighlighting", + "title": "Semantic Highlighting", "properties": { "rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang": { "markdownDescription": "When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro\ncalls.", @@ -2804,7 +2901,7 @@ } }, { - "title": "semanticHighlighting", + "title": "Semantic Highlighting", "properties": { "rust-analyzer.semanticHighlighting.punctuation.specialization.enable": { "markdownDescription": "Use specialized semantic tokens for punctuation.\n\nWhen enabled, rust-analyzer will emit special token types for punctuation tokens instead\nof the generic `punctuation` token type.", @@ -2814,7 +2911,7 @@ } }, { - "title": "semanticHighlighting", + "title": "Semantic Highlighting", "properties": { "rust-analyzer.semanticHighlighting.strings.enable": { "markdownDescription": "Use semantic tokens for strings.\n\nIn some editors (e.g. vscode) semantic tokens override other highlighting grammars.\nBy disabling semantic tokens for strings, other grammars can be used to highlight\ntheir contents.", @@ -2824,7 +2921,7 @@ } }, { - "title": "signatureInfo", + "title": "Signature Info", "properties": { "rust-analyzer.signatureInfo.detail": { "markdownDescription": "Show full signature of the callable. Only shows parameters if disabled.", @@ -2842,7 +2939,7 @@ } }, { - "title": "signatureInfo", + "title": "Signature Info", "properties": { "rust-analyzer.signatureInfo.documentation.enable": { "markdownDescription": "Show documentation.", @@ -2852,10 +2949,10 @@ } }, { - "title": "typing", + "title": "Typing", "properties": { "rust-analyzer.typing.triggerChars": { - "markdownDescription": "Specify the characters allowed to invoke special on typing triggers.\n- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression\n- typing `=` between two expressions adds `;` when in statement position\n- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position\n- typing `.` in a chain method call auto-indents\n- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression\n- typing `{` in a use item adds a closing `}` in the right place\n- typing `>` to complete a return type `->` will insert a whitespace after it\n- typing `<` in a path or type position inserts a closing `>` after the path or type.", + "markdownDescription": "Specify the characters allowed to invoke special on typing triggers.\n\n- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing\n expression\n- typing `=` between two expressions adds `;` when in statement position\n- typing `=` to turn an assignment into an equality comparison removes `;` when in\n expression position\n- typing `.` in a chain method call auto-indents\n- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the\n expression\n- typing `{` in a use item adds a closing `}` in the right place\n- typing `>` to complete a return type `->` will insert a whitespace after it\n- typing `<` in a path or type position inserts a closing `>` after the path or type.", "default": "=.", "type": [ "null", @@ -2865,7 +2962,7 @@ } }, { - "title": "vfs", + "title": "Vfs", "properties": { "rust-analyzer.vfs.extraIncludes": { "markdownDescription": "Additional paths to include in the VFS. Generally for code that is\ngenerated or otherwise managed by a build system outside of Cargo,\nthough Cargo might be the eventual consumer.", @@ -2878,10 +2975,10 @@ } }, { - "title": "workspace", + "title": "Workspace", "properties": { "rust-analyzer.workspace.discoverConfig": { - "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.\n`progress_label` is used for the title in progress indicators, whereas `files_to_watch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\"\n ],\n \"progressLabel\": \"rust-analyzer\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## On `DiscoverWorkspaceConfig::command`\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object\ncorresponding to `DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option<String> },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on\n`DiscoverProjectData` to provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`,\nwhich will be substituted with the JSON-serialized form of the following\nenum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`,\nand therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to\nto update an existing workspace. As a reference for implementors,\nbuck2's `rust-project` will likely be useful:\nhttps://github.com/facebook/buck2/tree/main/integrations/rust-project.", + "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.\n`progress_label` is used for the title in progress indicators, whereas `files_to_watch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\"\n ],\n \"progressLabel\": \"rust-analyzer\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## On `DiscoverWorkspaceConfig::command`\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to\n`DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option<String> },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on `DiscoverProjectData`\nto provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be\nsubstituted with the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and\ntherefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an\nexisting workspace. As a reference for implementors, buck2's `rust-project` will likely\nbe useful: https://github.com/facebook/buck2/tree/main/integrations/rust-project.", "default": null, "anyOf": [ { @@ -2912,7 +3009,7 @@ } }, { - "title": "workspace", + "title": "Workspace", "properties": { "rust-analyzer.workspace.symbol.search.excludeImports": { "markdownDescription": "Exclude all imports from workspace symbol search.\n\nIn addition to regular imports (which are always excluded),\nthis option removes public imports (better known as re-exports)\nand removes imports that rename the imported symbol.", @@ -2922,7 +3019,7 @@ } }, { - "title": "workspace", + "title": "Workspace", "properties": { "rust-analyzer.workspace.symbol.search.kind": { "markdownDescription": "Workspace symbol search kind.", @@ -2940,7 +3037,7 @@ } }, { - "title": "workspace", + "title": "Workspace", "properties": { "rust-analyzer.workspace.symbol.search.limit": { "markdownDescription": "Limits the number of items returned from a workspace symbol search (Defaults to 128).\nSome clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.\nOther clients requires all results upfront and might require a higher limit.", @@ -2951,7 +3048,7 @@ } }, { - "title": "workspace", + "title": "Workspace", "properties": { "rust-analyzer.workspace.symbol.search.scope": { "markdownDescription": "Workspace symbol search scope.",
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index d2dc740..3b1b076 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts
@@ -8,10 +8,9 @@ export type RunnableEnvCfgItem = { mask?: string; - env: Record<string, string>; + env: { [key: string]: { toString(): string } | null }; platform?: string | string[]; }; -export type RunnableEnvCfg = Record<string, string> | RunnableEnvCfgItem[]; type ShowStatusBar = "always" | "never" | { documentSelector: vscode.DocumentSelector }; @@ -261,18 +260,13 @@ return this.get<boolean | undefined>("testExplorer"); } - runnablesExtraEnv(label: string): Record<string, string> | undefined { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const item = this.get<any>("runnables.extraEnv") ?? this.get<any>("runnableEnv"); - if (!item) return undefined; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const fixRecord = (r: Record<string, any>) => { - for (const key in r) { - if (typeof r[key] !== "string") { - r[key] = String(r[key]); - } - } - }; + runnablesExtraEnv(label: string): Env { + const serverEnv = this.serverExtraEnv; + let extraEnv = + this.get< + RunnableEnvCfgItem[] | { [key: string]: { toString(): string } | null } | null + >("runnables.extraEnv") ?? {}; + if (!extraEnv) return serverEnv; const platform = process.platform; const checkPlatform = (it: RunnableEnvCfgItem) => { @@ -283,19 +277,25 @@ return true; }; - if (item instanceof Array) { + if (extraEnv instanceof Array) { const env = {}; - for (const it of item) { + for (const it of extraEnv) { const masked = !it.mask || new RegExp(it.mask).test(label); if (masked && checkPlatform(it)) { Object.assign(env, it.env); } } - fixRecord(env); - return env; + extraEnv = env; } - fixRecord(item); - return item; + const runnableExtraEnv = substituteVariablesInEnv( + Object.fromEntries( + Object.entries(extraEnv).map(([k, v]) => [ + k, + typeof v === "string" ? v : v?.toString(), + ]), + ), + ); + return { ...runnableExtraEnv, ...serverEnv }; } get restartServerOnConfigChange() {
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index adb75c2..24f8d90 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts
@@ -6,7 +6,14 @@ import { Cargo } from "./toolchain"; import type { Ctx } from "./ctx"; import { createTaskFromRunnable, prepareEnv } from "./run"; -import { execute, isCargoRunnableArgs, unwrapUndefinable, log, normalizeDriveLetter } from "./util"; +import { + execute, + isCargoRunnableArgs, + unwrapUndefinable, + log, + normalizeDriveLetter, + Env, +} from "./util"; import type { Config } from "./config"; // Here we want to keep track on everything that's currently running @@ -206,10 +213,7 @@ destination: string; }; -async function discoverSourceFileMap( - env: Record<string, string>, - cwd: string, -): Promise<SourceFileMap | undefined> { +async function discoverSourceFileMap(env: Env, cwd: string): Promise<SourceFileMap | undefined> { const sysroot = env["RUSTC_TOOLCHAIN"]; if (sysroot) { // let's try to use the default toolchain @@ -232,7 +236,7 @@ type DebugConfigProvider<Type extends string, DebugConfig extends BaseDebugConfig<Type>> = { executableProperty: keyof DebugConfig; - environmentProperty: PropertyFetcher<DebugConfig, Record<string, string>, keyof DebugConfig>; + environmentProperty: PropertyFetcher<DebugConfig, Env, keyof DebugConfig>; runnableArgsProperty: PropertyFetcher<DebugConfig, ra.CargoRunnableArgs, keyof DebugConfig>; sourceFileMapProperty?: keyof DebugConfig; type: Type; @@ -276,7 +280,7 @@ "environment", Object.entries(env).map((entry) => ({ name: entry[0], - value: entry[1], + value: entry[1] ?? "", })), ], runnableArgsProperty: (runnableArgs: ra.CargoRunnableArgs) => [ @@ -304,10 +308,7 @@ }, }; -async function getDebugExecutable( - runnableArgs: ra.CargoRunnableArgs, - env: Record<string, string>, -): Promise<string> { +async function getDebugExecutable(runnableArgs: ra.CargoRunnableArgs, env: Env): Promise<string> { const cargo = new Cargo(runnableArgs.workspaceRoot || ".", env); const executable = await cargo.executableFromArgs(runnableArgs); @@ -328,7 +329,7 @@ runnable: ra.Runnable, runnableArgs: ra.CargoRunnableArgs, executable: string, - env: Record<string, string>, + env: Env, sourceFileMap?: Record<string, string>, ): vscode.DebugConfiguration { const { @@ -380,14 +381,14 @@ args: string[]; sourceMap: Record<string, string> | undefined; sourceLanguages: ["rust"]; - env: Record<string, string>; + env: Env; } & BaseDebugConfig<"lldb">; type NativeDebugConfig = { target: string; // See https://github.com/WebFreak001/code-debug/issues/359 arguments: string; - env: Record<string, string>; + env: Env; valuesFormatting: "prettyPrinters"; } & BaseDebugConfig<"gdb">;
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 95166c4..87c1d52 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts
@@ -7,7 +7,7 @@ import { makeDebugConfig } from "./debug"; import type { Config } from "./config"; import type { LanguageClient } from "vscode-languageclient/node"; -import { log, unwrapUndefinable, type RustEditor } from "./util"; +import { Env, log, unwrapUndefinable, type RustEditor } from "./util"; const quickPickButtons = [ { iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." }, @@ -122,11 +122,8 @@ } } -export function prepareBaseEnv( - inheritEnv: boolean, - base?: Record<string, string>, -): Record<string, string> { - const env: Record<string, string> = { RUST_BACKTRACE: "short" }; +export function prepareBaseEnv(inheritEnv: boolean, base?: Env): Env { + const env: Env = { RUST_BACKTRACE: "short" }; if (inheritEnv) { Object.assign(env, process.env); } @@ -136,11 +133,7 @@ return env; } -export function prepareEnv( - inheritEnv: boolean, - runnableEnv?: Record<string, string>, - runnableEnvCfg?: Record<string, string>, -): Record<string, string> { +export function prepareEnv(inheritEnv: boolean, runnableEnv?: Env, runnableEnvCfg?: Env): Env { const env = prepareBaseEnv(inheritEnv, runnableEnv); if (runnableEnvCfg) {
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 730ec6d..eb0748a 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts
@@ -1,6 +1,7 @@ import * as vscode from "vscode"; import type { Config } from "./config"; import * as toolchain from "./toolchain"; +import { Env } from "./util"; // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and // our configuration should be compatible with it so use the same key. @@ -117,8 +118,8 @@ export async function targetToExecution( definition: TaskDefinition, options?: { - env?: { [key: string]: string }; cwd?: string; + env?: Env; }, cargo?: string, ): Promise<vscode.ProcessExecution | vscode.ShellExecution> { @@ -131,7 +132,12 @@ command = definition.command; args = definition.args || []; } - return new vscode.ProcessExecution(command, args, options); + return new vscode.ProcessExecution(command, args, { + cwd: options?.cwd, + env: Object.fromEntries( + Object.entries(options?.env ?? {}).map(([key, value]) => [key, value ?? ""]), + ), + }); } export function activateTaskProvider(config: Config): vscode.Disposable {
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index a859ce6..06f75a8 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts
@@ -3,7 +3,7 @@ import * as path from "path"; import * as readline from "readline"; import * as vscode from "vscode"; -import { log, memoizeAsync, unwrapUndefinable } from "./util"; +import { Env, log, memoizeAsync, unwrapUndefinable } from "./util"; import type { CargoRunnableArgs } from "./lsp_ext"; interface CompilationArtifact { @@ -37,7 +37,7 @@ export class Cargo { constructor( readonly rootFolder: string, - readonly env: Record<string, string>, + readonly env: Env, ) {} // Made public for testing purposes @@ -156,7 +156,7 @@ /** Mirrors `toolchain::cargo()` implementation */ // FIXME: The server should provide this -export function cargoPath(env?: Record<string, string>): Promise<string> { +export function cargoPath(env?: Env): Promise<string> { if (env?.["RUSTC_TOOLCHAIN"]) { return Promise.resolve("cargo"); }
diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml index 1fc1da5..f56a0de 100644 --- a/lib/lsp-server/Cargo.toml +++ b/lib/lsp-server/Cargo.toml
@@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" @@ -16,9 +16,9 @@ [dev-dependencies] lsp-types = "=0.95" ctrlc = "3.4.7" -anyhow.workspace = true +anyhow.workspace = true rustc-hash.workspace = true -toolchain.workspace = true +toolchain.workspace = true [lints] workspace = true
diff --git a/lib/lsp-server/src/msg.rs b/lib/lsp-server/src/msg.rs index 399d674..305008e 100644 --- a/lib/lsp-server/src/msg.rs +++ b/lib/lsp-server/src/msg.rs
@@ -84,9 +84,9 @@ // request id. We fail deserialization in that case, so we just // make this field mandatory. pub id: RequestId, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub result: Option<serde_json::Value>, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub error: Option<ResponseError>, } @@ -94,7 +94,7 @@ pub struct ResponseError { pub code: i32, pub message: String, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub data: Option<serde_json::Value>, } @@ -175,7 +175,7 @@ let msg = match serde_json::from_str(&text) { Ok(msg) => msg, Err(e) => { - return Err(invalid_data!("malformed LSP payload: {:?}", e)); + return Err(invalid_data!("malformed LSP payload `{e:?}`: {text:?}")); } };
diff --git a/rust-version b/rust-version index c2b1c15..02b217f 100644 --- a/rust-version +++ b/rust-version
@@ -1 +1 @@ -e05ab47e6c418fb2b9faa2eae9a7e70c65c98eaa +21a19c297d4f5a03501d92ca251bd7a17073c08a
diff --git a/triagebot.toml b/triagebot.toml index 2201b5a..c986249 100644 --- a/triagebot.toml +++ b/triagebot.toml
@@ -17,6 +17,7 @@ "sync from downstream", "Sync from rust", "sync from rust", + "Rustc pull update", ] labels = ["has-merge-commits", "S-waiting-on-author"]
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 19ca62e..bc7eb88 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs
@@ -173,11 +173,11 @@ #[allow(clippy::print_stderr)] fn ensure_file_contents(cg: CodegenType, file: &Path, contents: &str, check: bool) -> bool { let contents = normalize_newlines(contents); - if let Ok(old_contents) = fs::read_to_string(file) { - if normalize_newlines(&old_contents) == contents { - // File is already up to date. - return false; - } + if let Ok(old_contents) = fs::read_to_string(file) + && normalize_newlines(&old_contents) == contents + { + // File is already up to date. + return false; } let display_path = file.strip_prefix(project_root()).unwrap_or(file);
diff --git a/xtask/src/codegen/grammar.rs b/xtask/src/codegen/grammar.rs index 824b38f..9bd87a7 100644 --- a/xtask/src/codegen/grammar.rs +++ b/xtask/src/codegen/grammar.rs
@@ -1081,7 +1081,6 @@ "Enum", "Variant", "Trait", - "TraitAlias", "Module", "Static", "Const",
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs index 6ff6a1b..c9eea87 100644 --- a/xtask/src/metrics.rs +++ b/xtask/src/metrics.rs
@@ -16,13 +16,16 @@ impl flags::Metrics { pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { let mut metrics = Metrics::new(sh)?; - if !Path::new("./target/rustc-perf").exists() { - sh.create_dir("./target/rustc-perf")?; - cmd!(sh, "git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf") - .run()?; + if !Path::new("./target/metrics/rustc-perf").exists() { + sh.create_dir("./target/metrics/rustc-perf")?; + cmd!( + sh, + "git clone https://github.com/rust-lang/rustc-perf.git ./target/metrics/rustc-perf" + ) + .run()?; } { - let _d = sh.push_dir("./target/rustc-perf"); + let _d = sh.push_dir("./target/metrics/rustc-perf"); let revision = &metrics.perf_revision; cmd!(sh, "git reset --hard {revision}").run()?; } @@ -88,11 +91,12 @@ cmd!( sh, - "git clone --depth=1 --branch 1.76.0 https://github.com/rust-lang/rust.git --single-branch" + "git clone --depth=1 --branch 1.76.0 https://github.com/rust-lang/rust.git --single-branch ./target/metrics/rust" ) .run()?; - let output = cmd!(sh, "./target/release/rust-analyzer rustc-tests ./rust").read()?; + let output = + cmd!(sh, "./target/release/rust-analyzer rustc-tests ./target/metrics/rust").read()?; for (metric, value, unit) in parse_metrics(&output) { self.report(metric, value, unit.into()); } @@ -106,7 +110,7 @@ self.measure_analysis_stats_path( sh, bench, - &format!("./target/rustc-perf/collector/compile-benchmarks/{bench}"), + &format!("./target/metrics/rustc-perf/collector/compile-benchmarks/{bench}"), ) } fn measure_analysis_stats_path(
diff --git a/xtask/src/publish/notes.rs b/xtask/src/publish/notes.rs index 93592d4..8d36fcb 100644 --- a/xtask/src/publish/notes.rs +++ b/xtask/src/publish/notes.rs
@@ -72,13 +72,13 @@ } fn process_document_title(&mut self) -> anyhow::Result<()> { - if let Some(Ok(line)) = self.iter.next() { - if let Some((level, title)) = get_title(&line) { - let title = process_inline_macros(title)?; - if level == 1 { - self.write_title(level, &title); - return Ok(()); - } + if let Some(Ok(line)) = self.iter.next() + && let Some((level, title)) = get_title(&line) + { + let title = process_inline_macros(title)?; + if level == 1 { + self.write_title(level, &title); + return Ok(()); } } bail!("document title not found") @@ -141,39 +141,39 @@ } fn process_source_code_block(&mut self, level: usize) -> anyhow::Result<()> { - if let Some(Ok(line)) = self.iter.next() { - if let Some(styles) = line.strip_prefix("[source").and_then(|s| s.strip_suffix(']')) { - let mut styles = styles.split(','); - if !styles.next().unwrap().is_empty() { - bail!("not a source code block"); - } - let language = styles.next(); - return self.process_listing_block(language, level); + if let Some(Ok(line)) = self.iter.next() + && let Some(styles) = line.strip_prefix("[source").and_then(|s| s.strip_suffix(']')) + { + let mut styles = styles.split(','); + if !styles.next().unwrap().is_empty() { + bail!("not a source code block"); } + let language = styles.next(); + return self.process_listing_block(language, level); } bail!("not a source code block") } fn process_listing_block(&mut self, style: Option<&str>, level: usize) -> anyhow::Result<()> { - if let Some(Ok(line)) = self.iter.next() { - if line == LISTING_DELIMITER { - self.write_indent(level); - self.output.push_str("```"); - if let Some(style) = style { - self.output.push_str(style); - } - self.output.push('\n'); - while let Some(line) = self.iter.next() { - let line = line?; - if line == LISTING_DELIMITER { - self.write_line("```", level); - return Ok(()); - } else { - self.write_line(&line, level); - } - } - bail!("listing block is not terminated") + if let Some(Ok(line)) = self.iter.next() + && line == LISTING_DELIMITER + { + self.write_indent(level); + self.output.push_str("```"); + if let Some(style) = style { + self.output.push_str(style); } + self.output.push('\n'); + while let Some(line) = self.iter.next() { + let line = line?; + if line == LISTING_DELIMITER { + self.write_line("```", level); + return Ok(()); + } else { + self.write_line(&line, level); + } + } + bail!("listing block is not terminated") } bail!("not a listing block") } @@ -200,49 +200,48 @@ } fn process_image_block(&mut self, caption: Option<&str>, level: usize) -> anyhow::Result<()> { - if let Some(Ok(line)) = self.iter.next() { - if let Some((url, attrs)) = parse_media_block(&line, IMAGE_BLOCK_PREFIX) { - let alt = if let Some(stripped) = - attrs.strip_prefix('"').and_then(|s| s.strip_suffix('"')) - { + if let Some(Ok(line)) = self.iter.next() + && let Some((url, attrs)) = parse_media_block(&line, IMAGE_BLOCK_PREFIX) + { + let alt = + if let Some(stripped) = attrs.strip_prefix('"').and_then(|s| s.strip_suffix('"')) { stripped } else { attrs }; - if let Some(caption) = caption { - self.write_caption_line(caption, level); - } - self.write_indent(level); - self.output.push_str("; - self.output.push_str(url); - self.output.push_str(")\n"); - return Ok(()); + if let Some(caption) = caption { + self.write_caption_line(caption, level); } + self.write_indent(level); + self.output.push_str("; + self.output.push_str(url); + self.output.push_str(")\n"); + return Ok(()); } bail!("not a image block") } fn process_video_block(&mut self, caption: Option<&str>, level: usize) -> anyhow::Result<()> { - if let Some(Ok(line)) = self.iter.next() { - if let Some((url, attrs)) = parse_media_block(&line, VIDEO_BLOCK_PREFIX) { - let html_attrs = match attrs { - "options=loop" => "controls loop", - r#"options="autoplay,loop""# => "autoplay controls loop", - _ => bail!("unsupported video syntax"), - }; - if let Some(caption) = caption { - self.write_caption_line(caption, level); - } - self.write_indent(level); - self.output.push_str(r#"<video src=""#); - self.output.push_str(url); - self.output.push_str(r#"" "#); - self.output.push_str(html_attrs); - self.output.push_str(">Your browser does not support the video tag.</video>\n"); - return Ok(()); + if let Some(Ok(line)) = self.iter.next() + && let Some((url, attrs)) = parse_media_block(&line, VIDEO_BLOCK_PREFIX) + { + let html_attrs = match attrs { + "options=loop" => "controls loop", + r#"options="autoplay,loop""# => "autoplay controls loop", + _ => bail!("unsupported video syntax"), + }; + if let Some(caption) = caption { + self.write_caption_line(caption, level); } + self.write_indent(level); + self.output.push_str(r#"<video src=""#); + self.output.push_str(url); + self.output.push_str(r#"" "#); + self.output.push_str(html_attrs); + self.output.push_str(">Your browser does not support the video tag.</video>\n"); + return Ok(()); } bail!("not a video block") } @@ -371,12 +370,11 @@ } fn parse_media_block<'a>(line: &'a str, prefix: &str) -> Option<(&'a str, &'a str)> { - if let Some(line) = line.strip_prefix(prefix) { - if let Some((url, rest)) = line.split_once('[') { - if let Some(attrs) = rest.strip_suffix(']') { - return Some((url, attrs)); - } - } + if let Some(line) = line.strip_prefix(prefix) + && let Some((url, rest)) = line.split_once('[') + && let Some(attrs) = rest.strip_suffix(']') + { + return Some((url, attrs)); } None }