Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #3623 - RalfJung:rustup, r=RalfJung
Rustup
204 files changed, 5501 insertions, 2897 deletions
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index a302e23781..d5951a9420 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -6,3 +6,10 @@ # prettier format f247090558c9ba3c551566eae5882b7ca865225f + +# subtree syncs +932d85b52946d917deab2c23ead552f7f713b828 +3e358a6827d83e8d6473913a5e304734aadfed04 +9d2cb42a413e51deb50b36794a2e1605381878fc +f532576ac53ddcc666bc8d59e0b6437065e2f599 +c48062fe2ab9a2d913d1985a6b0aec4bf936bfc1 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a10345a706..87a1729d2b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -63,15 +63,15 @@ jobs: - name: Install Rust toolchain run: | rustup update --no-self-update ${{ env.RUST_CHANNEL }} - rustup component add --toolchain ${{ env.RUST_CHANNEL }} rustfmt rust-src rustup default ${{ env.RUST_CHANNEL }} + rustup component add --toolchain ${{ env.RUST_CHANNEL }} rustfmt rust-src # https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/rust.json - name: Install Rust Problem Matcher if: matrix.os == 'ubuntu-latest' run: echo "::add-matcher::.github/rust.json" - name: Cache Dependencies - uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012 + uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609 with: key: ${{ env.RUST_CHANNEL }} @@ -140,7 +140,7 @@ jobs: rustup target add ${{ env.targets }} ${{ env.targets_ide }} - name: Cache Dependencies - uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012 + uses: Swatinem/rust-cache@9bdad043e88c75890e36ad3bbc8d27f0090dd609 - name: Check run: | diff --git a/.github/workflows/metrics.yaml b/.github/workflows/metrics.yaml index b6cd4a795a..a4146d6021 100644 --- a/.github/workflows/metrics.yaml +++ b/.github/workflows/metrics.yaml @@ -11,34 +11,21 @@ env: RUSTUP_MAX_RETRIES: 10 jobs: - setup_cargo: + build_metrics: if: github.repository == 'rust-lang/rust-analyzer' runs-on: ubuntu-latest + steps: - name: Install Rust toolchain run: | rustup update --no-self-update stable - rustup component add rustfmt rust-src rustup default stable - - name: Cache cargo - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: ${{ runner.os }}-cargo-${{ github.sha }} + rustup component add --toolchain stable rust-src - build_metrics: - runs-on: ubuntu-latest - needs: setup_cargo - - steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Restore cargo cache + - name: Cache cargo uses: actions/cache@v4 with: path: | @@ -69,22 +56,18 @@ jobs: matrix: names: [self, ripgrep-13.0.0, webrender-2022, diesel-1.4.8, hyper-0.14.18] runs-on: ubuntu-latest - needs: [setup_cargo, build_metrics] + needs: build_metrics steps: + - name: Install Rust toolchain + run: | + rustup update --no-self-update stable + rustup default stable + rustup component add --toolchain stable rust-src + - name: Checkout repository uses: actions/checkout@v4 - - name: Restore cargo cache - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - key: ${{ runner.os }}-cargo-${{ github.sha }} - - name: Restore target cache uses: actions/cache@v4 with: diff --git a/.github/workflows/rustdoc.yaml b/.github/workflows/rustdoc.yaml index 12a1a791fd..f975bbaa51 100644 --- a/.github/workflows/rustdoc.yaml +++ b/.github/workflows/rustdoc.yaml @@ -26,7 +26,7 @@ jobs: run: cargo doc --all --no-deps - name: Deploy Docs - uses: peaceiris/actions-gh-pages@364c31d33bb99327c77b3a5438a83a357a6729ad # v3.4.0 + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_branch: gh-pages diff --git a/Cargo.lock b/Cargo.lock index a6e460134f..8eb872514a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "arbitrary" @@ -46,15 +46,15 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -91,9 +91,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "byteorder" @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.90" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" [[package]] name = "cfg" @@ -160,6 +160,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] name = "chalk-derive" version = "0.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -177,7 +183,7 @@ version = "0.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db18493569b190f7266a04901e520fc3a5c00564475154287906f8a27302c119" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "chalk-derive", ] @@ -282,11 +288,11 @@ checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "ctrlc" -version = "3.4.2" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ - "nix 0.27.1", + "nix 0.28.0", "windows-sys 0.52.0", ] @@ -324,10 +330,31 @@ dependencies = [ ] [[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] name = "dissimilar" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" +checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" [[package]] name = "dot" @@ -343,15 +370,15 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "ena" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] @@ -364,9 +391,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "expect-test" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" +checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0" dependencies = [ "dissimilar", "once_cell", @@ -380,7 +407,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "windows-sys 0.52.0", ] @@ -392,9 +419,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -442,9 +469,9 @@ checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -459,9 +486,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" @@ -504,7 +531,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg", "cov-mark", "dashmap", @@ -568,7 +595,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.4.2", + "bitflags 2.5.0", "chalk-derive", "chalk-ir", "chalk-recursive", @@ -589,7 +616,7 @@ dependencies = [ "oorandom", "project-model", "ra-ap-rustc_abi", - "ra-ap-rustc_index", + "ra-ap-rustc_index 0.53.0", "ra-ap-rustc_pattern_analysis", "rustc-hash", "scoped-tls", @@ -695,7 +722,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags 2.4.2", + "bitflags 2.5.0", "cov-mark", "crossbeam-channel", "either", @@ -776,9 +803,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -826,9 +853,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jod-thread" @@ -874,9 +901,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libloading" @@ -885,20 +912,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] name = "libmimalloc-sys" -version = "0.1.35" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3979b5c37ece694f1f5e51e7ecc871fdb0f517ed04ee45f88d15d6d553cb9664" +checksum = "81eb4061c0582dedea1cbc7aff2240300dd6982e0239d1c99e65c1dbf4a30ba7" dependencies = [ "cc", "libc", ] [[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] name = "limit" version = "0.0.0" @@ -948,9 +985,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1001,9 +1038,9 @@ dependencies = [ [[package]] name = "lz4_flex" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15" +checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" [[package]] name = "mbe" @@ -1023,9 +1060,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -1038,18 +1075,18 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "mimalloc" -version = "0.1.39" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa01922b5ea280a911e323e4d2fd24b7fe5cc4042e0d2cda3c40775cdc4bdc9c" +checksum = "9f41a2280ded0da56c8cf898babb86e8f10651a34adcfff190ae9a1159c6908d" dependencies = [ "libmimalloc-sys", ] @@ -1097,12 +1134,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg-if", + "cfg_aliases", "libc", ] @@ -1118,7 +1156,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -1187,10 +1225,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -1198,15 +1242,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -1224,9 +1268,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "paths" @@ -1262,9 +1306,9 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap", @@ -1272,9 +1316,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "powerfmt" @@ -1346,9 +1390,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -1365,7 +1409,7 @@ dependencies = [ "perf-event", "tikv-jemalloc-ctl", "tracing", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -1417,7 +1461,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "memchr", "unicase", ] @@ -1433,21 +1477,21 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "ra-ap-rustc_abi" -version = "0.44.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8709df2a746f055316bc0c62bd30948695a25e734863bf6e1f9755403e010ab" +checksum = "80b1d613eee933486c0613a7bc26e515e46f43adf479d1edd5e537f983e9ce46" dependencies = [ - "bitflags 2.4.2", - "ra-ap-rustc_index", + "bitflags 2.5.0", + "ra-ap-rustc_index 0.53.0", "tracing", ] @@ -1458,7 +1502,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ad68bacffb87dcdbb23a3ce11261375078aaa06b85d348c49f39ffd5510dc20" dependencies = [ "arrayvec", - "ra-ap-rustc_index_macros", + "ra-ap-rustc_index_macros 0.44.0", + "smallvec", +] + +[[package]] +name = "ra-ap-rustc_index" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072060ac77e9e1a02cc20028095993af7e72cc0804779c68bcbf47b16de49c9" +dependencies = [ + "arrayvec", + "ra-ap-rustc_index_macros 0.53.0", "smallvec", ] @@ -1475,10 +1530,22 @@ dependencies = [ ] [[package]] +name = "ra-ap-rustc_index_macros" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f3d6dcb30a66905388e14756b8f2216131d9f8004922c07f13335840e058d1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] name = "ra-ap-rustc_lexer" -version = "0.44.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aab683fc8579d09eb72033bd5dc9ba6d701aa9645b5fed087ef19af71184dff3" +checksum = "dbd8a2b0bdcba9892cbce0b25f6c953d31b0febc1f3420fc692884fce5a23ad8" dependencies = [ "unicode-properties", "unicode-xid", @@ -1486,11 +1553,11 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.44.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bcf9ff5edbf784b67b8ad5e03a068f1300fcc24062c0d476b3018965135d933" +checksum = "70dad7a491c2554590222e0c9212dcb7c2e7aceb668875075012a35ea780d135" dependencies = [ - "ra-ap-rustc_index", + "ra-ap-rustc_index 0.53.0", "ra-ap-rustc_lexer", ] @@ -1500,7 +1567,7 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d63d1e1d5b2a13273cee1a10011147418f40e12b70f70578ce1dee0f1cafc334" dependencies = [ - "ra-ap-rustc_index", + "ra-ap-rustc_index 0.44.0", "rustc-hash", "rustc_apfloat", "smallvec", @@ -1539,9 +1606,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -1567,6 +1634,26 @@ dependencies = [ ] [[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] name = "rowan" version = "0.15.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1634,16 +1721,16 @@ dependencies = [ "vfs", "vfs-notify", "walkdir", - "winapi", + "windows-sys 0.52.0", "xflags", "xshell", ] [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -1663,9 +1750,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "salsa" @@ -1729,27 +1816,27 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.197" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", @@ -1758,9 +1845,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "indexmap", "itoa", @@ -1770,9 +1857,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", @@ -1799,9 +1886,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smol_str" @@ -1856,14 +1943,14 @@ dependencies = [ "jod-thread", "libc", "miow", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "syn" -version = "2.0.52" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -1946,18 +2033,18 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", @@ -2007,9 +2094,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "num-conv", @@ -2041,9 +2128,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", @@ -2062,9 +2149,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap", "serde", @@ -2255,6 +2342,7 @@ dependencies = [ "paths", "rustc-hash", "stdx", + "tracing", ] [[package]] @@ -2304,11 +2392,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2332,7 +2420,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -2352,17 +2440,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -2373,9 +2462,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -2385,9 +2474,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -2397,9 +2486,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -2409,9 +2504,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -2421,9 +2516,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -2433,9 +2528,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -2445,15 +2540,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.5.32" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -2481,24 +2576,25 @@ checksum = "672423d4fea7ffa2f6c25ba60031ea13dc6258070556f125cc4d790007d4a155" [[package]] name = "xshell" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2107fe03e558353b4c71ad7626d58ed82efaf56c54134228608893c77023ad" +checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437" dependencies = [ "xshell-macros", ] [[package]] name = "xshell-macros" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2c411759b501fb9501aac2b1b2d287a6e93e5bdcf13c25306b23e1b716dd0e" +checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852" [[package]] name = "xtask" version = "0.1.0" dependencies = [ "anyhow", + "directories", "flate2", "itertools", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index f7e3ae51df..3108c1b3df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.76" +rust-version = "1.78" edition = "2021" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] @@ -85,10 +85,10 @@ tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.44.0", default-features = false } -ra-ap-rustc_parse_format = { version = "0.44.0", default-features = false } -ra-ap-rustc_index = { version = "0.44.0", default-features = false } -ra-ap-rustc_abi = { version = "0.44.0", default-features = false } +ra-ap-rustc_lexer = { version = "0.53.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.53.0", default-features = false } +ra-ap-rustc_index = { version = "0.53.0", default-features = false } +ra-ap-rustc_abi = { version = "0.53.0", default-features = false } ra-ap-rustc_pattern_analysis = { version = "0.44.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/crates/base-db/src/change.rs b/crates/base-db/src/change.rs index f202a885e2..927b2108a6 100644 --- a/crates/base-db/src/change.rs +++ b/crates/base-db/src/change.rs @@ -51,7 +51,7 @@ impl FileChange { } pub fn apply(self, db: &mut dyn SourceDatabaseExt) { - let _p = tracing::span!(tracing::Level::INFO, "RootDatabase::apply_change").entered(); + let _p = tracing::span!(tracing::Level::INFO, "FileChange::apply").entered(); if let Some(roots) = self.roots { for (idx, root) in roots.into_iter().enumerate() { let root_id = SourceRootId(idx as u32); diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 240af7925c..b2c3f38ab4 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -324,21 +324,27 @@ pub struct Dependency { pub crate_id: CrateId, pub name: CrateName, prelude: bool, + sysroot: bool, } impl Dependency { pub fn new(name: CrateName, crate_id: CrateId) -> Self { - Self { name, crate_id, prelude: true } + Self { name, crate_id, prelude: true, sysroot: false } } - pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool) -> Self { - Self { name, crate_id, prelude } + pub fn with_prelude(name: CrateName, crate_id: CrateId, prelude: bool, sysroot: bool) -> Self { + Self { name, crate_id, prelude, sysroot } } /// Whether this dependency is to be added to the depending crate's extern prelude. pub fn is_prelude(&self) -> bool { self.prelude } + + /// Whether this dependency is a sysroot injected one. + pub fn is_sysroot(&self) -> bool { + self.sysroot + } } impl CrateGraph { diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 2b64a07a5a..2c13eed56c 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -8,7 +8,7 @@ mod input; use std::panic; use salsa::Durability; -use syntax::{ast, Parse, SourceFile}; +use syntax::{ast, Parse, SourceFile, SyntaxError}; use triomphe::Arc; pub use crate::{ @@ -51,6 +51,7 @@ pub trait FileLoader { /// Text of the file. fn file_text(&self, file_id: FileId) -> Arc<str>; fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>; + /// Crates whose root's source root is the same as the source root of `file_id` fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>; } @@ -61,6 +62,9 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug { /// Parses the file into the syntax tree. fn parse(&self, file_id: FileId) -> Parse<ast::SourceFile>; + /// Returns the set of errors obtained from parsing the file including validation errors. + fn parse_errors(&self, file_id: FileId) -> Option<Arc<[SyntaxError]>>; + /// The crate graph. #[salsa::input] fn crate_graph(&self) -> Arc<CrateGraph>; @@ -81,12 +85,20 @@ fn toolchain_channel(db: &dyn SourceDatabase, krate: CrateId) -> Option<ReleaseC } fn parse(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { - let _p = tracing::span!(tracing::Level::INFO, "parse_query", ?file_id).entered(); + let _p = tracing::span!(tracing::Level::INFO, "parse", ?file_id).entered(); let text = db.file_text(file_id); // FIXME: Edition based parsing SourceFile::parse(&text, span::Edition::CURRENT) } +fn parse_errors(db: &dyn SourceDatabase, file_id: FileId) -> Option<Arc<[SyntaxError]>> { + let errors = db.parse(file_id).errors(); + match &*errors { + [] => None, + [..] => Some(errors.into()), + } +} + /// We don't want to give HIR knowledge of source roots, hence we extract these /// methods into a separate DB. #[salsa::query_group(SourceDatabaseExtStorage)] @@ -104,6 +116,7 @@ pub trait SourceDatabaseExt: SourceDatabase { #[salsa::input] fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; + /// Crates whose root fool is in `id`. fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>; } diff --git a/crates/cfg/src/tests.rs b/crates/cfg/src/tests.rs index a1ae15fcdd..dddaf2cce1 100644 --- a/crates/cfg/src/tests.rs +++ b/crates/cfg/src/tests.rs @@ -1,6 +1,6 @@ use arbitrary::{Arbitrary, Unstructured}; use expect_test::{expect, Expect}; -use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; +use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; use syntax::{ast, AstNode, Edition}; use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; @@ -8,7 +8,12 @@ use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; fn assert_parse_result(input: &str, expected: CfgExpr) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); + let tt = syntax_node_to_token_tree( + tt.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::ProcMacro, + ); let cfg = CfgExpr::parse(&tt); assert_eq!(cfg, expected); } @@ -16,7 +21,12 @@ fn assert_parse_result(input: &str, expected: CfgExpr) { fn check_dnf(input: &str, expect: Expect) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); + let tt = syntax_node_to_token_tree( + tt.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::ProcMacro, + ); let cfg = CfgExpr::parse(&tt); let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); expect.assert_eq(&actual); @@ -25,7 +35,12 @@ fn check_dnf(input: &str, expect: Expect) { fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); + let tt = syntax_node_to_token_tree( + tt.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::ProcMacro, + ); let cfg = CfgExpr::parse(&tt); let dnf = DnfExpr::new(cfg); let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); @@ -36,7 +51,12 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); + let tt = syntax_node_to_token_tree( + tt.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::ProcMacro, + ); let cfg = CfgExpr::parse(&tt); let dnf = DnfExpr::new(cfg); let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>(); diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 5dfaaf7742..6d5ca8321e 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -125,8 +125,10 @@ impl FlycheckHandle { 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); + let actor = + FlycheckActor::new(id, sender, config, sysroot_root, workspace_root, manifest_path); let (sender, receiver) = unbounded::<StateChange>(); let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("Flycheck".to_owned()) @@ -205,6 +207,7 @@ struct FlycheckActor { id: usize, sender: Box<dyn Fn(Message) + Send>, config: FlycheckConfig, + manifest_path: Option<AbsPathBuf>, /// Either the workspace root of the workspace we are flychecking, /// or the project root of the project. root: AbsPathBuf, @@ -233,6 +236,7 @@ impl FlycheckActor { config: FlycheckConfig, sysroot_root: Option<AbsPathBuf>, workspace_root: AbsPathBuf, + manifest_path: Option<AbsPathBuf>, ) -> FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { @@ -241,6 +245,7 @@ impl FlycheckActor { config, sysroot_root, root: workspace_root, + manifest_path, command_handle: None, command_receiver: None, } @@ -388,8 +393,13 @@ impl FlycheckActor { "--message-format=json" }); - cmd.arg("--manifest-path"); - cmd.arg(self.root.join("Cargo.toml")); + if let Some(manifest_path) = &self.manifest_path { + cmd.arg("--manifest-path"); + cmd.arg(manifest_path); + if manifest_path.extension().map_or(false, |ext| ext == "rs") { + cmd.arg("-Zscript"); + } + } options.apply_on_command(&mut cmd); (cmd, options.extra_args.clone()) diff --git a/crates/hir-def/src/attr/tests.rs b/crates/hir-def/src/attr/tests.rs index 9b68797fbf..727f442980 100644 --- a/crates/hir-def/src/attr/tests.rs +++ b/crates/hir-def/src/attr/tests.rs @@ -5,7 +5,7 @@ use triomphe::Arc; use base_db::FileId; use hir_expand::span_map::{RealSpanMap, SpanMap}; -use mbe::syntax_node_to_token_tree; +use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode}; use syntax::{ast, AstNode, TextRange}; use crate::attr::{DocAtom, DocExpr}; @@ -18,6 +18,7 @@ fn assert_parse_result(input: &str, expected: DocExpr) { tt.syntax(), map.as_ref(), map.span_for_range(TextRange::empty(0.into())), + DocCommentDesugarMode::ProcMacro, ); let cfg = DocExpr::parse(&tt); assert_eq!(cfg, expected); diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index c9f1add275..d2f4d7b7e5 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -10,9 +10,10 @@ use std::ops::Index; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; -use hir_expand::{name::Name, HirFileId, InFile}; +use hir_expand::{name::Name, InFile}; use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashMap; +use span::MacroFileId; use syntax::{ast, AstPtr, SyntaxNodePtr}; use triomphe::Arc; @@ -98,7 +99,7 @@ pub struct BodySourceMap { format_args_template_map: FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>, - expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, + expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>, /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in /// the source map (since they're just as volatile). @@ -349,11 +350,17 @@ impl BodySourceMap { self.expr_map.get(&src).cloned() } - pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> { + pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<MacroFileId> { let src = node.map(AstPtr::new); self.expansions.get(&src).cloned() } + pub fn macro_calls( + &self, + ) -> impl Iterator<Item = (InFile<AstPtr<ast::MacroCall>>, MacroFileId)> + '_ { + self.expansions.iter().map(|(&a, &b)| (a, b)) + } + pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> { self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax) } diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 340e95dbc2..82f89393ad 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -1006,7 +1006,9 @@ impl ExprCollector<'_> { Some((mark, expansion)) => { // Keep collecting even with expansion errors so we can provide completions and // other services in incomplete macro expressions. - self.source_map.expansions.insert(macro_call_ptr, self.expander.current_file_id()); + self.source_map + .expansions + .insert(macro_call_ptr, self.expander.current_file_id().macro_file().unwrap()); let prev_ast_id_map = mem::replace( &mut self.ast_id_map, self.db.ast_id_map(self.expander.current_file_id()), @@ -1869,42 +1871,45 @@ impl ExprCollector<'_> { ) -> ExprId { match count { Some(FormatCount::Literal(n)) => { - match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) { - Some(count_is) => { - let count_is = self.alloc_expr_desugared(Expr::Path(count_is)); - let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - *n as u128, - Some(BuiltinUint::Usize), - ))); - self.alloc_expr_desugared(Expr::Call { - callee: count_is, - args: Box::new([args]), - is_assignee_expr: false, - }) - } - None => self.missing_expr(), - } + let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( + *n as u128, + Some(BuiltinUint::Usize), + ))); + let count_is = + match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) { + Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), + None => self.missing_expr(), + }; + self.alloc_expr_desugared(Expr::Call { + callee: count_is, + args: Box::new([args]), + is_assignee_expr: false, + }) } Some(FormatCount::Argument(arg)) => { if let Ok(arg_index) = arg.index { let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize)); - match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param]) { - Some(count_param) => { - let count_param = self.alloc_expr_desugared(Expr::Path(count_param)); - let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - i as u128, - Some(BuiltinUint::Usize), - ))); - self.alloc_expr_desugared(Expr::Call { - callee: count_param, - args: Box::new([args]), - is_assignee_expr: false, - }) - } + let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( + i as u128, + Some(BuiltinUint::Usize), + ))); + let count_param = match LangItem::FormatCount.ty_rel_path( + self.db, + self.krate, + name![Param], + ) { + Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), None => self.missing_expr(), - } + }; + self.alloc_expr_desugared(Expr::Call { + callee: count_param, + args: Box::new([args]), + is_assignee_expr: false, + }) } else { + // FIXME: This drops arg causing it to potentially not be resolved/type checked + // when typing? self.missing_expr() } } @@ -1925,7 +1930,8 @@ impl ExprCollector<'_> { fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId { use ArgumentType::*; use FormatTrait::*; - match LangItem::FormatArgument.ty_rel_path( + + let new_fn = match LangItem::FormatArgument.ty_rel_path( self.db, self.krate, match ty { @@ -1941,16 +1947,14 @@ impl ExprCollector<'_> { Usize => name![from_usize], }, ) { - Some(new_fn) => { - let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn)); - self.alloc_expr_desugared(Expr::Call { - callee: new_fn, - args: Box::new([arg]), - is_assignee_expr: false, - }) - } + Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)), None => self.missing_expr(), - } + }; + self.alloc_expr_desugared(Expr::Call { + callee: new_fn, + args: Box::new([arg]), + is_assignee_expr: false, + }) } // endregion: format } diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs index 0020e4eac3..fd685235e1 100644 --- a/crates/hir-def/src/body/scope.rs +++ b/crates/hir-def/src/body/scope.rs @@ -7,7 +7,7 @@ use crate::{ body::Body, db::DefDatabase, hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement}, - BlockId, DefWithBodyId, + BlockId, ConstBlockId, DefWithBodyId, }; pub type ScopeId = Idx<ScopeData>; @@ -46,7 +46,9 @@ pub struct ScopeData { impl ExprScopes { pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> { let body = db.body(def); - let mut scopes = ExprScopes::new(&body); + let mut scopes = ExprScopes::new(&body, |const_block| { + db.lookup_intern_anonymous_const(const_block).root + }); scopes.shrink_to_fit(); Arc::new(scopes) } @@ -89,7 +91,10 @@ fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> { } impl ExprScopes { - fn new(body: &Body) -> ExprScopes { + fn new( + body: &Body, + resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, + ) -> ExprScopes { let mut scopes = ExprScopes { scopes: Arena::default(), scope_entries: Arena::default(), @@ -100,7 +105,7 @@ impl ExprScopes { scopes.add_bindings(body, root, self_param); } scopes.add_params_bindings(body, root, &body.params); - compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root); + compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root, resolve_const_block); scopes } @@ -183,35 +188,46 @@ fn compute_block_scopes( body: &Body, scopes: &mut ExprScopes, scope: &mut ScopeId, + resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, ) { for stmt in statements { match stmt { Statement::Let { pat, initializer, else_branch, .. } => { if let Some(expr) = initializer { - compute_expr_scopes(*expr, body, scopes, scope); + compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); } if let Some(expr) = else_branch { - compute_expr_scopes(*expr, body, scopes, scope); + compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); } *scope = scopes.new_scope(*scope); scopes.add_pat_bindings(body, *scope, *pat); } Statement::Expr { expr, .. } => { - compute_expr_scopes(*expr, body, scopes, scope); + compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); } Statement::Item => (), } } if let Some(expr) = tail { - compute_expr_scopes(expr, body, scopes, scope); + compute_expr_scopes(expr, body, scopes, scope, resolve_const_block); } } -fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: &mut ScopeId) { +fn compute_expr_scopes( + expr: ExprId, + body: &Body, + scopes: &mut ExprScopes, + scope: &mut ScopeId, + resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, +) { let make_label = |label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone())); + let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| { + compute_expr_scopes(expr, body, scopes, scope, resolve_const_block) + }; + scopes.set_scope(expr, *scope); match &body[expr] { Expr::Block { statements, tail, id, label } => { @@ -219,53 +235,54 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). scopes.set_scope(expr, scope); - compute_block_scopes(statements, *tail, body, scopes, &mut scope); + compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block); } - Expr::Const(_) => { - // FIXME: This is broken. + Expr::Const(id) => { + let mut scope = scopes.root_scope(); + compute_expr_scopes(scopes, resolve_const_block(*id), &mut scope); } Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => { let mut scope = scopes.new_block_scope(*scope, *id, None); // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). scopes.set_scope(expr, scope); - compute_block_scopes(statements, *tail, body, scopes, &mut scope); + compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block); } Expr::Loop { body: body_expr, label } => { let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); - compute_expr_scopes(*body_expr, body, scopes, &mut scope); + compute_expr_scopes(scopes, *body_expr, &mut scope); } Expr::Closure { args, body: body_expr, .. } => { let mut scope = scopes.new_scope(*scope); scopes.add_params_bindings(body, scope, args); - compute_expr_scopes(*body_expr, body, scopes, &mut scope); + compute_expr_scopes(scopes, *body_expr, &mut scope); } Expr::Match { expr, arms } => { - compute_expr_scopes(*expr, body, scopes, scope); + compute_expr_scopes(scopes, *expr, scope); for arm in arms.iter() { let mut scope = scopes.new_scope(*scope); scopes.add_pat_bindings(body, scope, arm.pat); if let Some(guard) = arm.guard { scope = scopes.new_scope(scope); - compute_expr_scopes(guard, body, scopes, &mut scope); + compute_expr_scopes(scopes, guard, &mut scope); } - compute_expr_scopes(arm.expr, body, scopes, &mut scope); + compute_expr_scopes(scopes, arm.expr, &mut scope); } } &Expr::If { condition, then_branch, else_branch } => { let mut then_branch_scope = scopes.new_scope(*scope); - compute_expr_scopes(condition, body, scopes, &mut then_branch_scope); - compute_expr_scopes(then_branch, body, scopes, &mut then_branch_scope); + compute_expr_scopes(scopes, condition, &mut then_branch_scope); + compute_expr_scopes(scopes, then_branch, &mut then_branch_scope); if let Some(else_branch) = else_branch { - compute_expr_scopes(else_branch, body, scopes, scope); + compute_expr_scopes(scopes, else_branch, scope); } } &Expr::Let { pat, expr } => { - compute_expr_scopes(expr, body, scopes, scope); + compute_expr_scopes(scopes, expr, scope); *scope = scopes.new_scope(*scope); scopes.add_pat_bindings(body, *scope, pat); } - e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)), + e => e.walk_child_exprs(|e| compute_expr_scopes(scopes, e, scope)), }; } diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs index a27ffe2167..4c8a54f7c8 100644 --- a/crates/hir-def/src/body/tests.rs +++ b/crates/hir-def/src/body/tests.rs @@ -318,18 +318,20 @@ fn f() { expect![[r#" fn f() { - $crate::panicking::panic_fmt( - builtin#lang(Arguments::new_v1_formatted)( - &[ - "cc", - ], - &[], - &[], - unsafe { - builtin#lang(UnsafeArg::new)() - }, - ), - ); + { + $crate::panicking::panic_fmt( + builtin#lang(Arguments::new_v1_formatted)( + &[ + "cc", + ], + &[], + &[], + unsafe { + builtin#lang(UnsafeArg::new)() + }, + ), + ); + }; }"#]] .assert_eq(&body.pretty_print(&db, def)) } diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index e3d750d33c..51a4dd6f42 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -229,7 +229,7 @@ pub struct TraitData { /// method calls to this trait's methods when the receiver is an array and the crate edition is /// 2015 or 2018. // box it as the vec is usually empty anyways - pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>, + pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>, } impl TraitData { @@ -258,12 +258,12 @@ impl TraitData { let mut collector = AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr)); collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items); - let (items, attribute_calls, diagnostics) = collector.finish(); + let (items, macro_calls, diagnostics) = collector.finish(); ( Arc::new(TraitData { name, - attribute_calls, + macro_calls, items, is_auto, is_unsafe, @@ -298,7 +298,7 @@ impl TraitData { } pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ { - self.attribute_calls.iter().flat_map(|it| it.iter()).copied() + self.macro_calls.iter().flat_map(|it| it.iter()).copied() } } @@ -319,7 +319,7 @@ impl TraitAliasData { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub struct ImplData { pub target_trait: Option<Interned<TraitRef>>, pub self_ty: Interned<TypeRef>, @@ -327,7 +327,7 @@ pub struct ImplData { pub is_negative: bool, pub is_unsafe: bool, // box it as the vec is usually empty anyways - pub attribute_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>, + pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>, } impl ImplData { @@ -354,7 +354,7 @@ impl ImplData { AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::ImplId(id)); collector.collect(&item_tree, tree_id.tree_id(), &impl_def.items); - let (items, attribute_calls, diagnostics) = collector.finish(); + let (items, macro_calls, diagnostics) = collector.finish(); let items = items.into_iter().map(|(_, item)| item).collect(); ( @@ -364,14 +364,14 @@ impl ImplData { items, is_negative, is_unsafe, - attribute_calls, + macro_calls, }), DefDiagnostics::new(diagnostics), ) } pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ { - self.attribute_calls.iter().flat_map(|it| it.iter()).copied() + self.macro_calls.iter().flat_map(|it| it.iter()).copied() } } @@ -573,7 +573,7 @@ struct AssocItemCollector<'a> { expander: Expander, items: Vec<(Name, AssocItemId)>, - attr_calls: Vec<(AstId<ast::Item>, MacroCallId)>, + macro_calls: Vec<(AstId<ast::Item>, MacroCallId)>, } impl<'a> AssocItemCollector<'a> { @@ -590,7 +590,7 @@ impl<'a> AssocItemCollector<'a> { container, expander: Expander::new(db, file_id, module_id), items: Vec::new(), - attr_calls: Vec::new(), + macro_calls: Vec::new(), diagnostics: Vec::new(), } } @@ -604,7 +604,7 @@ impl<'a> AssocItemCollector<'a> { ) { ( self.items, - if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) }, + if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) }, self.diagnostics, ) } @@ -662,11 +662,11 @@ impl<'a> AssocItemCollector<'a> { } } - self.attr_calls.push((ast_id, call_id)); + self.macro_calls.push((ast_id, call_id)); let res = self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id); - self.collect_macro_items(res, &|| loc.kind.clone()); + self.collect_macro_items(res); continue 'items; } Ok(_) => (), @@ -698,24 +698,22 @@ impl<'a> AssocItemCollector<'a> { match item { AssocItem::Function(id) => { let item = &item_tree[id]; - let def = FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); self.items.push((item.name.clone(), def.into())); } - AssocItem::Const(id) => { - let item = &item_tree[id]; - let Some(name) = item.name.clone() else { return }; - let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); - self.items.push((name, def.into())); - } AssocItem::TypeAlias(id) => { let item = &item_tree[id]; - let def = TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); self.items.push((item.name.clone(), def.into())); } + AssocItem::Const(id) => { + let item = &item_tree[id]; + let Some(name) = item.name.clone() else { return }; + let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); + self.items.push((name, def.into())); + } AssocItem::MacroCall(call) => { let file_id = self.expander.current_file_id(); let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call]; @@ -745,11 +743,8 @@ impl<'a> AssocItemCollector<'a> { Ok(Some(call_id)) => { let res = self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id); - self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike { - ast_id: InFile::new(file_id, ast_id), - expand_to: hir_expand::ExpandTo::Items, - eager: None, - }); + self.macro_calls.push((InFile::new(file_id, ast_id.upcast()), call_id)); + self.collect_macro_items(res); } Ok(None) => (), Err(_) => { @@ -768,39 +763,8 @@ impl<'a> AssocItemCollector<'a> { } } - fn collect_macro_items( - &mut self, - ExpandResult { value, err }: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>, - error_call_kind: &dyn Fn() -> hir_expand::MacroCallKind, - ) { - let Some((mark, parse)) = value else { return }; - - if let Some(err) = err { - let diag = match err { - // why is this reported here? - hir_expand::ExpandError::UnresolvedProcMacro(krate) => { - DefDiagnostic::unresolved_proc_macro( - self.module_id.local_id, - error_call_kind(), - krate, - ) - } - _ => DefDiagnostic::macro_error( - self.module_id.local_id, - error_call_kind(), - err.to_string(), - ), - }; - self.diagnostics.push(diag); - } - let errors = parse.errors(); - if !errors.is_empty() { - self.diagnostics.push(DefDiagnostic::macro_expansion_parse_error( - self.module_id.local_id, - error_call_kind(), - errors.into_boxed_slice(), - )); - } + fn collect_macro_items(&mut self, res: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>) { + let Some((mark, _parse)) = res.value else { return }; let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None); let item_tree = tree_id.item_tree(self.db); diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs index bf728a7107..4e57845a69 100644 --- a/crates/hir-def/src/find_path.rs +++ b/crates/hir-def/src/find_path.rs @@ -1177,6 +1177,8 @@ pub mod fmt { //- /main.rs crate:main deps:alloc,std #![no_std] +extern crate alloc; + $0 //- /std.rs crate:std deps:alloc diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index acc60e1d9e..10a1d65bb9 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -20,7 +20,7 @@ use triomphe::Arc; use crate::{ db::DefDatabase, expander::Expander, - item_tree::{GenericsItemTreeNode, ItemTree}, + item_tree::{AttrOwner, FileItemTreeId, GenericModItem, GenericsItemTreeNode, ItemTree}, lower::LowerCtx, nameres::{DefMap, MacroSubNs}, type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, @@ -339,6 +339,7 @@ impl GenericParamsCollector { target: Either<TypeRef, LifetimeRef>, ) { let bound = TypeBound::from_ast(lower_ctx, bound); + self.fill_impl_trait_bounds(lower_ctx.take_impl_traits_bounds()); let predicate = match (target, bound) { (Either::Left(type_ref), bound) => match hrtb_lifetimes { Some(hrtb_lifetimes) => WherePredicate::ForLifetime { @@ -359,7 +360,24 @@ impl GenericParamsCollector { self.where_predicates.push(predicate); } - pub(crate) fn fill_implicit_impl_trait_args( + fn fill_impl_trait_bounds(&mut self, impl_bounds: Vec<Vec<Interned<TypeBound>>>) { + for bounds in impl_bounds { + let param = TypeParamData { + name: None, + default: None, + provenance: TypeParamProvenance::ArgumentImplTrait, + }; + let param_id = self.type_or_consts.alloc(param.into()); + for bound in bounds { + self.where_predicates.push(WherePredicate::TypeBound { + target: WherePredicateTypeTarget::TypeOrConstParam(param_id), + bound, + }); + } + } + } + + fn fill_implicit_impl_trait_args( &mut self, db: &dyn DefDatabase, exp: &mut Lazy<(Arc<DefMap>, Expander), impl FnOnce() -> (Arc<DefMap>, Expander)>, @@ -456,56 +474,67 @@ impl GenericParams { let cfg_options = &cfg_options[krate].cfg_options; // Returns the generic parameters that are enabled under the current `#[cfg]` options - let enabled_params = |params: &Interned<GenericParams>, item_tree: &ItemTree| { - let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); - - // In the common case, no parameters will by disabled by `#[cfg]` attributes. - // Therefore, make a first pass to check if all parameters are enabled and, if so, - // clone the `Interned<GenericParams>` instead of recreating an identical copy. - let all_type_or_consts_enabled = - params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into())); - let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into())); - - if all_type_or_consts_enabled && all_lifetimes_enabled { - params.clone() - } else { - Interned::new(GenericParams { - type_or_consts: all_type_or_consts_enabled - .then(|| params.type_or_consts.clone()) - .unwrap_or_else(|| { - params - .type_or_consts - .iter() - .filter(|(idx, _)| enabled((*idx).into())) - .map(|(_, param)| param.clone()) - .collect() - }), - lifetimes: all_lifetimes_enabled - .then(|| params.lifetimes.clone()) - .unwrap_or_else(|| { - params - .lifetimes - .iter() - .filter(|(idx, _)| enabled((*idx).into())) - .map(|(_, param)| param.clone()) - .collect() - }), - where_predicates: params.where_predicates.clone(), - }) - } - }; + let enabled_params = + |params: &Interned<GenericParams>, item_tree: &ItemTree, parent: GenericModItem| { + let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options); + let attr_owner_ct = |param| AttrOwner::TypeOrConstParamData(parent, param); + let attr_owner_lt = |param| AttrOwner::LifetimeParamData(parent, param); + + // In the common case, no parameters will by disabled by `#[cfg]` attributes. + // Therefore, make a first pass to check if all parameters are enabled and, if so, + // clone the `Interned<GenericParams>` instead of recreating an identical copy. + let all_type_or_consts_enabled = + params.type_or_consts.iter().all(|(idx, _)| enabled(attr_owner_ct(idx))); + let all_lifetimes_enabled = + params.lifetimes.iter().all(|(idx, _)| enabled(attr_owner_lt(idx))); + + if all_type_or_consts_enabled && all_lifetimes_enabled { + params.clone() + } else { + Interned::new(GenericParams { + type_or_consts: all_type_or_consts_enabled + .then(|| params.type_or_consts.clone()) + .unwrap_or_else(|| { + params + .type_or_consts + .iter() + .filter(|&(idx, _)| enabled(attr_owner_ct(idx))) + .map(|(_, param)| param.clone()) + .collect() + }), + lifetimes: all_lifetimes_enabled + .then(|| params.lifetimes.clone()) + .unwrap_or_else(|| { + params + .lifetimes + .iter() + .filter(|&(idx, _)| enabled(attr_owner_lt(idx))) + .map(|(_, param)| param.clone()) + .collect() + }), + where_predicates: params.where_predicates.clone(), + }) + } + }; fn id_to_generics<Id: GenericsItemTreeNode>( db: &dyn DefDatabase, id: impl for<'db> Lookup< Database<'db> = dyn DefDatabase + 'db, Data = impl ItemTreeLoc<Id = Id>, >, - enabled_params: impl Fn(&Interned<GenericParams>, &ItemTree) -> Interned<GenericParams>, - ) -> Interned<GenericParams> { + enabled_params: impl Fn( + &Interned<GenericParams>, + &ItemTree, + GenericModItem, + ) -> Interned<GenericParams>, + ) -> Interned<GenericParams> + where + FileItemTreeId<Id>: Into<GenericModItem>, + { let id = id.lookup(db).item_tree_id(); let tree = id.item_tree(db); let item = &tree[id.value]; - enabled_params(item.generic_params(), &tree) + enabled_params(item.generic_params(), &tree, id.value.into()) } match def { @@ -514,7 +543,8 @@ impl GenericParams { let tree = loc.id.item_tree(db); let item = &tree[loc.id.value]; - let enabled_params = enabled_params(&item.explicit_generic_params, &tree); + let enabled_params = + enabled_params(&item.explicit_generic_params, &tree, loc.id.value.into()); let module = loc.container.module(db); let func_data = db.function_data(id); diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index ac0caaf0dc..2f7ebbfec1 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -136,15 +136,15 @@ impl From<ast::LiteralKind> for Literal { Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty) } LiteralKind::ByteString(bs) => { - let text = bs.value().map(Box::from).unwrap_or_else(Default::default); + let text = bs.value().map_or_else(|_| Default::default(), Box::from); Literal::ByteString(text) } LiteralKind::String(s) => { - let text = s.value().map(Box::from).unwrap_or_else(Default::default); + let text = s.value().map_or_else(|_| Default::default(), Box::from); Literal::String(text) } LiteralKind::CString(s) => { - let text = s.value().map(Box::from).unwrap_or_else(Default::default); + let text = s.value().map_or_else(|_| Default::default(), Box::from); Literal::CString(text) } LiteralKind::Byte(b) => { diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index a60b9f9f3a..54cd57110e 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -234,6 +234,14 @@ impl ItemScope { self.impls.iter().copied() } + pub fn all_macro_calls(&self) -> impl Iterator<Item = MacroCallId> + '_ { + self.macro_invocations.values().copied().chain(self.attr_macros.values().copied()).chain( + self.derive_macros.values().flat_map(|it| { + it.iter().flat_map(|it| it.derive_call_ids.iter().copied().flatten()) + }), + ) + } + pub(crate) fn modules_in_scope(&self) -> impl Iterator<Item = (ModuleId, Visibility)> + '_ { self.types.values().copied().filter_map(|(def, vis, _)| match def { ModuleDefId::ModuleId(module) => Some((module, vis)), diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 610480736c..acda64c41f 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -29,6 +29,7 @@ //! //! In general, any item in the `ItemTree` stores its `AstId`, which allows mapping it back to its //! surface syntax. +#![allow(unexpected_cfgs)] mod lower; mod pretty; @@ -57,21 +58,21 @@ use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, - generics::{GenericParams, LifetimeParamData, TypeOrConstParamData}, + generics::GenericParams, path::{GenericArgs, ImportAlias, ModPath, Path, PathKind}, type_ref::{Mutability, TraitRef, TypeBound, TypeRef}, visibility::{RawVisibility, VisibilityExplicitness}, - BlockId, Lookup, + BlockId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, }; #[derive(Copy, Clone, Eq, PartialEq)] pub struct RawVisibilityId(u32); impl RawVisibilityId { - pub const PUB: Self = RawVisibilityId(u32::max_value()); - pub const PRIV_IMPLICIT: Self = RawVisibilityId(u32::max_value() - 1); - pub const PRIV_EXPLICIT: Self = RawVisibilityId(u32::max_value() - 2); - pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 3); + pub const PUB: Self = RawVisibilityId(u32::MAX); + pub const PRIV_IMPLICIT: Self = RawVisibilityId(u32::MAX - 1); + pub const PRIV_EXPLICIT: Self = RawVisibilityId(u32::MAX - 2); + pub const PUB_CRATE: Self = RawVisibilityId(u32::MAX - 3); } impl fmt::Debug for RawVisibilityId { @@ -293,8 +294,8 @@ pub enum AttrOwner { Variant(FileItemTreeId<Variant>), Field(Idx<Field>), Param(Idx<Param>), - TypeOrConstParamData(Idx<TypeOrConstParamData>), - LifetimeParamData(Idx<LifetimeParamData>), + TypeOrConstParamData(GenericModItem, LocalTypeOrConstParamId), + LifetimeParamData(GenericModItem, LocalLifetimeParamId), } macro_rules! from_attrs { @@ -314,8 +315,6 @@ from_attrs!( Variant(FileItemTreeId<Variant>), Field(Idx<Field>), Param(Idx<Param>), - TypeOrConstParamData(Idx<TypeOrConstParamData>), - LifetimeParamData(Idx<LifetimeParamData>), ); /// Trait implemented by all nodes in the item tree. @@ -465,12 +464,49 @@ macro_rules! mod_items { )+ } + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] + pub enum GenericModItem { + $( + $( + #[cfg_attr(ignore_fragment, $generic_params)] + $typ(FileItemTreeId<$typ>), + )? + )+ + } + + impl From<GenericModItem> for ModItem { + fn from(id: GenericModItem) -> ModItem { + match id { + $( + $( + #[cfg_attr(ignore_fragment, $generic_params)] + GenericModItem::$typ(id) => ModItem::$typ(id), + )? + )+ + } + } + } + + impl From<GenericModItem> for AttrOwner { + fn from(t: GenericModItem) -> AttrOwner { + AttrOwner::ModItem(t.into()) + } + } + $( impl From<FileItemTreeId<$typ>> for ModItem { fn from(id: FileItemTreeId<$typ>) -> ModItem { ModItem::$typ(id) } } + $( + #[cfg_attr(ignore_fragment, $generic_params)] + impl From<FileItemTreeId<$typ>> for GenericModItem { + fn from(id: FileItemTreeId<$typ>) -> GenericModItem { + GenericModItem::$typ(id) + } + } + )? )+ $( diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 4b5ef56d78..199b8daa37 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -4,6 +4,7 @@ use std::collections::hash_map::Entry; use hir_expand::{mod_path::path, name, name::AsName, span_map::SpanMapRef, HirFileId}; use la_arena::Arena; +use rustc_hash::FxHashMap; use span::{AstIdMap, SyntaxContextId}; use syntax::{ ast::{self, HasModuleItem, HasName, HasTypeBounds, IsString}, @@ -16,11 +17,11 @@ use crate::{ generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance}, item_tree::{ AssocItem, AttrOwner, Const, Either, Enum, ExternBlock, ExternCrate, Field, FieldAstId, - Fields, FileItemTreeId, FnFlags, Function, GenericArgs, Idx, IdxRange, Impl, ImportAlias, - Interned, ItemTree, ItemTreeData, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, - ModItem, ModKind, ModPath, Mutability, Name, Param, ParamAstId, Path, Range, RawAttrs, - RawIdx, RawVisibilityId, Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, - Use, UseTree, UseTreeKind, Variant, + Fields, FileItemTreeId, FnFlags, Function, GenericArgs, GenericModItem, Idx, IdxRange, + Impl, ImportAlias, Interned, ItemTree, ItemTreeData, ItemTreeNode, Macro2, MacroCall, + MacroRules, Mod, ModItem, ModKind, ModPath, Mutability, Name, Param, ParamAstId, Path, + Range, RawAttrs, RawIdx, RawVisibilityId, Static, Struct, StructKind, Trait, TraitAlias, + TypeAlias, Union, Use, UseTree, UseTreeKind, Variant, }, path::AssociatedTypeBinding, type_ref::{LifetimeRef, TraitBoundModifier, TraitRef, TypeBound, TypeRef}, @@ -36,6 +37,8 @@ pub(super) struct Ctx<'a> { db: &'a dyn DefDatabase, tree: ItemTree, source_ast_id_map: Arc<AstIdMap>, + generic_param_attr_buffer: + FxHashMap<Either<LocalTypeOrConstParamId, LocalLifetimeParamId>, RawAttrs>, body_ctx: crate::lower::LowerCtx<'a>, } @@ -44,6 +47,7 @@ impl<'a> Ctx<'a> { Self { db, tree: ItemTree::default(), + generic_param_attr_buffer: FxHashMap::default(), source_ast_id_map: db.ast_id_map(file), body_ctx: crate::lower::LowerCtx::new(db, file), } @@ -56,6 +60,7 @@ impl<'a> Ctx<'a> { pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree { self.tree.top_level = item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect(); + assert!(self.generic_param_attr_buffer.is_empty()); self.tree } @@ -89,6 +94,7 @@ impl<'a> Ctx<'a> { } } + assert!(self.generic_param_attr_buffer.is_empty()); self.tree } @@ -117,6 +123,7 @@ impl<'a> Ctx<'a> { } } + assert!(self.generic_param_attr_buffer.is_empty()); self.tree } @@ -185,10 +192,12 @@ impl<'a> Ctx<'a> { let visibility = self.lower_visibility(strukt); let name = strukt.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(strukt); - let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt); let fields = self.lower_fields(&strukt.kind()); + let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt); let res = Struct { name, visibility, generic_params, fields, ast_id }; - Some(id(self.data().structs.alloc(res))) + let id = id(self.data().structs.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields { @@ -252,28 +261,32 @@ impl<'a> Ctx<'a> { let visibility = self.lower_visibility(union); let name = union.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(union); - let generic_params = self.lower_generic_params(HasImplicitSelf::No, union); let fields = match union.record_field_list() { Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)), None => Fields::Record(IdxRange::new(self.next_field_idx()..self.next_field_idx())), }; + let generic_params = self.lower_generic_params(HasImplicitSelf::No, union); let res = Union { name, visibility, generic_params, fields, ast_id }; - Some(id(self.data().unions.alloc(res))) + let id = id(self.data().unions.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<FileItemTreeId<Enum>> { let visibility = self.lower_visibility(enum_); let name = enum_.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(enum_); - let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_); let variants = match &enum_.variant_list() { Some(variant_list) => self.lower_variants(variant_list), None => { FileItemTreeId(self.next_variant_idx())..FileItemTreeId(self.next_variant_idx()) } }; + let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_); let res = Enum { name, visibility, generic_params, variants, ast_id }; - Some(id(self.data().enums.alloc(res))) + let id = id(self.data().enums.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_variants(&mut self, variants: &ast::VariantList) -> Range<FileItemTreeId<Variant>> { @@ -414,7 +427,9 @@ impl<'a> Ctx<'a> { flags, }; - Some(id(self.data().functions.alloc(res))) + let id = id(self.data().functions.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_type_alias( @@ -428,7 +443,9 @@ impl<'a> Ctx<'a> { let ast_id = self.source_ast_id_map.ast_id(type_alias); let generic_params = self.lower_generic_params(HasImplicitSelf::No, type_alias); let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id }; - Some(id(self.data().type_aliases.alloc(res))) + let id = id(self.data().type_aliases.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_static(&mut self, static_: &ast::Static) -> Option<FileItemTreeId<Static>> { @@ -475,8 +492,6 @@ impl<'a> Ctx<'a> { let name = trait_def.name()?.as_name(); let visibility = self.lower_visibility(trait_def); let ast_id = self.source_ast_id_map.ast_id(trait_def); - let generic_params = - self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def); let is_auto = trait_def.auto_token().is_some(); let is_unsafe = trait_def.unsafe_token().is_some(); @@ -487,8 +502,12 @@ impl<'a> Ctx<'a> { .filter_map(|item_node| self.lower_assoc_item(&item_node)) .collect(); + let generic_params = + self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def); let def = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id }; - Some(id(self.data().traits.alloc(def))) + let id = id(self.data().traits.alloc(def)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_trait_alias( @@ -504,19 +523,18 @@ impl<'a> Ctx<'a> { ); let alias = TraitAlias { name, visibility, generic_params, ast_id }; - Some(id(self.data().trait_aliases.alloc(alias))) + let id = id(self.data().trait_aliases.alloc(alias)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<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 - // type alias rather than a type parameter, so this is handled by the resolver. - let generic_params = self.lower_generic_params(HasImplicitSelf::No, impl_def); // FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl // as if it was an non-trait impl. Ideally we want to create a unique missing ref that only // equals itself. - let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr)); let self_ty = self.lower_type_ref(&impl_def.self_ty()?); + let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr)); let is_negative = impl_def.excl_token().is_some(); let is_unsafe = impl_def.unsafe_token().is_some(); @@ -527,9 +545,14 @@ impl<'a> Ctx<'a> { .flat_map(|it| it.assoc_items()) .filter_map(|item| self.lower_assoc_item(&item)) .collect(); + // Note that trait impls don't get implicit `Self` unlike traits, because here they are a + // type alias rather than a type parameter, so this is handled by the resolver. + let generic_params = self.lower_generic_params(HasImplicitSelf::No, impl_def); let res = Impl { generic_params, target_trait, self_ty, is_negative, is_unsafe, items, ast_id }; - Some(id(self.data().impls.alloc(res))) + let id = id(self.data().impls.alloc(res)); + self.write_generic_params_attributes(id.into()); + Some(id) } fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Use>> { @@ -616,11 +639,30 @@ impl<'a> Ctx<'a> { id(self.data().extern_blocks.alloc(res)) } + fn write_generic_params_attributes(&mut self, parent: GenericModItem) { + self.generic_param_attr_buffer.drain().for_each(|(idx, attrs)| { + self.tree.attrs.insert( + match idx { + Either::Left(id) => AttrOwner::TypeOrConstParamData(parent, id), + Either::Right(id) => AttrOwner::LifetimeParamData(parent, id), + }, + attrs, + ); + }) + } + fn lower_generic_params( &mut self, has_implicit_self: HasImplicitSelf, node: &dyn ast::HasGenericParams, ) -> Interned<GenericParams> { + debug_assert!(self.generic_param_attr_buffer.is_empty(),); + let add_param_attrs = |item: Either<LocalTypeOrConstParamId, LocalLifetimeParamId>, + param| { + let attrs = RawAttrs::new(self.db.upcast(), ¶m, self.body_ctx.span_map()); + debug_assert!(self.generic_param_attr_buffer.insert(item, attrs).is_none()); + }; + self.body_ctx.take_impl_traits_bounds(); let mut generics = GenericParamsCollector::default(); if let HasImplicitSelf::Yes(bounds) = has_implicit_self { @@ -635,28 +677,13 @@ impl<'a> Ctx<'a> { ); // add super traits as bounds on Self // i.e., `trait Foo: Bar` is equivalent to `trait Foo where Self: Bar` - let self_param = TypeRef::Path(name![Self].into()); - generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param)); + generics.fill_bounds( + &self.body_ctx, + bounds, + Either::Left(TypeRef::Path(name![Self].into())), + ); } - let add_param_attrs = |item: Either<LocalTypeOrConstParamId, LocalLifetimeParamId>, - param| { - let attrs = RawAttrs::new(self.db.upcast(), ¶m, self.body_ctx.span_map()); - // This is identical to the body of `Ctx::add_attrs()` but we can't call that here - // because it requires `&mut self` and the call to `generics.fill()` below also - // references `self`. - match self.tree.attrs.entry(match item { - Either::Right(id) => id.into(), - Either::Left(id) => id.into(), - }) { - Entry::Occupied(mut entry) => { - *entry.get_mut() = entry.get().merge(attrs); - } - Entry::Vacant(entry) => { - entry.insert(attrs); - } - } - }; generics.fill(&self.body_ctx, node, add_param_attrs); Interned::new(generics.finish()) diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index cef2a3fb86..2803678a33 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -8,8 +8,8 @@ use crate::{ generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, item_tree::{ AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldAstId, Fields, - FileItemTreeId, FnFlags, Function, GenericParams, Impl, Interned, ItemTree, Macro2, - MacroCall, MacroRules, Mod, ModItem, ModKind, Param, ParamAstId, Path, RawAttrs, + FileItemTreeId, FnFlags, Function, GenericModItem, GenericParams, Impl, Interned, ItemTree, + Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, ParamAstId, Path, RawAttrs, RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound, TypeRef, Union, Use, UseTree, UseTreeKind, Variant, }, @@ -276,7 +276,7 @@ impl Printer<'_> { w!(self, "extern \"{}\" ", abi); } w!(self, "fn {}", name.display(self.db.upcast())); - self.print_generic_params(explicit_generic_params); + self.print_generic_params(explicit_generic_params, it.into()); w!(self, "("); if !params.is_empty() { self.indented(|this| { @@ -316,7 +316,7 @@ impl Printer<'_> { self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "struct {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { wln!(self); @@ -329,7 +329,7 @@ impl Printer<'_> { self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "union {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { wln!(self); @@ -342,7 +342,7 @@ impl Printer<'_> { self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "enum {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { for variant in FileItemTreeId::range_iter(variants.clone()) { @@ -394,7 +394,7 @@ impl Printer<'_> { w!(self, "auto "); } w!(self, "trait {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { for item in &**items { @@ -408,7 +408,7 @@ impl Printer<'_> { self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "trait {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); w!(self, " = "); self.print_where_clause(generic_params); w!(self, ";"); @@ -429,7 +429,7 @@ impl Printer<'_> { w!(self, "unsafe"); } w!(self, "impl"); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); w!(self, " "); if *is_negative { w!(self, "!"); @@ -453,7 +453,7 @@ impl Printer<'_> { self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "type {}", name.display(self.db.upcast())); - self.print_generic_params(generic_params); + self.print_generic_params(generic_params, it.into()); if !bounds.is_empty() { w!(self, ": "); self.print_type_bounds(bounds); @@ -525,7 +525,7 @@ impl Printer<'_> { print_path(self.db, path, self).unwrap(); } - fn print_generic_params(&mut self, params: &GenericParams) { + fn print_generic_params(&mut self, params: &GenericParams, parent: GenericModItem) { if params.is_empty() { return; } @@ -537,7 +537,7 @@ impl Printer<'_> { w!(self, ", "); } first = false; - self.print_attrs_of(idx, " "); + self.print_attrs_of(AttrOwner::LifetimeParamData(parent, idx), " "); w!(self, "{}", lt.name.display(self.db.upcast())); } for (idx, x) in params.type_or_consts.iter() { @@ -545,7 +545,7 @@ impl Printer<'_> { w!(self, ", "); } first = false; - self.print_attrs_of(idx, " "); + self.print_attrs_of(AttrOwner::TypeOrConstParamData(parent, idx), " "); match x { TypeOrConstParamData::TypeParamData(ty) => match &ty.name { Some(name) => w!(self, "{}", name.display(self.db.upcast())), diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs index 48da876ac1..79bab11998 100644 --- a/crates/hir-def/src/item_tree/tests.rs +++ b/crates/hir-def/src/item_tree/tests.rs @@ -427,10 +427,18 @@ fn generics_with_attributes() { check( r#" struct S<#[cfg(never)] T>; +struct S<A, B, #[cfg(never)] C>; +struct S<A, #[cfg(never)] B, C>; "#, expect![[r#" // AstId: 1 pub(self) struct S<#[cfg(never)] T>; + + // AstId: 2 + pub(self) struct S<A, B, #[cfg(never)] C>; + + // AstId: 3 + pub(self) struct S<A, #[cfg(never)] B, C>; "#]], ) } diff --git a/crates/hir-def/src/lower.rs b/crates/hir-def/src/lower.rs index d574d80a8e..ecd8d79f20 100644 --- a/crates/hir-def/src/lower.rs +++ b/crates/hir-def/src/lower.rs @@ -1,26 +1,34 @@ //! Context for lowering paths. -use std::cell::OnceCell; +use std::cell::{OnceCell, RefCell}; use hir_expand::{ span_map::{SpanMap, SpanMapRef}, AstId, HirFileId, InFile, }; +use intern::Interned; use span::{AstIdMap, AstIdNode}; use syntax::ast; use triomphe::Arc; -use crate::{db::DefDatabase, path::Path}; +use crate::{db::DefDatabase, path::Path, type_ref::TypeBound}; pub struct LowerCtx<'a> { pub db: &'a dyn DefDatabase, file_id: HirFileId, span_map: OnceCell<SpanMap>, ast_id_map: OnceCell<Arc<AstIdMap>>, + impl_trait_bounds: RefCell<Vec<Vec<Interned<TypeBound>>>>, } impl<'a> LowerCtx<'a> { pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self { - LowerCtx { db, file_id, span_map: OnceCell::new(), ast_id_map: OnceCell::new() } + LowerCtx { + db, + file_id, + span_map: OnceCell::new(), + ast_id_map: OnceCell::new(), + impl_trait_bounds: RefCell::new(Vec::new()), + } } pub fn with_span_map_cell( @@ -28,7 +36,13 @@ impl<'a> LowerCtx<'a> { file_id: HirFileId, span_map: OnceCell<SpanMap>, ) -> Self { - LowerCtx { db, file_id, span_map, ast_id_map: OnceCell::new() } + LowerCtx { + db, + file_id, + span_map, + ast_id_map: OnceCell::new(), + impl_trait_bounds: RefCell::new(Vec::new()), + } } pub(crate) fn span_map(&self) -> SpanMapRef<'_> { @@ -45,4 +59,12 @@ impl<'a> LowerCtx<'a> { self.ast_id_map.get_or_init(|| self.db.ast_id_map(self.file_id)).ast_id(item), ) } + + pub fn update_impl_traits_bounds(&self, bounds: Vec<Interned<TypeBound>>) { + self.impl_trait_bounds.borrow_mut().push(bounds); + } + + pub fn take_impl_traits_bounds(&self) -> Vec<Vec<Interned<TypeBound>>> { + self.impl_trait_bounds.take() + } } diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs index a4864c74d7..6f605c0cb3 100644 --- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -186,3 +186,33 @@ fn#0:[email protected]#0# foo#0:[email protected]#0#(#0:[email protected]#0#�:[email protected]#0#self#0:[email protected]# }#0:[email protected]#0#"#]], ); } + +#[test] +fn attribute_macro_doc_desugaring() { + check( + r#" +//- proc_macros: identity +#[proc_macros::identity] +/// doc string \n with newline +/** + MultiLines Doc + MultiLines Doc +*/ +#[doc = "doc attr"] +struct S; +"#, + expect![[r##" +#[proc_macros::identity] +/// doc string \n with newline +/** + MultiLines Doc + MultiLines Doc +*/ +#[doc = "doc attr"] +struct S; + +#[doc = " doc string \\n with newline"] +#[doc = "\n MultiLines Doc\n MultiLines Doc\n"] +#[doc = "doc attr"] struct S;"##]], + ); +} diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 0a6cd0fe9e..262bc538b9 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -5,7 +5,7 @@ use std::{cmp::Ordering, iter, mem, ops::Not}; -use base_db::{CrateId, Dependency, FileId}; +use base_db::{CrateId, CrateOrigin, Dependency, FileId, LangCrateOrigin}; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ @@ -15,15 +15,13 @@ use hir_expand::{ builtin_fn_macro::find_builtin_macro, name::{name, AsName, Name}, proc_macro::CustomProcMacroExpander, - ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, - MacroDefId, MacroDefKind, + ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, }; use itertools::{izip, Itertools}; use la_arena::Idx; use limit::Limit; use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContextId}; -use stdx::always; use syntax::ast; use triomphe::Arc; @@ -279,7 +277,8 @@ impl DefCollector<'_> { fn seed_with_top_level(&mut self) { let _p = tracing::span!(tracing::Level::INFO, "seed_with_top_level").entered(); - let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; + let crate_graph = self.db.crate_graph(); + let file_id = crate_graph[self.def_map.krate].root_file_id; let item_tree = self.db.file_item_tree(file_id.into()); let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); @@ -288,19 +287,14 @@ impl DefCollector<'_> { crate_data.proc_macro_loading_error = Some(e.clone()); } - for (name, dep) in &self.deps { - if dep.is_prelude() { - crate_data - .extern_prelude - .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None)); - } - } + let mut process = true; // Process other crate-level attributes. for attr in &*attrs { if let Some(cfg) = attr.cfg() { if self.cfg_options.check(&cfg) == Some(false) { - return; + process = false; + break; } } let Some(attr_name) = attr.path.as_ident() else { continue }; @@ -350,9 +344,38 @@ impl DefCollector<'_> { } } - crate_data.shrink_to_fit(); + for (name, dep) in &self.deps { + if dep.is_prelude() { + // This is a bit confusing but the gist is that `no_core` and `no_std` remove the + // sysroot dependence on `core` and `std` respectively. Our `CrateGraph` is eagerly + // constructed with them in place no matter what though, since at that point we + // don't do pre-configured attribute resolution yet. + // So here check if we are no_core / no_std and we are trying to add the + // corresponding dep from the sysroot + let skip = match crate_graph[dep.crate_id].origin { + CrateOrigin::Lang(LangCrateOrigin::Core) => { + crate_data.no_core && dep.is_sysroot() + } + CrateOrigin::Lang(LangCrateOrigin::Std) => { + crate_data.no_std && dep.is_sysroot() + } + _ => false, + }; + if skip { + continue; + } + crate_data + .extern_prelude + .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None)); + } + } + self.inject_prelude(); + if !process { + return; + } + ModCollector { def_collector: self, macro_depth: 0, @@ -362,6 +385,7 @@ impl DefCollector<'_> { mod_dir: ModDir::root(), } .collect_in_top_module(item_tree.top_level_items()); + Arc::get_mut(&mut self.def_map.data).unwrap().shrink_to_fit(); } fn seed_with_inner(&mut self, tree_id: TreeId) { @@ -519,15 +543,12 @@ impl DefCollector<'_> { let krate = if self.def_map.data.no_std { name![core] + } else if self.def_map.extern_prelude().any(|(name, _)| *name == name![std]) { + name![std] } else { - let std = name![std]; - if self.def_map.extern_prelude().any(|(name, _)| *name == std) { - std - } else { - // If `std` does not exist for some reason, fall back to core. This mostly helps - // keep r-a's own tests minimal. - name![core] - } + // If `std` does not exist for some reason, fall back to core. This mostly helps + // keep r-a's own tests minimal. + name![core] }; let edition = match self.def_map.data.edition { @@ -1389,31 +1410,6 @@ impl DefCollector<'_> { } let file_id = macro_call_id.as_file(); - // First, fetch the raw expansion result for purposes of error reporting. This goes through - // `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve - // incrementality). - // FIXME: This kind of error fetching feels a bit odd? - let ExpandResult { value: errors, err } = - self.db.parse_macro_expansion_error(macro_call_id); - if let Some(err) = err { - let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id); - let diag = match err { - // why is this reported here? - hir_expand::ExpandError::UnresolvedProcMacro(krate) => { - always!(krate == loc.def.krate); - DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate) - } - _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()), - }; - - self.def_map.diagnostics.push(diag); - } - if !errors.is_empty() { - let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id); - let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, errors); - self.def_map.diagnostics.push(diag); - } - // Then, fetch and process the item tree. This will reuse the expansion result from above. let item_tree = self.db.file_item_tree(file_id); diff --git a/crates/hir-def/src/nameres/diagnostics.rs b/crates/hir-def/src/nameres/diagnostics.rs index 8c7fdaaf58..523a4c107b 100644 --- a/crates/hir-def/src/nameres/diagnostics.rs +++ b/crates/hir-def/src/nameres/diagnostics.rs @@ -6,7 +6,7 @@ use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; use hir_expand::{attrs::AttrId, ErasedAstId, MacroCallKind}; use la_arena::Idx; -use syntax::{ast, SyntaxError}; +use syntax::ast; use crate::{ item_tree::{self, ItemTreeId}, @@ -23,8 +23,6 @@ pub enum DefDiagnosticKind { UnconfiguredCode { ast: ErasedAstId, cfg: CfgExpr, opts: CfgOptions }, UnresolvedProcMacro { ast: MacroCallKind, krate: CrateId }, UnresolvedMacroCall { ast: MacroCallKind, path: ModPath }, - MacroError { ast: MacroCallKind, message: String }, - MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> }, UnimplementedBuiltinMacro { ast: AstId<ast::Macro> }, InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize }, MalformedDerive { ast: AstId<ast::Adt>, id: usize }, @@ -98,7 +96,7 @@ impl DefDiagnostic { // FIXME: This is used for a lot of things, unresolved proc macros, disabled proc macros, etc // yet the diagnostic handler in ide-diagnostics has to figure out what happened because this // struct loses all that information! - pub(crate) fn unresolved_proc_macro( + pub fn unresolved_proc_macro( container: LocalModuleId, ast: MacroCallKind, krate: CrateId, @@ -106,25 +104,6 @@ impl DefDiagnostic { Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } } } - pub(crate) fn macro_error( - container: LocalModuleId, - ast: MacroCallKind, - message: String, - ) -> Self { - Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } } - } - - pub(crate) fn macro_expansion_parse_error( - container: LocalModuleId, - ast: MacroCallKind, - errors: Box<[SyntaxError]>, - ) -> Self { - Self { - in_module: container, - kind: DefDiagnosticKind::MacroExpansionParseError { ast, errors }, - } - } - // FIXME: Whats the difference between this and unresolved_proc_macro pub(crate) fn unresolved_macro_call( container: LocalModuleId, diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs index b3c41a073c..6af5261411 100644 --- a/crates/hir-def/src/path/lower.rs +++ b/crates/hir-def/src/path/lower.rs @@ -208,6 +208,13 @@ pub(super) fn lower_generic_args( .and_then(|args| lower_generic_args(lower_ctx, args)) .map(Interned::new); let type_ref = assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it)); + let type_ref = type_ref.inspect(|tr| { + tr.walk(&mut |tr| { + if let TypeRef::ImplTrait(bounds) = tr { + lower_ctx.update_impl_traits_bounds(bounds.clone()); + } + }); + }); let bounds = if let Some(l) = assoc_type_arg.type_bound_list() { l.bounds() .map(|it| Interned::new(TypeBound::from_ast(lower_ctx, it))) diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index fadab858aa..1602b17385 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -1,5 +1,5 @@ //! Name resolution façade. -use std::{fmt, hash::BuildHasherDefault, mem}; +use std::{fmt, hash::BuildHasherDefault, iter, mem}; use base_db::CrateId; use hir_expand::{ @@ -591,13 +591,13 @@ impl Resolver { pub fn where_predicates_in_scope( &self, - ) -> impl Iterator<Item = &crate::generics::WherePredicate> { + ) -> impl Iterator<Item = (&crate::generics::WherePredicate, &GenericDefId)> { self.scopes() .filter_map(|scope| match scope { - Scope::GenericParams { params, .. } => Some(params), + Scope::GenericParams { params, def } => Some((params, def)), _ => None, }) - .flat_map(|params| params.where_predicates.iter()) + .flat_map(|(params, def)| params.where_predicates.iter().zip(iter::repeat(def))) } pub fn generic_def(&self) -> Option<GenericDefId> { diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index f8bf88d83c..85ec02ae07 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -5,7 +5,7 @@ use base_db::CrateId; use cfg::CfgExpr; use either::Either; use intern::Interned; -use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; +use mbe::{syntax_node_to_token_tree, DelimiterKind, DocCommentDesugarMode, Punct}; use smallvec::{smallvec, SmallVec}; use span::{Span, SyntaxContextId}; use syntax::unescape; @@ -239,7 +239,12 @@ impl Attr { span, }))) } else if let Some(tt) = ast.token_tree() { - let tree = syntax_node_to_token_tree(tt.syntax(), span_map, span); + let tree = syntax_node_to_token_tree( + tt.syntax(), + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); Some(Interned::new(AttrInput::TokenTree(Box::new(tree)))) } else { None @@ -247,8 +252,18 @@ impl Attr { Some(Attr { id, path, input, ctxt: span.ctx }) } - fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree], id: AttrId) -> Option<Attr> { - let ctxt = tt.first()?.first_span().ctx; + fn from_tt(db: &dyn ExpandDatabase, mut tt: &[tt::TokenTree], id: AttrId) -> Option<Attr> { + if matches!(tt, + [tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { text, .. })), ..] + if text == "unsafe" + ) { + match tt.get(1) { + Some(tt::TokenTree::Subtree(subtree)) => tt = &subtree.token_trees, + _ => return None, + } + } + let first = &tt.first()?; + let ctxt = first.first_span().ctx; let path_end = tt .iter() .position(|tt| { @@ -430,7 +445,7 @@ fn inner_attributes( // Input subtree is: `(cfg, $(attr),+)` // Split it up into a `cfg` subtree and the `attr` subtrees. -pub fn parse_cfg_attr_input( +fn parse_cfg_attr_input( subtree: &Subtree, ) -> Option<(&[tt::TokenTree], impl Iterator<Item = &[tt::TokenTree]>)> { let mut parts = subtree diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs index 94681b42a9..c7cdc5e922 100644 --- a/crates/hir-expand/src/builtin_derive_macro.rs +++ b/crates/hir-expand/src/builtin_derive_macro.rs @@ -1,6 +1,7 @@ //! Builtin derives. use itertools::izip; +use mbe::DocCommentDesugarMode; use rustc_hash::FxHashSet; use span::{MacroCallId, Span}; use stdx::never; @@ -262,7 +263,12 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr match this { Some(it) => { param_type_set.insert(it.as_name()); - mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site) + mbe::syntax_node_to_token_tree( + it.syntax(), + tm, + call_site, + DocCommentDesugarMode::ProcMacro, + ) } None => { tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) @@ -270,15 +276,27 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr } }; let bounds = match ¶m { - ast::TypeOrConstParam::Type(it) => it - .type_bound_list() - .map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site)), + ast::TypeOrConstParam::Type(it) => it.type_bound_list().map(|it| { + mbe::syntax_node_to_token_tree( + it.syntax(), + tm, + call_site, + DocCommentDesugarMode::ProcMacro, + ) + }), ast::TypeOrConstParam::Const(_) => None, }; let ty = if let ast::TypeOrConstParam::Const(param) = param { let ty = param .ty() - .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax(), tm, call_site)) + .map(|ty| { + mbe::syntax_node_to_token_tree( + ty.syntax(), + tm, + call_site, + DocCommentDesugarMode::ProcMacro, + ) + }) .unwrap_or_else(|| { tt::Subtree::empty(::tt::DelimSpan { open: call_site, close: call_site }) }); @@ -292,7 +310,14 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr let where_clause = if let Some(w) = where_clause { w.predicates() - .map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site)) + .map(|it| { + mbe::syntax_node_to_token_tree( + it.syntax(), + tm, + call_site, + DocCommentDesugarMode::ProcMacro, + ) + }) .collect() } else { vec![] @@ -322,7 +347,14 @@ fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result<BasicAdtInfo, ExpandEr let name = p.path()?.qualifier()?.as_single_name_ref()?.as_name(); param_type_set.contains(&name).then_some(p) }) - .map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm, call_site)) + .map(|it| { + mbe::syntax_node_to_token_tree( + it.syntax(), + tm, + call_site, + DocCommentDesugarMode::ProcMacro, + ) + }) .collect(); let name_token = name_to_token(tm, name)?; Ok(BasicAdtInfo { name: name_token, shape, param_types, where_clause, associated_types }) diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 4d6fe6db39..ba96ab6cc2 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -441,21 +441,21 @@ fn unquote_str(lit: &tt::Literal) -> Option<(String, Span)> { let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::String::cast(lit)?; - token.value().map(|it| (it.into_owned(), span)) + token.value().ok().map(|it| (it.into_owned(), span)) } fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> { let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::Char::cast(lit)?; - token.value().zip(Some(span)) + token.value().ok().zip(Some(span)) } fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec<u8>, Span)> { let span = lit.span; let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::ByteString::cast(lit)?; - token.value().map(|it| (it.into_owned(), span)) + token.value().ok().map(|it| (it.into_owned(), span)) } fn compile_error_expand( diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index d7233a8923..2e5fa6131a 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -3,7 +3,7 @@ use base_db::{salsa, CrateId, FileId, SourceDatabase}; use either::Either; use limit::Limit; -use mbe::{syntax_node_to_token_tree, MatchedArmIndex}; +use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, MatchedArmIndex}; use rustc_hash::FxHashSet; use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId}; use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T}; @@ -132,7 +132,7 @@ pub trait ExpandDatabase: SourceDatabase { fn parse_macro_expansion_error( &self, macro_call: MacroCallId, - ) -> ExpandResult<Box<[SyntaxError]>>; + ) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>; } /// This expands the given macro call, but with different arguments. This is @@ -156,11 +156,25 @@ pub fn expand_speculative( // Build the subtree and token mapping for the speculative args let (mut tt, undo_info) = match loc.kind { MacroCallKind::FnLike { .. } => ( - mbe::syntax_node_to_token_tree(speculative_args, span_map, span), + mbe::syntax_node_to_token_tree( + speculative_args, + span_map, + span, + if loc.def.is_proc_macro() { + DocCommentDesugarMode::ProcMacro + } else { + DocCommentDesugarMode::Mbe + }, + ), SyntaxFixupUndoInfo::NONE, ), MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => ( - mbe::syntax_node_to_token_tree(speculative_args, span_map, span), + mbe::syntax_node_to_token_tree( + speculative_args, + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ), SyntaxFixupUndoInfo::NONE, ), MacroCallKind::Derive { derive_attr_index: index, .. } @@ -176,7 +190,12 @@ pub fn expand_speculative( let censor_cfg = cfg_process::process_cfg_attrs(db, speculative_args, &loc).unwrap_or_default(); - let mut fixups = fixup::fixup_syntax(span_map, speculative_args, span); + let mut fixups = fixup::fixup_syntax( + span_map, + speculative_args, + span, + DocCommentDesugarMode::ProcMacro, + ); fixups.append.retain(|it, _| match it { syntax::NodeOrToken::Token(_) => true, it => !censor.contains(it) && !censor_cfg.contains(it), @@ -191,6 +210,7 @@ pub fn expand_speculative( fixups.append, fixups.remove, span, + DocCommentDesugarMode::ProcMacro, ), fixups.undo_info, ) @@ -212,7 +232,12 @@ pub fn expand_speculative( }?; match attr.token_tree() { Some(token_tree) => { - let mut tree = syntax_node_to_token_tree(token_tree.syntax(), span_map, span); + let mut tree = syntax_node_to_token_tree( + token_tree.syntax(), + span_map, + span, + DocCommentDesugarMode::ProcMacro, + ); tree.delimiter = tt::Delimiter::invisible_spanned(span); Some(tree) @@ -332,9 +357,14 @@ fn parse_macro_expansion( fn parse_macro_expansion_error( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, -) -> ExpandResult<Box<[SyntaxError]>> { - db.parse_macro_expansion(MacroFileId { macro_call_id }) - .map(|it| it.0.errors().into_boxed_slice()) +) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>> { + let e: ExpandResult<Arc<[SyntaxError]>> = + db.parse_macro_expansion(MacroFileId { macro_call_id }).map(|it| Arc::from(it.0.errors())); + if e.value.is_empty() && e.err.is_none() { + None + } else { + Some(Arc::new(e)) + } } pub(crate) fn parse_with_map( @@ -432,7 +462,16 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { return dummy_tt(kind); } - let mut tt = mbe::syntax_node_to_token_tree(tt.syntax(), map.as_ref(), span); + let mut tt = mbe::syntax_node_to_token_tree( + tt.syntax(), + map.as_ref(), + span, + if loc.def.is_proc_macro() { + DocCommentDesugarMode::ProcMacro + } else { + DocCommentDesugarMode::Mbe + }, + ); if loc.def.is_proc_macro() { // proc macros expect their inputs without parentheses, MBEs expect it with them included tt.delimiter.kind = tt::DelimiterKind::Invisible; @@ -469,7 +508,8 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let (mut tt, undo_info) = { let syntax = item_node.syntax(); let censor_cfg = cfg_process::process_cfg_attrs(db, syntax, &loc).unwrap_or_default(); - let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, span); + let mut fixups = + fixup::fixup_syntax(map.as_ref(), syntax, span, DocCommentDesugarMode::ProcMacro); fixups.append.retain(|it, _| match it { syntax::NodeOrToken::Token(_) => true, it => !censor.contains(it) && !censor_cfg.contains(it), @@ -484,6 +524,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { fixups.append, fixups.remove, span, + DocCommentDesugarMode::ProcMacro, ), fixups.undo_info, ) diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs index 66465ce600..7c3bf995b1 100644 --- a/crates/hir-expand/src/declarative.rs +++ b/crates/hir-expand/src/declarative.rs @@ -2,6 +2,7 @@ use std::sync::OnceLock; use base_db::{CrateId, VersionReq}; +use mbe::DocCommentDesugarMode; use span::{Edition, MacroCallId, Span, SyntaxContextId}; use stdx::TupleExt; use syntax::{ast, AstNode}; @@ -158,6 +159,7 @@ impl DeclarativeMacroExpander { map.span_for_range( macro_rules.macro_rules_token().unwrap().text_range(), ), + DocCommentDesugarMode::Mbe, ); mbe::DeclarativeMacro::parse_macro_rules(&tt, edition, new_meta_vars) @@ -175,6 +177,7 @@ impl DeclarativeMacroExpander { arg.syntax(), map.as_ref(), map.span_for_range(macro_def.macro_token().unwrap().text_range()), + DocCommentDesugarMode::Mbe, ); mbe::DeclarativeMacro::parse_macro2(&tt, edition, new_meta_vars) diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs index 8b147c88c1..64e04bc08f 100644 --- a/crates/hir-expand/src/eager.rs +++ b/crates/hir-expand/src/eager.rs @@ -19,6 +19,7 @@ //! //! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros> use base_db::CrateId; +use mbe::DocCommentDesugarMode; use span::SyntaxContextId; use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent}; use triomphe::Arc; @@ -80,7 +81,12 @@ pub fn expand_eager_macro_input( return ExpandResult { value: None, err }; }; - let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, span); + let mut subtree = mbe::syntax_node_to_token_tree( + &expanded_eager_input, + arg_map, + span, + DocCommentDesugarMode::Mbe, + ); subtree.delimiter.kind = crate::tt::DelimiterKind::Invisible; diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 711acfeb3d..9ec2a83162 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -1,6 +1,7 @@ //! To make attribute macros work reliably when typing, we need to take care to //! fix up syntax errors in the code we're passing to them. +use mbe::DocCommentDesugarMode; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::SmallVec; use span::{ErasedFileAstId, Span, SpanAnchor, FIXUP_ERASED_FILE_AST_ID_MARKER}; @@ -49,6 +50,7 @@ pub(crate) fn fixup_syntax( span_map: SpanMapRef<'_>, node: &SyntaxNode, call_site: Span, + mode: DocCommentDesugarMode, ) -> SyntaxFixups { let mut append = FxHashMap::<SyntaxElement, _>::default(); let mut remove = FxHashSet::<SyntaxElement>::default(); @@ -70,7 +72,7 @@ pub(crate) fn fixup_syntax( if can_handle_error(&node) && has_error_to_handle(&node) { remove.insert(node.clone().into()); // the node contains an error node, we have to completely replace it by something valid - let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site); + let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site, mode); let idx = original.len() as u32; original.push(original_tree); let span = span_map.span_for_range(node_range); @@ -360,6 +362,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { mod tests { use base_db::FileId; use expect_test::{expect, Expect}; + use mbe::DocCommentDesugarMode; use syntax::TextRange; use triomphe::Arc; @@ -402,6 +405,7 @@ mod tests { span_map.as_ref(), &parsed.syntax_node(), span_map.span_for_range(TextRange::empty(0.into())), + DocCommentDesugarMode::Mbe, ); let mut tt = mbe::syntax_node_to_token_tree_modified( &parsed.syntax_node(), @@ -409,6 +413,7 @@ mod tests { fixups.append, fixups.remove, span_map.span_for_range(TextRange::empty(0.into())), + DocCommentDesugarMode::Mbe, ); let actual = format!("{tt}\n"); @@ -436,6 +441,7 @@ mod tests { &parsed.syntax_node(), span_map.as_ref(), span_map.span_for_range(TextRange::empty(0.into())), + DocCommentDesugarMode::Mbe, ); assert!( check_subtree_eq(&tt, &original_as_tt), diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 338bd25ede..4ab989bec2 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -132,13 +132,13 @@ pub enum ExpandError { MacroDefinition, Mbe(mbe::ExpandError), RecursionOverflow, - Other(Box<Box<str>>), - ProcMacroPanic(Box<Box<str>>), + Other(Arc<Box<str>>), + ProcMacroPanic(Arc<Box<str>>), } impl ExpandError { pub fn other(msg: impl Into<Box<str>>) -> Self { - ExpandError::Other(Box::new(msg.into())) + ExpandError::Other(Arc::new(msg.into())) } } diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs index abed16fecd..def2578b0e 100644 --- a/crates/hir-expand/src/proc_macro.rs +++ b/crates/hir-expand/src/proc_macro.rs @@ -8,6 +8,7 @@ use rustc_hash::FxHashMap; use span::Span; use stdx::never; use syntax::SmolStr; +use triomphe::Arc; use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult}; @@ -157,7 +158,7 @@ impl CustomProcMacroExpander { ProcMacroExpansionError::System(text) | ProcMacroExpansionError::Panic(text) => ExpandResult::new( tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), - ExpandError::ProcMacroPanic(Box::new(text.into_boxed_str())), + ExpandError::ProcMacroPanic(Arc::new(text.into_boxed_str())), ), }, } diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index 46612242b0..84ac8740ec 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -806,7 +806,7 @@ pub(crate) fn impl_datum_query( krate: CrateId, impl_id: ImplId, ) -> Arc<ImplDatum> { - let _p = tracing::span!(tracing::Level::INFO, "impl_datum").entered(); + let _p = tracing::span!(tracing::Level::INFO, "impl_datum_query").entered(); debug!("impl_datum {:?}", impl_id); let impl_: hir_def::ImplId = from_chalk(db, impl_id); impl_def_datum(db, krate, impl_id, impl_) diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 0bf01b0bc6..d99ef6679e 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -27,6 +27,7 @@ pub trait TyExt { fn is_scalar(&self) -> bool; fn is_floating_point(&self) -> bool; fn is_never(&self) -> bool; + fn is_str(&self) -> bool; fn is_unknown(&self) -> bool; fn contains_unknown(&self) -> bool; fn is_ty_var(&self) -> bool; @@ -87,6 +88,10 @@ impl TyExt for Ty { matches!(self.kind(Interner), TyKind::Never) } + fn is_str(&self) -> bool { + matches!(self.kind(Interner), TyKind::Str) + } + fn is_unknown(&self) -> bool { matches!(self.kind(Interner), TyKind::Error) } diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index a357e85035..c010f5d22b 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -797,8 +797,20 @@ impl HirDisplay for Ty { c.hir_fmt(f)?; write!(f, "]")?; } - TyKind::Raw(m, t) | TyKind::Ref(m, _, t) => { - if matches!(self.kind(Interner), TyKind::Raw(..)) { + kind @ (TyKind::Raw(m, t) | TyKind::Ref(m, _, t)) => { + if let TyKind::Ref(_, l, _) = kind { + f.write_char('&')?; + if cfg!(test) { + // rendering these unconditionally is probably too much (at least for inlay + // hints) so we gate it to testing only for the time being + l.hir_fmt(f)?; + f.write_char(' ')?; + } + match m { + Mutability::Not => (), + Mutability::Mut => f.write_str("mut ")?, + } + } else { write!( f, "*{}", @@ -807,15 +819,6 @@ impl HirDisplay for Ty { Mutability::Mut => "mut ", } )?; - } else { - write!( - f, - "&{}", - match m { - Mutability::Not => "", - Mutability::Mut => "mut ", - } - )?; } // FIXME: all this just to decide whether to use parentheses... @@ -1330,7 +1333,18 @@ fn hir_fmt_generics( } let parameters_to_write = generic_args_sans_defaults(f, generic_def, parameters); - if !parameters_to_write.is_empty() { + + // FIXME: Remote this + // most of our lifetimes will be errors as we lack elision and inference + // so don't render them for now + let only_err_lifetimes = !cfg!(test) + && parameters_to_write.iter().all(|arg| { + matches!( + arg.data(Interner), + chalk_ir::GenericArgData::Lifetime(it) if *it.data(Interner) == LifetimeData::Error + ) + }); + if !parameters_to_write.is_empty() && !only_err_lifetimes { write!(f, "<")?; hir_fmt_generic_arguments(f, parameters_to_write)?; write!(f, ">")?; @@ -1403,6 +1417,18 @@ fn hir_fmt_generic_arguments( None => (parameters, &[][..]), }; for generic_arg in lifetimes.iter().chain(ty_or_const) { + // FIXME: Remove this + // most of our lifetimes will be errors as we lack elision and inference + // so don't render them for now + if !cfg!(test) + && matches!( + generic_arg.lifetime(Interner), + Some(l) if ***l.interned() == LifetimeData::Error + ) + { + continue; + } + if !first { write!(f, ", ")?; } @@ -1728,9 +1754,9 @@ impl HirDisplay for LifetimeData { LifetimeData::BoundVar(idx) => idx.hir_fmt(f), LifetimeData::InferenceVar(_) => write!(f, "_"), LifetimeData::Static => write!(f, "'static"), - LifetimeData::Error => write!(f, "'{{error}}"), - LifetimeData::Erased => Ok(()), - LifetimeData::Phantom(_, _) => Ok(()), + LifetimeData::Error => write!(f, "'?"), + LifetimeData::Erased => write!(f, "'<erased>"), + LifetimeData::Phantom(void, _) => match *void {}, } } } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 281386e136..6f2f70dd40 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -198,6 +198,7 @@ pub enum InferenceDiagnostic { NoSuchField { field: ExprOrPatId, private: bool, + variant: VariantId, }, PrivateField { expr: ExprId, diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index d011a62e77..38076fce8f 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -563,6 +563,7 @@ impl InferenceContext<'_> { InferenceDiagnostic::NoSuchField { field: field.expr.into(), private: true, + variant: def, }, ); } @@ -572,6 +573,7 @@ impl InferenceContext<'_> { self.push_diagnostic(InferenceDiagnostic::NoSuchField { field: field.expr.into(), private: false, + variant: def, }); None } diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 1b354935a5..dac5a5ea69 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -177,6 +177,7 @@ impl InferenceContext<'_> { self.push_diagnostic(InferenceDiagnostic::NoSuchField { field: inner.into(), private: true, + variant: def, }); } let f = field_types[local_id].clone(); @@ -190,6 +191,7 @@ impl InferenceContext<'_> { self.push_diagnostic(InferenceDiagnostic::NoSuchField { field: inner.into(), private: false, + variant: def, }); self.err_ty() } diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 4d0516ead6..04ace38202 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -345,51 +345,47 @@ impl<'a> TyLoweringContext<'a> { } ImplTraitLoweringState::Param(counter) => { let idx = counter.get(); - // FIXME we're probably doing something wrong here + // Count the number of `impl Trait` things that appear within our bounds. + // Since t hose have been emitted as implicit type args already. counter.set(idx + count_impl_traits(type_ref) as u16); - if let Some(generics) = self.generics() { - let param = generics - .iter() - .filter(|(_, data)| { - matches!( - data, - GenericParamDataRef::TypeParamData(data) - if data.provenance == TypeParamProvenance::ArgumentImplTrait - ) - }) - .nth(idx as usize) - .map_or(TyKind::Error, |(id, _)| { - if let GenericParamId::TypeParamId(id) = id { - TyKind::Placeholder(to_placeholder_idx(self.db, id.into())) - } else { - // we just filtered them out - unreachable!("Unexpected lifetime or const argument"); - } - }); - param.intern(Interner) - } else { - TyKind::Error.intern(Interner) - } + let kind = self + .generics() + .expect("param impl trait lowering must be in a generic def") + .iter() + .filter_map(|(id, data)| match (id, data) { + ( + GenericParamId::TypeParamId(id), + GenericParamDataRef::TypeParamData(data), + ) if data.provenance == TypeParamProvenance::ArgumentImplTrait => { + Some(id) + } + _ => None, + }) + .nth(idx as usize) + .map_or(TyKind::Error, |id| { + TyKind::Placeholder(to_placeholder_idx(self.db, id.into())) + }); + kind.intern(Interner) } ImplTraitLoweringState::Variable(counter) => { let idx = counter.get(); - // FIXME we're probably doing something wrong here + // Count the number of `impl Trait` things that appear within our bounds. + // Since t hose have been emitted as implicit type args already. counter.set(idx + count_impl_traits(type_ref) as u16); let ( _parent_params, self_params, - list_params, + type_params, const_params, _impl_trait_params, _lifetime_params, - ) = if let Some(generics) = self.generics() { - generics.provenance_split() - } else { - (0, 0, 0, 0, 0, 0) - }; + ) = self + .generics() + .expect("variable impl trait lowering must be in a generic def") + .provenance_split(); TyKind::BoundVar(BoundVar::new( self.in_binders, - idx as usize + self_params + list_params + const_params, + idx as usize + self_params + type_params + const_params, )) .intern(Interner) } @@ -1010,6 +1006,7 @@ impl<'a> TyLoweringContext<'a> { pub(crate) fn lower_where_predicate<'b>( &'b self, where_predicate: &'b WherePredicate, + &def: &GenericDefId, ignore_bindings: bool, ) -> impl Iterator<Item = QuantifiedWhereClause> + 'b { match where_predicate { @@ -1018,7 +1015,6 @@ impl<'a> TyLoweringContext<'a> { let self_ty = match target { WherePredicateTypeTarget::TypeRef(type_ref) => self.lower_ty(type_ref), &WherePredicateTypeTarget::TypeOrConstParam(local_id) => { - let def = self.resolver.generic_def().expect("generics in scope"); let param_id = hir_def::TypeOrConstParamId { parent: def, local_id }; match self.type_param_mode { ParamLoweringMode::Placeholder => { @@ -1056,23 +1052,7 @@ impl<'a> TyLoweringContext<'a> { let clause = match bound.as_ref() { TypeBound::Path(path, TraitBoundModifier::None) => { trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty)); - trait_ref - .clone() - .filter(|tr| { - // ignore `T: Drop` or `T: Destruct` bounds. - // - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement. - // (So ideally, we'd only ignore `~const Drop` here) - // - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until - // the builtin impls are supported by Chalk, we ignore them here. - if let Some(lang) = self.db.lang_attr(tr.hir_trait_id().into()) { - if matches!(lang, LangItem::Drop | LangItem::Destruct) { - return false; - } - } - true - }) - .map(WhereClause::Implemented) - .map(crate::wrap_empty_binders) + trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) } TypeBound::Path(path, TraitBoundModifier::Maybe) => { let sized_trait = self @@ -1166,84 +1146,77 @@ impl<'a> TyLoweringContext<'a> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = &binding.type_ref { - if let ( - TypeRef::ImplTrait(bounds), - ImplTraitLoweringState::Param(_) - | ImplTraitLoweringState::Variable(_) - | ImplTraitLoweringState::Disallowed, - ) = (type_ref, &self.impl_trait_mode) - { - for bound in bounds { - predicates.extend( - self.lower_type_bound( - bound, - TyKind::Alias(AliasTy::Projection(projection_ty.clone())) - .intern(Interner), - false, - ), - ); + match (type_ref, &self.impl_trait_mode) { + (TypeRef::ImplTrait(_), ImplTraitLoweringState::Disallowed) => (), + ( + _, + ImplTraitLoweringState::Disallowed | ImplTraitLoweringState::Opaque(_), + ) => { + let ty = self.lower_ty(type_ref); + let alias_eq = + AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; + predicates + .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); } - } else { - let ty = 'ty: { - if matches!( - self.impl_trait_mode, - ImplTraitLoweringState::Param(_) - | ImplTraitLoweringState::Variable(_) - ) { - // Find the generic index for the target of our `bound` - let target_param_idx = self - .resolver - .where_predicates_in_scope() - .find_map(|p| match p { - WherePredicate::TypeBound { - target: WherePredicateTypeTarget::TypeOrConstParam(idx), - bound: b, - } if b == bound => Some(idx), - _ => None, - }); - if let Some(target_param_idx) = target_param_idx { - let mut counter = 0; - let generics = self.generics().expect("generics in scope"); - for (idx, data) in generics.params.type_or_consts.iter() { - // Count the number of `impl Trait` things that appear before - // the target of our `bound`. - // Our counter within `impl_trait_mode` should be that number - // to properly lower each types within `type_ref` - if data.type_param().is_some_and(|p| { - p.provenance == TypeParamProvenance::ArgumentImplTrait - }) { - counter += 1; - } - if idx == *target_param_idx { - break; - } + ( + _, + ImplTraitLoweringState::Param(_) | ImplTraitLoweringState::Variable(_), + ) => { + // Find the generic index for the target of our `bound` + let target_param_idx = self + .resolver + .where_predicates_in_scope() + .find_map(|(p, _)| match p { + WherePredicate::TypeBound { + target: WherePredicateTypeTarget::TypeOrConstParam(idx), + bound: b, + } if b == bound => Some(idx), + _ => None, + }); + let ty = if let Some(target_param_idx) = target_param_idx { + let mut counter = 0; + let generics = self.generics().expect("generics in scope"); + for (idx, data) in generics.params.type_or_consts.iter() { + // Count the number of `impl Trait` things that appear before + // the target of our `bound`. + // Our counter within `impl_trait_mode` should be that number + // to properly lower each types within `type_ref` + if data.type_param().is_some_and(|p| { + p.provenance == TypeParamProvenance::ArgumentImplTrait + }) { + counter += 1; } - let mut ext = TyLoweringContext::new_maybe_unowned( - self.db, - self.resolver, - self.owner, - ) - .with_type_param_mode(self.type_param_mode); - match &self.impl_trait_mode { - ImplTraitLoweringState::Param(_) => { - ext.impl_trait_mode = - ImplTraitLoweringState::Param(Cell::new(counter)); - } - ImplTraitLoweringState::Variable(_) => { - ext.impl_trait_mode = ImplTraitLoweringState::Variable( - Cell::new(counter), - ); - } - _ => unreachable!(), + if idx == *target_param_idx { + break; } - break 'ty ext.lower_ty(type_ref); } - } - self.lower_ty(type_ref) - }; - let alias_eq = - AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; - predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + let mut ext = TyLoweringContext::new_maybe_unowned( + self.db, + self.resolver, + self.owner, + ) + .with_type_param_mode(self.type_param_mode); + match &self.impl_trait_mode { + ImplTraitLoweringState::Param(_) => { + ext.impl_trait_mode = + ImplTraitLoweringState::Param(Cell::new(counter)); + } + ImplTraitLoweringState::Variable(_) => { + ext.impl_trait_mode = + ImplTraitLoweringState::Variable(Cell::new(counter)); + } + _ => unreachable!(), + } + ext.lower_ty(type_ref) + } else { + self.lower_ty(type_ref) + }; + + let alias_eq = + AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; + predicates + .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + } } } for bound in binding.bounds.iter() { @@ -1338,11 +1311,10 @@ impl<'a> TyLoweringContext<'a> { bounds, lifetime: match lifetime { Some(it) => match it.bound_var(Interner) { - Some(bound_var) => LifetimeData::BoundVar(BoundVar::new( - DebruijnIndex::INNERMOST, - bound_var.index, - )) - .intern(Interner), + Some(bound_var) => bound_var + .shifted_out_to(DebruijnIndex::new(2)) + .map(|bound_var| LifetimeData::BoundVar(bound_var).intern(Interner)) + .unwrap_or(it), None => it, }, None => static_lifetime(), @@ -1410,16 +1382,6 @@ impl<'a> TyLoweringContext<'a> { } } -fn count_impl_traits(type_ref: &TypeRef) -> usize { - let mut count = 0; - type_ref.walk(&mut |type_ref| { - if matches!(type_ref, TypeRef::ImplTrait(_)) { - count += 1; - } - }); - count -} - /// Build the signature of a callable item (function, struct or enum variant). pub(crate) fn callable_item_sig(db: &dyn HirDatabase, def: CallableDefId) -> PolyFnSig { match def { @@ -1438,6 +1400,17 @@ pub fn associated_type_shorthand_candidates<R>( named_associated_type_shorthand_candidates(db, def, res, None, |name, _, id| cb(name, id)) } +// FIXME: This does not handle macros! +fn count_impl_traits(type_ref: &TypeRef) -> usize { + let mut count = 0; + type_ref.walk(&mut |type_ref| { + if matches!(type_ref, TypeRef::ImplTrait(_)) { + count += 1; + } + }); + count +} + fn named_associated_type_shorthand_candidates<R>( db: &dyn HirDatabase, // If the type parameter is defined in an impl and we're in a method, there @@ -1575,7 +1548,7 @@ pub(crate) fn generic_predicates_for_param_query( let generics = generics(db.upcast(), def); // we have to filter out all other predicates *first*, before attempting to lower them - let predicate = |pred: &&_| match pred { + let predicate = |(pred, &def): &(&_, _)| match pred { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound, .. } => { let invalid_target = match target { @@ -1617,8 +1590,8 @@ pub(crate) fn generic_predicates_for_param_query( let mut predicates: Vec<_> = resolver .where_predicates_in_scope() .filter(predicate) - .flat_map(|pred| { - ctx.lower_where_predicate(pred, true).map(|p| make_binders(db, &generics, p)) + .flat_map(|(pred, def)| { + ctx.lower_where_predicate(pred, def, true).map(|p| make_binders(db, &generics, p)) }) .collect(); @@ -1671,8 +1644,8 @@ pub(crate) fn trait_environment_query( }; let mut traits_in_scope = Vec::new(); let mut clauses = Vec::new(); - for pred in resolver.where_predicates_in_scope() { - for pred in ctx.lower_where_predicate(pred, false) { + for (pred, def) in resolver.where_predicates_in_scope() { + for pred in ctx.lower_where_predicate(pred, def, false) { if let WhereClause::Implemented(tr) = &pred.skip_binders() { traits_in_scope.push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id())); } @@ -1726,8 +1699,8 @@ pub(crate) fn generic_predicates_query( let mut predicates = resolver .where_predicates_in_scope() - .flat_map(|pred| { - ctx.lower_where_predicate(pred, false).map(|p| make_binders(db, &generics, p)) + .flat_map(|(pred, def)| { + ctx.lower_where_predicate(pred, def, false).map(|p| make_binders(db, &generics, p)) }) .collect::<Vec<_>>(); diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index cd72349471..cb56a6f0bf 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -368,7 +368,7 @@ pub(crate) fn incoherent_inherent_impl_crates( krate: CrateId, fp: TyFingerprint, ) -> SmallVec<[CrateId; 2]> { - let _p = tracing::span!(tracing::Level::INFO, "inherent_impl_crates_query").entered(); + let _p = tracing::span!(tracing::Level::INFO, "incoherent_inherent_impl_crates").entered(); let mut res = SmallVec::new(); let crate_graph = db.crate_graph(); diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 045ffb418c..2de1aa30c9 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -428,6 +428,17 @@ impl MirEvalError { } Ok(()) } + + pub fn is_panic(&self) -> Option<&str> { + let mut err = self; + while let MirEvalError::InFunction(e, _) = err { + err = e; + } + match err { + MirEvalError::Panic(msg) => Some(msg), + _ => None, + } + } } impl std::fmt::Debug for MirEvalError { @@ -1138,7 +1149,7 @@ impl Evaluator<'_> { let mut ty = self.operand_ty(lhs, locals)?; while let TyKind::Ref(_, _, z) = ty.kind(Interner) { ty = z.clone(); - let size = if ty.kind(Interner) == &TyKind::Str { + let size = if ty.is_str() { if *op != BinOp::Eq { never!("Only eq is builtin for `str`"); } diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index fee3dd3ada..3438712049 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -49,6 +49,7 @@ impl Evaluator<'_> { if self.not_special_fn_cache.borrow().contains(&def) { return Ok(false); } + let function_data = self.db.function_data(def); let is_intrinsic = match &function_data.abi { Some(abi) => *abi == Interned::new_str("rust-intrinsic"), @@ -131,9 +132,7 @@ impl Evaluator<'_> { return Ok(true); } if let Some(it) = self.detect_lang_function(def) { - let arg_bytes = - args.iter().map(|it| Ok(it.get(self)?.to_owned())).collect::<Result<Vec<_>>>()?; - let result = self.exec_lang_item(it, generic_args, &arg_bytes, locals, span)?; + let result = self.exec_lang_item(it, generic_args, args, locals, span)?; destination.write_from_bytes(self, &result)?; return Ok(true); } @@ -311,16 +310,20 @@ impl Evaluator<'_> { fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> { use LangItem::*; - let candidate = self.db.lang_attr(def.into())?; + let attrs = self.db.attrs(def.into()); + + if attrs.by_key("rustc_const_panic_str").exists() { + // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE. + return Some(LangItem::BeginPanic); + } + + let candidate = attrs.by_key("lang").string_value().and_then(LangItem::from_str)?; // We want to execute these functions with special logic // `PanicFmt` is not detected here as it's redirected later. if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) { return Some(candidate); } - if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() { - // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE. - return Some(LangItem::BeginPanic); - } + None } @@ -328,18 +331,52 @@ impl Evaluator<'_> { &mut self, it: LangItem, generic_args: &Substitution, - args: &[Vec<u8>], + args: &[IntervalAndTy], locals: &Locals, span: MirSpan, ) -> Result<Vec<u8>> { use LangItem::*; let mut args = args.iter(); match it { - BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())), + BeginPanic => { + let mut arg = args + .next() + .ok_or(MirEvalError::InternalError( + "argument of BeginPanic is not provided".into(), + ))? + .clone(); + while let TyKind::Ref(_, _, ty) = arg.ty.kind(Interner) { + if ty.is_str() { + let (pointee, metadata) = arg.interval.get(self)?.split_at(self.ptr_size()); + let len = from_bytes!(usize, metadata); + + return { + Err(MirEvalError::Panic( + std::str::from_utf8( + self.read_memory(Address::from_bytes(pointee)?, len)?, + ) + .unwrap() + .to_owned(), + )) + }; + } + let size = self.size_of_sized(ty, locals, "begin panic arg")?; + let pointee = arg.interval.get(self)?; + arg = IntervalAndTy { + interval: Interval::new(Address::from_bytes(pointee)?, size), + ty: ty.clone(), + }; + } + Err(MirEvalError::Panic(format!( + "unknown-panic-payload: {:?}", + arg.ty.kind(Interner) + ))) + } SliceLen => { let arg = args.next().ok_or(MirEvalError::InternalError( "argument of <[T]>::len() is not provided".into(), ))?; + let arg = arg.get(self)?; let ptr_size = arg.len() / 2; Ok(arg[ptr_size..].into()) } @@ -353,6 +390,7 @@ impl Evaluator<'_> { let arg = args.next().ok_or(MirEvalError::InternalError( "argument of drop_in_place is not provided".into(), ))?; + let arg = arg.interval.get(self)?.to_owned(); self.run_drop_glue_deep( ty.clone(), locals, diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 381522c9ab..4abbda56cb 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -31,6 +31,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr db.trait_environment(func_id.into()), ) .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; + let (result, output) = interpret_mir(db, body, false, None); result?; Ok((output.stdout().into_owned(), output.stderr().into_owned())) @@ -72,6 +73,13 @@ fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr } } +fn check_panic(ra_fixture: &str, expected_panic: &str) { + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + let file_id = *file_ids.last().unwrap(); + let e = eval_main(&db, file_id).unwrap_err(); + assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {:?}", e)), expected_panic); +} + #[test] fn function_with_extern_c_abi() { check_pass( @@ -88,6 +96,43 @@ fn main() { } #[test] +fn panic_fmt() { + // panic! + // -> panic_2021 (builtin macro redirection) + // -> #[lang = "panic_fmt"] core::panicking::panic_fmt (hooked by CTFE for redirection) + // -> core::panicking::const_panic_fmt + // -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic) + // -> Err(ConstEvalError::Panic) + check_panic( + r#" +//- minicore: fmt, panic +fn main() { + panic!("hello, world!"); +} + "#, + "hello, world!", + ); +} + +#[test] +fn panic_display() { + // panic! + // -> panic_2021 (builtin macro redirection) + // -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic) + // -> Err(ConstEvalError::Panic) + check_panic( + r#" +//- minicore: fmt, panic + +fn main() { + panic!("{}", "hello, world!"); +} + "#, + "hello, world!", + ); +} + +#[test] fn drop_basic() { check_pass( r#" diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index d56b15b9b7..526db2af6d 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs @@ -186,7 +186,7 @@ fn test() { let x = match 1 { 1 => t as *mut i32, 2 => t as &i32, - //^^^^^^^^^ expected *mut i32, got &i32 + //^^^^^^^^^ expected *mut i32, got &'? i32 _ => t as *const i32, // ^^^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer) @@ -307,7 +307,7 @@ fn test() { let foo = Foo; //^^^ type: Foo<{unknown}> let _: &u32 = &Foo; - //^^^^ expected &u32, got &Foo<{unknown}> + //^^^^ expected &'? u32, got &'? Foo<{unknown}> }", ); } @@ -544,9 +544,9 @@ struct Bar<T>(Foo<T>); fn test() { let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; - //^^^^^^^^^^^^^^^^^^^^^ expected &Foo<[usize]>, got &Foo<[i32; 3]> + //^^^^^^^^^^^^^^^^^^^^^ expected &'? Foo<[usize]>, got &'? Foo<[i32; 3]> let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); - //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &Bar<[usize]>, got &Bar<[i32; 3]> + //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &'? Bar<[usize]>, got &'? Bar<[i32; 3]> } "#, ); @@ -562,7 +562,7 @@ trait Foo {} fn test(f: impl Foo, g: &(impl Foo + ?Sized)) { let _: &dyn Foo = &f; let _: &dyn Foo = g; - //^ expected &dyn Foo, got &impl Foo + ?Sized + //^ expected &'? dyn Foo, got &'? impl Foo + ?Sized } "#, ); @@ -828,11 +828,11 @@ struct V<T> { t: T } fn main() { let a: V<&dyn Tr>; (a,) = V { t: &S }; - //^^^^expected V<&S>, got (V<&dyn Tr>,) + //^^^^expected V<&'? S>, got (V<&'? dyn Tr>,) let mut a: V<&dyn Tr> = V { t: &S }; (a,) = V { t: &S }; - //^^^^expected V<&S>, got (V<&dyn Tr>,) + //^^^^expected V<&'? S>, got (V<&'? dyn Tr>,) } "#, ); diff --git a/crates/hir-ty/src/tests/diagnostics.rs b/crates/hir-ty/src/tests/diagnostics.rs index 119de7f050..def06f2d59 100644 --- a/crates/hir-ty/src/tests/diagnostics.rs +++ b/crates/hir-ty/src/tests/diagnostics.rs @@ -8,7 +8,7 @@ fn function_return_type_mismatch_1() { r#" fn test() -> &'static str { 5 - //^ expected &str, got i32 + //^ expected &'static str, got i32 } "#, ); @@ -21,7 +21,7 @@ fn function_return_type_mismatch_2() { fn test(x: bool) -> &'static str { if x { return 1; - //^ expected &str, got i32 + //^ expected &'static str, got i32 } "ok" } @@ -38,7 +38,7 @@ fn test(x: bool) -> &'static str { return "ok"; } 1 - //^ expected &str, got i32 + //^ expected &'static str, got i32 } "#, ); @@ -53,7 +53,7 @@ fn test(x: bool) -> &'static str { "ok" } else { 1 - //^ expected &str, got i32 + //^ expected &'static str, got i32 } } "#, @@ -67,7 +67,7 @@ fn function_return_type_mismatch_5() { fn test(x: bool) -> &'static str { if x { 1 - //^ expected &str, got i32 + //^ expected &'static str, got i32 } else { "ok" } @@ -83,10 +83,10 @@ fn non_unit_block_expr_stmt_no_semi() { fn test(x: bool) { if x { "notok" - //^^^^^^^ expected (), got &str + //^^^^^^^ expected (), got &'static str } else { "ok" - //^^^^ expected (), got &str + //^^^^ expected (), got &'static str } match x { true => true, false => 0 } //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), got bool diff --git a/crates/hir-ty/src/tests/display_source_code.rs b/crates/hir-ty/src/tests/display_source_code.rs index e8369caa77..60c03b5224 100644 --- a/crates/hir-ty/src/tests/display_source_code.rs +++ b/crates/hir-ty/src/tests/display_source_code.rs @@ -67,11 +67,11 @@ trait B: A {} fn test( _: &(dyn A<Assoc = ()> + Send), - //^ &(dyn A<Assoc = ()> + Send) + //^ &'_ (dyn A<Assoc = ()> + Send) _: &(dyn Send + A<Assoc = ()>), - //^ &(dyn A<Assoc = ()> + Send) + //^ &'_ (dyn A<Assoc = ()> + Send) _: &dyn B<Assoc = ()>, - //^ &(dyn B<Assoc = ()>) + //^ &'_ (dyn B<Assoc = ()>) ) {} "#, ); @@ -85,7 +85,7 @@ fn render_dyn_for_ty() { trait Foo<'a> {} fn foo(foo: &dyn for<'a> Foo<'a>) {} - // ^^^ &dyn Foo<'_> + // ^^^ &'_ dyn Foo<'_> "#, ); } @@ -111,11 +111,11 @@ fn test( b; //^ impl Foo c; - //^ &impl Foo + ?Sized + //^ &'_ impl Foo + ?Sized d; //^ S<impl Foo> ref_any; - //^^^^^^^ &impl ?Sized + //^^^^^^^ &'_ impl ?Sized empty; } //^^^^^ impl Sized "#, @@ -192,7 +192,7 @@ fn test( b; //^ fn(impl Foo) -> impl Foo c; -} //^ fn(&impl Foo + ?Sized) -> &impl Foo + ?Sized +} //^ fn(&'_ impl Foo + ?Sized) -> &'_ impl Foo + ?Sized "#, ); } diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs index 2f75338f99..a0899cb1d6 100644 --- a/crates/hir-ty/src/tests/macros.rs +++ b/crates/hir-ty/src/tests/macros.rs @@ -200,8 +200,8 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize> 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 _ ...!() {}': &'? 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 _ ...!() {}': () 100..119 'for _ ...!() {}': () @@ -221,7 +221,7 @@ fn expr_macro_def_expanded_in_various_places() { 281..303 'Spam {...m!() }': {unknown} 309..325 'spam!(...am!()]': {unknown} 350..366 'spam!(... usize': usize - 372..380 '&spam!()': &isize + 372..380 '&spam!()': &'? isize 386..394 '-spam!()': isize 400..416 'spam!(...pam!()': {unknown} 422..439 'spam!(...pam!()': isize @@ -293,8 +293,8 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize> 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 _ ...!() {}': &'? 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 _ ...!() {}': () 114..133 'for _ ...!() {}': () @@ -314,7 +314,7 @@ fn expr_macro_rules_expanded_in_various_places() { 295..317 'Spam {...m!() }': {unknown} 323..339 'spam!(...am!()]': {unknown} 364..380 'spam!(... usize': usize - 386..394 '&spam!()': &isize + 386..394 '&spam!()': &'? isize 400..408 '-spam!()': isize 414..430 'spam!(...pam!()': {unknown} 436..453 'spam!(...pam!()': isize @@ -539,7 +539,7 @@ fn test() { let msg = foo::Message(foo::MessageRef); let r = msg.deref(); r; - //^ &MessageRef + //^ &'? MessageRef } //- /lib.rs crate:foo @@ -703,9 +703,9 @@ fn infer_builtin_macros_file() { } "#, expect![[r#" - !0..2 '""': &str + !0..2 '""': &'static str 63..87 '{ ...!(); }': () - 73..74 'x': &str + 73..74 'x': &'static str "#]], ); } @@ -741,9 +741,9 @@ fn infer_builtin_macros_concat() { } "#, expect![[r#" - !0..13 '"helloworld!"': &str + !0..13 '"helloworld!"': &'static str 65..121 '{ ...")); }': () - 75..76 'x': &str + 75..76 'x': &'static str "#]], ); } @@ -820,7 +820,7 @@ macro_rules! include_str {() => {}} fn main() { let a = include_str!("foo.rs"); a; -} //^ &str +} //^ &'static str //- /foo.rs hello @@ -847,7 +847,7 @@ macro_rules! m { fn main() { let a = include_str!(m!(".rs")); a; -} //^ &str +} //^ &'static str //- /foo.rs hello @@ -960,9 +960,9 @@ fn infer_builtin_macros_concat_with_lazy() { } "#, expect![[r#" - !0..13 '"helloworld!"': &str + !0..13 '"helloworld!"': &'static str 103..160 '{ ...")); }': () - 113..114 'x': &str + 113..114 'x': &'static str "#]], ); } @@ -977,7 +977,7 @@ fn infer_builtin_macros_env() { fn main() { let x = env!("foo"); - //^ &str + //^ &'static str } "#, ); @@ -991,7 +991,7 @@ fn infer_builtin_macros_option_env() { //- /main.rs env:foo=bar fn main() { let x = option_env!("foo"); - //^ Option<&str> + //^ Option<&'static str> } "#, ); diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index 8609ba4103..63a83d403f 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs @@ -658,7 +658,7 @@ fn infer_call_trait_method_on_generic_param_1() { } "#, expect![[r#" - 29..33 'self': &Self + 29..33 'self': &'? Self 63..64 't': T 69..88 '{ ...d(); }': () 75..76 't': T @@ -679,7 +679,7 @@ fn infer_call_trait_method_on_generic_param_2() { } "#, expect![[r#" - 32..36 'self': &Self + 32..36 'self': &'? Self 70..71 't': T 76..95 '{ ...d(); }': () 82..83 't': T @@ -757,7 +757,7 @@ struct S; impl Clone for S {} impl Clone for &S {} fn test() { (S.clone(), (&S).clone(), (&&S).clone()); } - //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (S, S, &S) + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (S, S, &'? S) "#, ); } @@ -1150,12 +1150,12 @@ fn dyn_trait_super_trait_not_in_scope() { } "#, expect![[r#" - 51..55 'self': &Self + 51..55 'self': &'? Self 64..69 '{ 0 }': u32 66..67 '0': u32 - 176..177 'd': &dyn Trait + 176..177 'd': &'? dyn Trait 191..207 '{ ...o(); }': () - 197..198 'd': &dyn Trait + 197..198 'd': &'? dyn Trait 197..204 'd.foo()': u32 "#]], ); @@ -1182,15 +1182,15 @@ fn test() { } "#, expect![[r#" - 75..79 'self': &S + 75..79 'self': &'? S 89..109 '{ ... }': bool 99..103 'true': bool 123..167 '{ ...o(); }': () - 133..134 's': &S - 137..151 'unsafe { f() }': &S - 146..147 'f': fn f() -> &S - 146..149 'f()': &S - 157..158 's': &S + 133..134 's': &'? S + 137..151 'unsafe { f() }': &'static S + 146..147 'f': fn f() -> &'static S + 146..149 'f()': &'static S + 157..158 's': &'? S 157..164 's.foo()': bool "#]], ); @@ -1601,11 +1601,11 @@ use core::IntoIterator; fn f() { let v = [4].into_iter(); v; - //^ &i32 + //^ &'? i32 let a = [0, 1].into_iter(); a; - //^ &i32 + //^ &'? i32 } //- /main2021.rs crate:main2021 deps:core edition:2021 @@ -1618,7 +1618,7 @@ fn f() { let a = [0, 1].into_iter(); a; - //^ &i32 + //^ &'? i32 } //- /core.rs crate:core @@ -1767,7 +1767,7 @@ fn test() { let a2 = A(make(), 1i32); let _: &str = a2.thing(); a2; - //^^ A<C<&str>, i32> + //^^ A<C<&'? str>, i32> } "#, ); diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs index 5d809b8239..0ccbcf63e2 100644 --- a/crates/hir-ty/src/tests/never_type.rs +++ b/crates/hir-ty/src/tests/never_type.rs @@ -104,7 +104,7 @@ enum Option<T> { None, Some(T) } fn test() { let a = if true { Option::None } else { Option::Some(return) }; a; - //^ Option<&str> + //^ Option<&'static str> match 42 { 42 => a, _ => Option::Some("str"), @@ -317,8 +317,8 @@ fn diverging_expression_2() { 63..81 '{ loop...foo" }': u32 65..72 'loop {}': ! 70..72 '{}': () - 74..79 '"foo"': &str - 74..79: expected u32, got &str + 74..79 '"foo"': &'static str + 74..79: expected u32, got &'static str "#]], ); } @@ -365,8 +365,8 @@ fn diverging_expression_3_break() { 151..172 'for a ...eak; }': {unknown} 151..172 'for a ...eak; }': ! 151..172 'for a ...eak; }': {unknown} - 151..172 'for a ...eak; }': &mut {unknown} - 151..172 'for a ...eak; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 151..172 'for a ...eak; }': &'? mut {unknown} + 151..172 'for a ...eak; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 151..172 'for a ...eak; }': Option<{unknown}> 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () @@ -381,8 +381,8 @@ fn diverging_expression_3_break() { 237..250 'for a in b {}': {unknown} 237..250 'for a in b {}': ! 237..250 'for a in b {}': {unknown} - 237..250 'for a in b {}': &mut {unknown} - 237..250 'for a in b {}': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 237..250 'for a in b {}': &'? mut {unknown} + 237..250 'for a in b {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 237..250 'for a in b {}': Option<{unknown}> 237..250 'for a in b {}': () 237..250 'for a in b {}': () @@ -396,8 +396,8 @@ fn diverging_expression_3_break() { 315..337 'for a ...urn; }': {unknown} 315..337 'for a ...urn; }': ! 315..337 'for a ...urn; }': {unknown} - 315..337 'for a ...urn; }': &mut {unknown} - 315..337 'for a ...urn; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 315..337 'for a ...urn; }': &'? mut {unknown} + 315..337 'for a ...urn; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 315..337 'for a ...urn; }': Option<{unknown}> 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index 4355881d72..5e040a60e2 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -32,27 +32,27 @@ fn infer_pattern() { } "#, expect![[r#" - 8..9 'x': &i32 + 8..9 'x': &'? i32 17..400 '{ ...o_x; }': () - 27..28 'y': &i32 - 31..32 'x': &i32 - 42..44 '&z': &i32 + 27..28 'y': &'? i32 + 31..32 'x': &'? i32 + 42..44 '&z': &'? i32 43..44 'z': i32 - 47..48 'x': &i32 + 47..48 'x': &'? i32 58..59 'a': i32 62..63 'z': i32 - 73..79 '(c, d)': (i32, &str) + 73..79 '(c, d)': (i32, &'static str) 74..75 'c': i32 - 77..78 'd': &str - 82..94 '(1, "hello")': (i32, &str) + 77..78 'd': &'static str + 82..94 '(1, "hello")': (i32, &'static str) 83..84 '1': i32 - 86..93 '"hello"': &str + 86..93 '"hello"': &'static str 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter 101..151 'for (e... }': {unknown} 101..151 'for (e... }': ! 101..151 'for (e... }': {unknown} - 101..151 'for (e... }': &mut {unknown} - 101..151 'for (e... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 101..151 'for (e... }': &'? mut {unknown} + 101..151 'for (e... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 101..151 'for (e... }': Option<({unknown}, {unknown})> 101..151 'for (e... }': () 101..151 'for (e... }': () @@ -74,10 +74,10 @@ fn infer_pattern() { 194..197 'val': {unknown} 210..236 'if let...rue {}': () 213..233 'let x ... &true': bool - 217..225 'x @ true': &bool + 217..225 'x @ true': &'? bool 221..225 'true': bool 221..225 'true': bool - 228..233 '&true': &bool + 228..233 '&true': &'? bool 229..233 'true': bool 234..236 '{}': () 246..252 'lambda': impl Fn(u64, u64, i32) -> i32 @@ -90,14 +90,14 @@ fn infer_pattern() { 277..282 'a + b': u64 281..282 'b': u64 284..285 'c': i32 - 298..310 'ref ref_to_x': &&i32 - 313..314 'x': &i32 - 324..333 'mut mut_x': &i32 - 336..337 'x': &i32 - 347..367 'ref mu...f_to_x': &mut &i32 - 370..371 'x': &i32 - 381..382 'k': &mut &i32 - 385..397 'mut_ref_to_x': &mut &i32 + 298..310 'ref ref_to_x': &'? &'? i32 + 313..314 'x': &'? i32 + 324..333 'mut mut_x': &'? i32 + 336..337 'x': &'? i32 + 347..367 'ref mu...f_to_x': &'? mut &'? i32 + 370..371 'x': &'? i32 + 381..382 'k': &'? mut &'? i32 + 385..397 'mut_ref_to_x': &'? mut &'? i32 "#]], ); } @@ -120,14 +120,14 @@ fn infer_literal_pattern() { 17..28 '{ loop {} }': T 19..26 'loop {}': ! 24..26 '{}': () - 37..38 'x': &i32 + 37..38 'x': &'? i32 46..208 '{ ...) {} }': () 52..75 'if let...y() {}': () 55..72 'let "f... any()': bool - 59..64 '"foo"': &str - 59..64 '"foo"': &str - 67..70 'any': fn any<&str>() -> &str - 67..72 'any()': &str + 59..64 '"foo"': &'static str + 59..64 '"foo"': &'static str + 67..70 'any': fn any<&'static str>() -> &'static str + 67..72 'any()': &'static str 73..75 '{}': () 80..99 'if let...y() {}': () 83..96 'let 1 = any()': bool @@ -178,7 +178,7 @@ fn infer_range_pattern() { } "#, expect![[r#" - 8..9 'x': &i32 + 8..9 'x': &'? i32 17..75 '{ ...2 {} }': () 23..45 'if let...u32 {}': () 26..42 'let 1....= 2u32': bool @@ -208,14 +208,14 @@ fn infer_pattern_match_ergonomics() { expect![[r#" 27..78 '{ ...(1); }': () 37..41 'A(n)': A<i32> - 39..40 'n': &i32 - 44..49 '&A(1)': &A<i32> + 39..40 'n': &'? i32 + 44..49 '&A(1)': &'? A<i32> 45..46 'A': extern "rust-call" A<i32>(i32) -> A<i32> 45..49 'A(1)': A<i32> 47..48 '1': i32 59..63 'A(n)': A<i32> - 61..62 'n': &mut i32 - 66..75 '&mut A(1)': &mut A<i32> + 61..62 'n': &'? mut i32 + 66..75 '&mut A(1)': &'? mut A<i32> 71..72 'A': extern "rust-call" A<i32>(i32) -> A<i32> 71..75 'A(1)': A<i32> 73..74 '1': i32 @@ -235,17 +235,17 @@ fn infer_pattern_match_ergonomics_ref() { "#, expect![[r#" 10..56 '{ ...= v; }': () - 20..21 'v': &(i32, &i32) - 24..32 '&(1, &2)': &(i32, &i32) - 25..32 '(1, &2)': (i32, &i32) + 20..21 'v': &'? (i32, &'? i32) + 24..32 '&(1, &2)': &'? (i32, &'? i32) + 25..32 '(1, &2)': (i32, &'? i32) 26..27 '1': i32 - 29..31 '&2': &i32 + 29..31 '&2': &'? i32 30..31 '2': i32 - 42..49 '(_, &w)': (i32, &i32) + 42..49 '(_, &w)': (i32, &'? i32) 43..44 '_': i32 - 46..48 '&w': &i32 + 46..48 '&w': &'? i32 47..48 'w': i32 - 52..53 'v': &(i32, &i32) + 52..53 'v': &'? (i32, &'? i32) "#]], ); } @@ -286,28 +286,28 @@ fn infer_pattern_match_slice() { "#, expect![[r#" 10..209 '{ ... } }': () - 20..25 'slice': &[f64] - 36..42 '&[0.0]': &[f64; 1] + 20..25 'slice': &'? [f64] + 36..42 '&[0.0]': &'? [f64; 1] 37..42 '[0.0]': [f64; 1] 38..41 '0.0': f64 48..207 'match ... }': () - 54..59 'slice': &[f64] - 70..73 '&[]': &[f64] + 54..59 'slice': &'? [f64] + 70..73 '&[]': &'? [f64] 71..73 '[]': [f64] 77..79 '{}': () - 89..93 '&[a]': &[f64] + 89..93 '&[a]': &'? [f64] 90..93 '[a]': [f64] 91..92 'a': f64 97..123 '{ ... }': () 111..112 'a': f64 - 133..140 '&[b, c]': &[f64] + 133..140 '&[b, c]': &'? [f64] 134..140 '[b, c]': [f64] 135..136 'b': f64 138..139 'c': f64 144..185 '{ ... }': () 158..159 'b': f64 173..174 'c': f64 - 194..195 '_': &[f64] + 194..195 '_': &'? [f64] 199..201 '{}': () "#]], ); @@ -327,14 +327,14 @@ fn infer_pattern_match_string_literal() { "#, expect![[r#" 10..98 '{ ... } }': () - 20..21 's': &str - 30..37 '"hello"': &str + 20..21 's': &'? str + 30..37 '"hello"': &'static str 43..96 'match ... }': () - 49..50 's': &str - 61..68 '"hello"': &str - 61..68 '"hello"': &str + 49..50 's': &'? str + 61..68 '"hello"': &'static str + 61..68 '"hello"': &'static str 72..74 '{}': () - 83..84 '_': &str + 83..84 '_': &'? str 88..90 '{}': () "#]], ); @@ -358,27 +358,27 @@ fn infer_pattern_match_byte_string_literal() { } "#, expect![[r#" - 105..109 'self': &[T; N] + 105..109 'self': &'? [T; N] 111..116 'index': {unknown} - 157..180 '{ ... }': &[u8] + 157..180 '{ ... }': &'? [u8] 167..174 'loop {}': ! 172..174 '{}': () 191..192 'v': [u8; 3] 203..261 '{ ...v {} }': () 209..233 'if let...[S] {}': () 212..230 'let b"... &v[S]': bool - 216..222 'b"foo"': &[u8] - 216..222 'b"foo"': &[u8] - 225..230 '&v[S]': &[u8] + 216..222 'b"foo"': &'static [u8] + 216..222 'b"foo"': &'static [u8] + 225..230 '&v[S]': &'? [u8] 226..227 'v': [u8; 3] 226..230 'v[S]': [u8] 228..229 'S': S 231..233 '{}': () 238..259 'if let... &v {}': () 241..256 'let b"foo" = &v': bool - 245..251 'b"foo"': &[u8; 3] - 245..251 'b"foo"': &[u8; 3] - 254..256 '&v': &[u8; 3] + 245..251 'b"foo"': &'static [u8; 3] + 245..251 'b"foo"': &'static [u8; 3] + 254..256 '&v': &'? [u8; 3] 255..256 'v': [u8; 3] 257..259 '{}': () "#]], @@ -399,17 +399,17 @@ fn infer_pattern_match_or() { "#, expect![[r#" 10..108 '{ ... } }': () - 20..21 's': &str - 30..37 '"hello"': &str + 20..21 's': &'? str + 30..37 '"hello"': &'static str 43..106 'match ... }': () - 49..50 's': &str - 61..68 '"hello"': &str - 61..68 '"hello"': &str - 61..78 '"hello...world"': &str - 71..78 '"world"': &str - 71..78 '"world"': &str + 49..50 's': &'? str + 61..68 '"hello"': &'static str + 61..68 '"hello"': &'static str + 61..78 '"hello...world"': &'? str + 71..78 '"world"': &'static str + 71..78 '"world"': &'static str 82..84 '{}': () - 93..94 '_': &str + 93..94 '_': &'? str 98..100 '{}': () "#]], ); @@ -505,10 +505,10 @@ fn infer_adt_pattern() { 216..217 '1': usize 227..231 'E::B': E 235..237 '10': usize - 255..274 'ref d ...{ .. }': &E + 255..274 'ref d ...{ .. }': &'? E 263..274 'E::A { .. }': E 277..278 'e': E - 284..285 'd': &E + 284..285 'd': &'? E "#]], ); } @@ -529,14 +529,14 @@ impl Foo { expect![[r#" 42..151 '{ ... }': () 56..64 'Self(s,)': Foo - 61..62 's': &usize - 67..75 '&Foo(0,)': &Foo + 61..62 's': &'? usize + 67..75 '&Foo(0,)': &'? Foo 68..71 'Foo': extern "rust-call" Foo(usize) -> Foo 68..75 'Foo(0,)': Foo 72..73 '0': usize 89..97 'Self(s,)': Foo - 94..95 's': &mut usize - 100..112 '&mut Foo(0,)': &mut Foo + 94..95 's': &'? mut usize + 100..112 '&mut Foo(0,)': &'? mut Foo 105..108 'Foo': extern "rust-call" Foo(usize) -> Foo 105..112 'Foo(0,)': Foo 109..110 '0': usize @@ -669,7 +669,7 @@ fn main() { } "#, expect![[r#" - 27..31 'self': &S + 27..31 'self': &'? S 41..50 '{ false }': bool 43..48 'false': bool 64..115 '{ ... } }': () @@ -679,7 +679,7 @@ fn main() { 93..94 's': S 93..100 's.foo()': bool 104..106 '()': () - "#]], + "#]], ) } @@ -702,29 +702,29 @@ fn test() { 51..58 'loop {}': ! 56..58 '{}': () 72..171 '{ ... x); }': () - 78..81 'foo': fn foo<&(i32, &str), i32, impl FnOnce(&(i32, &str)) -> i32>(&(i32, &str), impl FnOnce(&(i32, &str)) -> i32) -> i32 + 78..81 'foo': fn foo<&'? (i32, &'? str), i32, impl FnOnce(&'? (i32, &'? str)) -> i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> i32) -> i32 78..105 'foo(&(...y)| x)': i32 - 82..91 '&(1, "a")': &(i32, &str) - 83..91 '(1, "a")': (i32, &str) + 82..91 '&(1, "a")': &'? (i32, &'static str) + 83..91 '(1, "a")': (i32, &'static str) 84..85 '1': i32 - 87..90 '"a"': &str - 93..104 '|&(x, y)| x': impl FnOnce(&(i32, &str)) -> i32 - 94..101 '&(x, y)': &(i32, &str) - 95..101 '(x, y)': (i32, &str) + 87..90 '"a"': &'static str + 93..104 '|&(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> i32 + 94..101 '&(x, y)': &'? (i32, &'? str) + 95..101 '(x, y)': (i32, &'? str) 96..97 'x': i32 - 99..100 'y': &str + 99..100 'y': &'? str 103..104 'x': i32 - 142..145 'foo': fn foo<&(i32, &str), &i32, impl FnOnce(&(i32, &str)) -> &i32>(&(i32, &str), impl FnOnce(&(i32, &str)) -> &i32) -> &i32 - 142..168 'foo(&(...y)| x)': &i32 - 146..155 '&(1, "a")': &(i32, &str) - 147..155 '(1, "a")': (i32, &str) + 142..145 'foo': fn foo<&'? (i32, &'? str), &'? i32, impl FnOnce(&'? (i32, &'? str)) -> &'? i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> &'? i32) -> &'? i32 + 142..168 'foo(&(...y)| x)': &'? i32 + 146..155 '&(1, "a")': &'? (i32, &'static str) + 147..155 '(1, "a")': (i32, &'static str) 148..149 '1': i32 - 151..154 '"a"': &str - 157..167 '|(x, y)| x': impl FnOnce(&(i32, &str)) -> &i32 - 158..164 '(x, y)': (i32, &str) - 159..160 'x': &i32 - 162..163 'y': &&str - 166..167 'x': &i32 + 151..154 '"a"': &'static str + 157..167 '|(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> &'? i32 + 158..164 '(x, y)': (i32, &'? str) + 159..160 'x': &'? i32 + 162..163 'y': &'? &'? str + 166..167 'x': &'? i32 "#]], ); } @@ -741,13 +741,13 @@ fn slice_tail_pattern() { } "#, expect![[r#" - 7..13 'params': &[i32] + 7..13 'params': &'? [i32] 23..92 '{ ... } }': () 29..90 'match ... }': () - 35..41 'params': &[i32] + 35..41 'params': &'? [i32] 52..69 '[head,... @ ..]': [i32] - 53..57 'head': &i32 - 59..68 'tail @ ..': &[i32] + 53..57 'head': &'? i32 + 59..68 'tail @ ..': &'? [i32] 66..68 '..': [i32] 73..84 '{ }': () "#]], @@ -1109,7 +1109,7 @@ fn var_args() { #[lang = "va_list"] pub struct VaListImpl<'f>; fn my_fn(foo: ...) {} - //^^^ VaListImpl<'{error}> + //^^^ VaListImpl<'?> "#, ); } @@ -1122,9 +1122,9 @@ fn foo() { let &() = &(); let &mut () = &mut (); let &mut () = &(); - //^^^^^^^ expected &(), got &mut () + //^^^^^^^ expected &'? (), got &'? mut () let &() = &mut (); - //^^^ expected &mut (), got &() + //^^^ expected &'? mut (), got &'? () } "#, ); @@ -1148,7 +1148,7 @@ fn main() { }; if let Wrap::<X>::A { cool, ..} = &wrapped {} - //^^^^ &u32 + //^^^^ &'? u32 } "#, ); @@ -1182,7 +1182,7 @@ fn main() { }; if let Wrap::<<S as Schematic>::Props>::A { cool, ..} = &wrapped {} - //^^^^ &u32 + //^^^^ &'? u32 } "#, ); @@ -1217,7 +1217,7 @@ fn main() { match &6 { Foo::<i32>::TEST_I32_REF => (), Foo::<i32>::TEST_I32 => (), - //^^^^^^^^^^^^^^^^^^^^ expected &i32, got i32 + //^^^^^^^^^^^^^^^^^^^^ expected &'? i32, got i32 _ => (), } } diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index c2d2047e6f..3aa94be755 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -99,7 +99,7 @@ fn recursive_vars() { 24..31 'unknown': {unknown} 37..44 '[y, &y]': [{unknown}; 2] 38..39 'y': {unknown} - 41..43 '&y': &{unknown} + 41..43 '&y': &'? {unknown} 42..43 'y': {unknown} "#]], ); @@ -117,19 +117,19 @@ fn recursive_vars_2() { "#, expect![[r#" 10..79 '{ ...x)]; }': () - 20..21 'x': &{unknown} - 24..31 'unknown': &{unknown} + 20..21 'x': &'? {unknown} + 24..31 'unknown': &'? {unknown} 41..42 'y': {unknown} 45..52 'unknown': {unknown} - 58..76 '[(x, y..., &x)]': [(&{unknown}, {unknown}); 2] - 59..65 '(x, y)': (&{unknown}, {unknown}) - 60..61 'x': &{unknown} + 58..76 '[(x, y..., &x)]': [(&'? {unknown}, {unknown}); 2] + 59..65 '(x, y)': (&'? {unknown}, {unknown}) + 60..61 'x': &'? {unknown} 63..64 'y': {unknown} - 67..75 '(&y, &x)': (&{unknown}, {unknown}) - 68..70 '&y': &{unknown} + 67..75 '(&y, &x)': (&'? {unknown}, {unknown}) + 68..70 '&y': &'? {unknown} 69..70 'y': {unknown} - 72..74 '&x': &&{unknown} - 73..74 'x': &{unknown} + 72..74 '&x': &'? &'? {unknown} + 73..74 'x': &'? {unknown} "#]], ); } @@ -166,7 +166,7 @@ fn infer_std_crash_1() { 59..136 'match ... }': () 65..82 'someth...nknown': Maybe<{unknown}> 93..123 'Maybe:...thing)': Maybe<{unknown}> - 105..122 'ref mu...ething': &mut {unknown} + 105..122 'ref mu...ething': &'? mut {unknown} 127..129 '()': () "#]], ); @@ -183,7 +183,7 @@ fn infer_std_crash_2() { "#, expect![[r#" 22..52 '{ ...n']; }': () - 28..49 '&[0, b...b'\n']': &[u8; 4] + 28..49 '&[0, b...b'\n']': &'? [u8; 4] 29..49 '[0, b'...b'\n']': [u8; 4] 30..31 '0': u8 33..38 'b'\n'': u8 @@ -269,8 +269,8 @@ fn infer_std_crash_5() { 32..320 'for co... }': {unknown} 32..320 'for co... }': ! 32..320 'for co... }': {unknown} - 32..320 'for co... }': &mut {unknown} - 32..320 'for co... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 32..320 'for co... }': &'? mut {unknown} + 32..320 'for co... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> 32..320 'for co... }': Option<{unknown}> 32..320 'for co... }': () 32..320 'for co... }': () @@ -278,22 +278,22 @@ fn infer_std_crash_5() { 36..43 'content': {unknown} 47..60 'doesnt_matter': {unknown} 61..320 '{ ... }': () - 75..79 'name': &{unknown} - 82..166 'if doe... }': &{unknown} + 75..79 'name': &'? {unknown} + 82..166 'if doe... }': &'? {unknown} 85..98 'doesnt_matter': bool - 99..128 '{ ... }': &{unknown} - 113..118 'first': &{unknown} - 134..166 '{ ... }': &{unknown} - 148..156 '&content': &{unknown} + 99..128 '{ ... }': &'? {unknown} + 113..118 'first': &'? {unknown} + 134..166 '{ ... }': &'? {unknown} + 148..156 '&content': &'? {unknown} 149..156 'content': {unknown} - 181..188 'content': &{unknown} - 191..313 'if ICE... }': &{unknown} + 181..188 'content': &'? {unknown} + 191..313 'if ICE... }': &'? {unknown} 194..231 'ICE_RE..._VALUE': {unknown} 194..247 'ICE_RE...&name)': bool - 241..246 '&name': &&{unknown} - 242..246 'name': &{unknown} - 248..276 '{ ... }': &{unknown} - 262..266 'name': &{unknown} + 241..246 '&name': &'? &'? {unknown} + 242..246 'name': &'? {unknown} + 248..276 '{ ... }': &'? {unknown} + 262..266 'name': &'? {unknown} 282..313 '{ ... }': {unknown} 296..303 'content': {unknown} "#]], @@ -318,7 +318,7 @@ fn infer_nested_generics_crash() { expect![[r#" 91..105 'query_response': Canonical<QueryResponse<R>> 136..166 '{ ...lue; }': () - 142..163 '&query....value': &QueryResponse<R> + 142..163 '&query....value': &'? QueryResponse<R> 143..157 'query_response': Canonical<QueryResponse<R>> 143..163 'query_....value': QueryResponse<R> "#]], @@ -465,12 +465,12 @@ fn issue_3999_slice() { } "#, expect![[r#" - 7..13 'params': &[usize] + 7..13 'params': &'? [usize] 25..80 '{ ... } }': () 31..78 'match ... }': () - 37..43 'params': &[usize] + 37..43 'params': &'? [usize] 54..66 '[ps @ .., _]': [usize] - 55..62 'ps @ ..': &[usize] + 55..62 'ps @ ..': &'? [usize] 60..62 '..': [usize] 64..65 '_': usize 70..72 '{}': () @@ -523,13 +523,13 @@ fn issue_4235_name_conflicts() { "#, expect![[r#" 31..37 'FOO {}': FOO - 63..67 'self': &FOO + 63..67 'self': &'? FOO 69..71 '{}': () 85..119 '{ ...o(); }': () - 95..96 'a': &FOO - 99..103 '&FOO': &FOO + 95..96 'a': &'? FOO + 99..103 '&FOO': &'? FOO 100..103 'FOO': FOO - 109..110 'a': &FOO + 109..110 'a': &'? FOO 109..116 'a.foo()': () "#]], ); @@ -715,12 +715,12 @@ fn issue_4885() { } "#, expect![[r#" - 70..73 'key': &K + 70..73 'key': &'? K 132..148 '{ ...key) }': impl Future<Output = <K as Foo<R>>::Bar> - 138..141 'bar': fn bar<R, K>(&K) -> impl Future<Output = <K as Foo<R>>::Bar> + 138..141 'bar': fn bar<R, K>(&'? K) -> impl Future<Output = <K as Foo<R>>::Bar> 138..146 'bar(key)': impl Future<Output = <K as Foo<R>>::Bar> - 142..145 'key': &K - 162..165 'key': &K + 142..145 'key': &'? K + 162..165 'key': &'? K 224..227 '{ }': () "#]], ); @@ -771,11 +771,11 @@ fn issue_4800() { } "#, expect![[r#" - 379..383 'self': &mut PeerSet<D> + 379..383 'self': &'? mut PeerSet<D> 401..424 '{ ... }': dyn Future<Output = ()> 411..418 'loop {}': ! 416..418 '{}': () - 575..579 'self': &mut Self + 575..579 'self': &'? mut Self "#]], ); } @@ -815,19 +815,19 @@ fn issue_4966() { 225..229 'iter': T 244..246 '{}': Vec<A> 258..402 '{ ...r(); }': () - 268..273 'inner': Map<impl Fn(&f64) -> f64> - 276..300 'Map { ... 0.0 }': Map<impl Fn(&f64) -> f64> - 285..298 '|_: &f64| 0.0': impl Fn(&f64) -> f64 - 286..287 '_': &f64 + 268..273 'inner': Map<impl Fn(&'? f64) -> f64> + 276..300 'Map { ... 0.0 }': Map<impl Fn(&'? f64) -> f64> + 285..298 '|_: &f64| 0.0': impl Fn(&'? f64) -> f64 + 286..287 '_': &'? f64 295..298 '0.0': f64 - 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>>>> - 372..378 'repeat': Repeat<Map<impl Fn(&f64) -> f64>> - 386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>> + 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>>>> + 372..378 'repeat': Repeat<Map<impl Fn(&'? f64) -> f64>> + 386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&'? f64) -> f64>>>> 386..399 'vec.foo_bar()': {unknown} "#]], ); @@ -850,10 +850,10 @@ fn main() { } "#, expect![[r#" - 40..44 'self': &S<T> + 40..44 'self': &'? S<T> 46..48 '_t': T 53..55 '{}': () - 81..85 'self': &S<T> + 81..85 'self': &'? S<T> 87..89 '_f': F 94..96 '{}': () 109..160 '{ ...10); }': () @@ -862,8 +862,8 @@ fn main() { 123..126 'S()': S<i32> 132..133 's': S<i32> 132..144 's.g(|_x| {})': () - 136..143 '|_x| {}': impl FnOnce(&i32) - 137..139 '_x': &i32 + 136..143 '|_x| {}': impl FnOnce(&'? i32) + 137..139 '_x': &'? i32 141..143 '{}': () 150..151 's': S<i32> 150..157 's.f(10)': () @@ -895,14 +895,14 @@ fn flush(&self) { } "#, expect![[r#" - 123..127 'self': &Mutex<T> - 150..152 '{}': MutexGuard<'{error}, T> - 234..238 'self': &{unknown} + 123..127 'self': &'? Mutex<T> + 150..152 '{}': MutexGuard<'?, T> + 234..238 'self': &'? {unknown} 240..290 '{ ...()); }': () - 250..251 'w': &Mutex<BufWriter> + 250..251 'w': &'? Mutex<BufWriter> 276..287 '*(w.lock())': BufWriter - 278..279 'w': &Mutex<BufWriter> - 278..286 'w.lock()': MutexGuard<'{error}, BufWriter> + 278..279 'w': &'? Mutex<BufWriter> + 278..286 'w.lock()': MutexGuard<'?, BufWriter> "#]], ); } @@ -1023,20 +1023,20 @@ fn cfg_tail() { expect![[r#" 14..53 '{ ...)] 9 }': () 20..31 '{ "first" }': () - 22..29 '"first"': &str + 22..29 '"first"': &'static str 72..190 '{ ...] 13 }': () 78..88 '{ "fake" }': () - 80..86 '"fake"': &str + 80..86 '"fake"': &'static str 93..103 '{ "fake" }': () - 95..101 '"fake"': &str + 95..101 '"fake"': &'static str 108..120 '{ "second" }': () - 110..118 '"second"': &str + 110..118 '"second"': &'static str 210..273 '{ ... 15; }': () 216..227 '{ "third" }': () - 218..225 '"third"': &str + 218..225 '"third"': &'static str 293..357 '{ ...] 15 }': () - 299..311 '{ "fourth" }': &str - 301..309 '"fourth"': &str + 299..311 '{ "fourth" }': &'static str + 301..309 '"fourth"': &'static str "#]], ) } @@ -1238,8 +1238,8 @@ fn test() { 16..66 'for _ ... }': IntoIterator::IntoIter<()> 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 _ ... }': &'? 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 _ ... }': () 16..66 'for _ ... }': () @@ -1600,85 +1600,6 @@ fn f(s: S) { } #[test] -fn rust_161_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 Drop + ~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 - } -} - -pub trait Destruct {} - -impl<T: ?Sized> const Destruct for T {} -"#, - ); -} - -#[test] -fn rust_162_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 {} -"#, - ); -} - -#[test] fn tuple_struct_pattern_with_unmatched_args_crash() { check_infer( r#" @@ -1727,7 +1648,7 @@ fn dyn_with_unresolved_trait() { r#" fn foo(a: &dyn DoesNotExist) { a.bar(); - //^&{unknown} + //^&'? {unknown} } "#, ); @@ -1851,9 +1772,9 @@ fn foo() { match &E::A { b @ (x @ E::A | x) => { b; - //^ &E + //^ &'? E x; - //^ &E + //^ &'? E } } }", @@ -2040,3 +1961,41 @@ fn main() { "#, ) } + +#[test] +fn cfg_first_trait_param_16141() { + check_no_mismatches( + r#" +//- minicore: sized, coerce_unsized +trait Bar { + fn bar(&self) {} +} + +impl<#[cfg(feature = "a-feature")] A> Bar for (){} +"#, + ) +} + +#[test] +fn nested_anon_generics_and_where_bounds_17173() { + check_types( + r#" +//- minicore: sized, fn +pub trait Lookup { + type Data; + fn lookup(&self) -> Self::Data; +} +pub trait ItemTreeLoc { + type Id; +} +fn id_to_generics(id: impl Lookup<Data = impl ItemTreeLoc<Id = ()>>, + //^^ impl Lookup<Data = impl ItemTreeLoc<Id = ()>> + enabled_params: impl Fn(), + //^^^^^^^^^^^^^^ impl Fn() + ) +where + (): Sized, +{} +"#, + ); +} diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index a9d28ebfef..e2cd7fa26b 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -115,15 +115,15 @@ fn test(a: u32, b: isize, c: !, d: &str) { 8..9 'a': u32 16..17 'b': isize 26..27 'c': ! - 32..33 'd': &str + 32..33 'd': &'? str 41..120 '{ ...f32; }': () 47..48 'a': u32 54..55 'b': isize 61..62 'c': ! - 68..69 'd': &str + 68..69 'd': &'? str 75..81 '1usize': usize 87..93 '1isize': isize - 99..105 '"test"': &str + 99..105 '"test"': &'static str 111..117 '1.0f32': f32 "#]], ); @@ -344,23 +344,23 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { } "#, expect![[r#" - 8..9 'a': &u32 - 17..18 'b': &mut u32 + 8..9 'a': &'? u32 + 17..18 'b': &'? mut u32 30..31 'c': *const u32 45..46 'd': *mut u32 58..149 '{ ... *d; }': () - 64..65 'a': &u32 + 64..65 'a': &'? u32 71..73 '*a': u32 - 72..73 'a': &u32 - 79..81 '&a': &&u32 - 80..81 'a': &u32 - 87..93 '&mut a': &mut &u32 - 92..93 'a': &u32 - 99..100 'b': &mut u32 + 72..73 'a': &'? u32 + 79..81 '&a': &'? &'? u32 + 80..81 'a': &'? u32 + 87..93 '&mut a': &'? mut &'? u32 + 92..93 'a': &'? u32 + 99..100 'b': &'? mut u32 106..108 '*b': u32 - 107..108 'b': &mut u32 - 114..116 '&b': &&mut u32 - 115..116 'b': &mut u32 + 107..108 'b': &'? mut u32 + 114..116 '&b': &'? &'? mut u32 + 115..116 'b': &'? mut u32 122..123 'c': *const u32 129..131 '*c': u32 130..131 'c': *const u32 @@ -425,22 +425,22 @@ h"; 32..36 '5i32': i32 50..54 '5f32': f32 68..72 '5f64': f64 - 86..93 '"hello"': &str - 107..115 'b"bytes"': &[u8; 5] + 86..93 '"hello"': &'static str + 107..115 'b"bytes"': &'static [u8; 5] 129..132 ''c'': char 146..150 'b'b'': u8 164..168 '3.14': f64 182..186 '5000': i32 200..205 'false': bool 219..223 'true': bool - 237..333 'r#" ... "#': &str - 347..357 'br#"yolo"#': &[u8; 4] - 375..376 'a': &[u8; 4] - 379..403 'b"a\x2... c"': &[u8; 4] - 421..422 'b': &[u8; 4] - 425..433 'br"g\ h"': &[u8; 4] - 451..452 'c': &[u8; 6] - 455..467 'br#"x"\"yb"#': &[u8; 6] + 237..333 'r#" ... "#': &'static str + 347..357 'br#"yolo"#': &'static [u8; 4] + 375..376 'a': &'static [u8; 4] + 379..403 'b"a\x2... c"': &'static [u8; 4] + 421..422 'b': &'static [u8; 4] + 425..433 'br"g\ h"': &'static [u8; 4] + 451..452 'c': &'static [u8; 6] + 455..467 'br#"x"\"yb"#': &'static [u8; 6] "##]], ); } @@ -508,9 +508,9 @@ fn test(x: SomeType) { 238..240 '!x': {unknown} 239..240 'x': SomeType 246..254 '-"hello"': {unknown} - 247..254 '"hello"': &str + 247..254 '"hello"': &'static str 260..268 '!"hello"': {unknown} - 261..268 '"hello"': &str + 261..268 '"hello"': &'static str "#]], ); } @@ -535,7 +535,7 @@ fn test() -> &mut &f64 { expect![[r#" 13..14 'x': u32 21..23 '{}': () - 77..230 '{ ...t &c }': &mut &f64 + 77..230 '{ ...t &c }': &'? mut &'? f64 87..88 'a': u32 91..107 'unknow...nction': {unknown} 91..109 'unknow...tion()': u32 @@ -550,8 +550,8 @@ fn test() -> &mut &f64 { 193..194 'c': f64 197..213 'unknow...nction': {unknown} 197..215 'unknow...tion()': f64 - 221..228 '&mut &c': &mut &f64 - 226..228 '&c': &f64 + 221..228 '&mut &c': &'? mut &'? f64 + 226..228 '&c': &'? f64 227..228 'c': f64 "#]], ); @@ -579,12 +579,12 @@ impl S { } "#, expect![[r#" - 33..37 'self': &S + 33..37 'self': &'? S 39..60 '{ ... }': () - 49..53 'self': &S - 74..78 'self': &S + 49..53 'self': &'? S + 74..78 'self': &'? S 87..108 '{ ... }': () - 97..101 'self': &S + 97..101 'self': &'? S 132..152 '{ ... }': S 142..146 'S {}': S 176..199 '{ ... }': S @@ -771,35 +771,35 @@ fn test2(a1: *const A, a2: *mut A) { 64..65 'a': A 71..73 'a1': A 71..75 'a1.b': B - 85..87 'a2': &A - 90..92 '&a': &A + 85..87 'a2': &'? A + 90..92 '&a': &'? A 91..92 'a': A - 98..100 'a2': &A + 98..100 'a2': &'? A 98..102 'a2.b': B - 112..114 'a3': &mut A - 117..123 '&mut a': &mut A + 112..114 'a3': &'? mut A + 117..123 '&mut a': &'? mut A 122..123 'a': A - 129..131 'a3': &mut A + 129..131 'a3': &'? mut A 129..133 'a3.b': B - 143..145 'a4': &&&&&&&A - 148..156 '&&&&&&&a': &&&&&&&A - 149..156 '&&&&&&a': &&&&&&A - 150..156 '&&&&&a': &&&&&A - 151..156 '&&&&a': &&&&A - 152..156 '&&&a': &&&A - 153..156 '&&a': &&A - 154..156 '&a': &A + 143..145 'a4': &'? &'? &'? &'? &'? &'? &'? A + 148..156 '&&&&&&&a': &'? &'? &'? &'? &'? &'? &'? A + 149..156 '&&&&&&a': &'? &'? &'? &'? &'? &'? A + 150..156 '&&&&&a': &'? &'? &'? &'? &'? A + 151..156 '&&&&a': &'? &'? &'? &'? A + 152..156 '&&&a': &'? &'? &'? A + 153..156 '&&a': &'? &'? A + 154..156 '&a': &'? A 155..156 'a': A - 162..164 'a4': &&&&&&&A + 162..164 'a4': &'? &'? &'? &'? &'? &'? &'? A 162..166 'a4.b': B - 176..178 'a5': &mut &&mut &&mut A - 181..199 '&mut &...&mut a': &mut &&mut &&mut A - 186..199 '&&mut &&mut a': &&mut &&mut A - 187..199 '&mut &&mut a': &mut &&mut A - 192..199 '&&mut a': &&mut A - 193..199 '&mut a': &mut A + 176..178 'a5': &'? mut &'? &'? mut &'? &'? mut A + 181..199 '&mut &...&mut a': &'? mut &'? &'? mut &'? &'? mut A + 186..199 '&&mut &&mut a': &'? &'? mut &'? &'? mut A + 187..199 '&mut &&mut a': &'? mut &'? &'? mut A + 192..199 '&&mut a': &'? &'? mut A + 193..199 '&mut a': &'? mut A 198..199 'a': A - 205..207 'a5': &mut &&mut &&mut A + 205..207 'a5': &'? mut &'? &'? mut &'? &'? mut A 205..209 'a5.b': B 223..225 'a1': *const A 237..239 'a2': *mut A @@ -840,22 +840,22 @@ fn test() { } "#, expect![[r#" - 66..70 'self': &A<T> - 78..101 '{ ... }': &T - 88..95 '&self.0': &T - 89..93 'self': &A<T> + 66..70 'self': &'? A<T> + 78..101 '{ ... }': &'? T + 88..95 '&self.0': &'? T + 89..93 'self': &'? A<T> 89..95 'self.0': T - 182..186 'self': &B<T> - 205..228 '{ ... }': &T - 215..222 '&self.0': &T - 216..220 'self': &B<T> + 182..186 'self': &'? B<T> + 205..228 '{ ... }': &'? T + 215..222 '&self.0': &'? T + 216..220 'self': &'? B<T> 216..222 'self.0': T 242..280 '{ ...))); }': () - 252..253 't': &i32 - 256..262 'A::foo': fn foo<i32>(&A<i32>) -> &i32 - 256..277 'A::foo...42))))': &i32 - 263..276 '&&B(B(A(42)))': &&B<B<A<i32>>> - 264..276 '&B(B(A(42)))': &B<B<A<i32>>> + 252..253 't': &'? i32 + 256..262 'A::foo': fn foo<i32>(&'? A<i32>) -> &'? i32 + 256..277 'A::foo...42))))': &'? i32 + 263..276 '&&B(B(A(42)))': &'? &'? B<B<A<i32>>> + 264..276 '&B(B(A(42)))': &'? B<B<A<i32>>> 265..266 'B': extern "rust-call" B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> 265..276 'B(B(A(42)))': B<B<A<i32>>> 267..268 'B': extern "rust-call" B<A<i32>>(A<i32>) -> B<A<i32>> @@ -895,28 +895,28 @@ fn test(a: A<i32>) { } "#, expect![[r#" - 71..75 'self': &A<T> - 77..78 'x': &A<T> - 93..114 '{ ... }': &T - 103..108 '&*x.0': &T + 71..75 'self': &'? A<T> + 77..78 'x': &'? A<T> + 93..114 '{ ... }': &'? T + 103..108 '&*x.0': &'? T 104..108 '*x.0': T - 105..106 'x': &A<T> + 105..106 'x': &'? A<T> 105..108 'x.0': *mut T - 195..199 'self': &B<T> - 218..241 '{ ... }': &T - 228..235 '&self.0': &T - 229..233 'self': &B<T> + 195..199 'self': &'? B<T> + 218..241 '{ ... }': &'? T + 228..235 '&self.0': &'? T + 229..233 'self': &'? B<T> 229..235 'self.0': T 253..254 'a': A<i32> 264..310 '{ ...))); }': () - 274..275 't': &i32 + 274..275 't': &'? i32 278..279 'A': extern "rust-call" A<i32>(*mut i32) -> A<i32> 278..292 'A(0 as *mut _)': A<i32> - 278..307 'A(0 as...B(a)))': &i32 + 278..307 'A(0 as...B(a)))': &'? i32 280..281 '0': i32 280..291 '0 as *mut _': *mut i32 - 297..306 '&&B(B(a))': &&B<B<A<i32>>> - 298..306 '&B(B(a))': &B<B<A<i32>>> + 297..306 '&&B(B(a))': &'? &'? B<B<A<i32>>> + 298..306 '&B(B(a))': &'? B<B<A<i32>>> 299..300 'B': extern "rust-call" B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> 299..306 'B(B(a))': B<B<A<i32>>> 301..302 'B': extern "rust-call" B<A<i32>>(A<i32>) -> B<A<i32>> @@ -1044,7 +1044,7 @@ fn infer_inherent_method() { 31..35 'self': A 37..38 'x': u32 52..54 '{}': i32 - 106..110 'self': &A + 106..110 'self': &'? A 112..113 'x': u64 127..129 '{}': i64 147..148 'a': A @@ -1053,7 +1053,7 @@ fn infer_inherent_method() { 159..167 'a.foo(1)': i32 165..166 '1': u32 173..184 '(&a).bar(1)': i64 - 174..176 '&a': &A + 174..176 '&a': &'? A 175..176 'a': A 182..183 '1': u64 190..191 'a': A @@ -1078,10 +1078,10 @@ fn test() { } "#, expect![[r#" - 67..71 'self': &str + 67..71 'self': &'? str 80..82 '{}': i32 96..116 '{ ...o(); }': () - 102..107 '"foo"': &str + 102..107 '"foo"': &'static str 102..113 '"foo".foo()': i32 "#]], ); @@ -1101,33 +1101,33 @@ fn infer_tuple() { } "#, expect![[r#" - 8..9 'x': &str + 8..9 'x': &'? str 17..18 'y': isize 27..169 '{ ...d"); }': () - 37..38 'a': (u32, &str) - 54..62 '(1, "a")': (u32, &str) + 37..38 'a': (u32, &'? str) + 54..62 '(1, "a")': (u32, &'? str) 55..56 '1': u32 - 58..61 '"a"': &str - 72..73 'b': ((u32, &str), &str) - 76..82 '(a, x)': ((u32, &str), &str) - 77..78 'a': (u32, &str) - 80..81 'x': &str - 92..93 'c': (isize, &str) - 96..102 '(y, x)': (isize, &str) + 58..61 '"a"': &'static str + 72..73 'b': ((u32, &'? str), &'? str) + 76..82 '(a, x)': ((u32, &'? str), &'? str) + 77..78 'a': (u32, &'? str) + 80..81 'x': &'? str + 92..93 'c': (isize, &'? str) + 96..102 '(y, x)': (isize, &'? str) 97..98 'y': isize - 100..101 'x': &str - 112..113 'd': ((isize, &str), &str) - 116..122 '(c, x)': ((isize, &str), &str) - 117..118 'c': (isize, &str) - 120..121 'x': &str - 132..133 'e': (i32, &str) - 136..144 '(1, "e")': (i32, &str) + 100..101 'x': &'? str + 112..113 'd': ((isize, &'? str), &'? str) + 116..122 '(c, x)': ((isize, &'? str), &'? str) + 117..118 'c': (isize, &'? str) + 120..121 'x': &'? str + 132..133 'e': (i32, &'static str) + 136..144 '(1, "e")': (i32, &'static str) 137..138 '1': i32 - 140..143 '"e"': &str - 154..155 'f': ((i32, &str), &str) - 158..166 '(e, "d")': ((i32, &str), &str) - 159..160 'e': (i32, &str) - 162..165 '"d"': &str + 140..143 '"e"': &'static str + 154..155 'f': ((i32, &'static str), &'static str) + 158..166 '(e, "d")': ((i32, &'static str), &'static str) + 159..160 'e': (i32, &'static str) + 162..165 '"d"': &'static str "#]], ); } @@ -1156,20 +1156,20 @@ fn infer_array() { } "#, expect![[r#" - 8..9 'x': &str + 8..9 'x': &'? str 17..18 'y': isize 27..326 '{ ...,4]; }': () - 37..38 'a': [&str; 1] - 41..44 '[x]': [&str; 1] - 42..43 'x': &str - 54..55 'b': [[&str; 1]; 2] - 58..64 '[a, a]': [[&str; 1]; 2] - 59..60 'a': [&str; 1] - 62..63 'a': [&str; 1] - 74..75 'c': [[[&str; 1]; 2]; 2] - 78..84 '[b, b]': [[[&str; 1]; 2]; 2] - 79..80 'b': [[&str; 1]; 2] - 82..83 'b': [[&str; 1]; 2] + 37..38 'a': [&'? str; 1] + 41..44 '[x]': [&'? str; 1] + 42..43 'x': &'? str + 54..55 'b': [[&'? str; 1]; 2] + 58..64 '[a, a]': [[&'? str; 1]; 2] + 59..60 'a': [&'? str; 1] + 62..63 'a': [&'? str; 1] + 74..75 'c': [[[&'? str; 1]; 2]; 2] + 78..84 '[b, b]': [[[&'? str; 1]; 2]; 2] + 79..80 'b': [[&'? str; 1]; 2] + 82..83 'b': [[&'? str; 1]; 2] 95..96 'd': [isize; 4] 99..111 '[y, 1, 2, 3]': [isize; 4] 100..101 'y': isize @@ -1197,15 +1197,15 @@ fn infer_array() { 209..215 '[1, 2]': [i32; 2] 210..211 '1': i32 213..214 '2': i32 - 225..226 'i': [&str; 2] - 229..239 '["a", "b"]': [&str; 2] - 230..233 '"a"': &str - 235..238 '"b"': &str - 250..251 'b': [[&str; 1]; 2] - 254..264 '[a, ["b"]]': [[&str; 1]; 2] - 255..256 'a': [&str; 1] - 258..263 '["b"]': [&str; 1] - 259..262 '"b"': &str + 225..226 'i': [&'? str; 2] + 229..239 '["a", "b"]': [&'? str; 2] + 230..233 '"a"': &'static str + 235..238 '"b"': &'static str + 250..251 'b': [[&'? str; 1]; 2] + 254..264 '[a, ["b"]]': [[&'? str; 1]; 2] + 255..256 'a': [&'? str; 1] + 258..263 '["b"]': [&'? str; 1] + 259..262 '"b"': &'static str 274..275 'x': [u8; 0] 287..289 '[]': [u8; 0] 299..300 'y': [u8; 4] @@ -1279,12 +1279,12 @@ fn infer_tuple_struct_generics() { 92..93 'A': extern "rust-call" A<u128>(u128) -> A<u128> 92..101 'A(42u128)': A<u128> 94..100 '42u128': u128 - 107..111 'Some': extern "rust-call" Some<&str>(&str) -> Option<&str> - 107..116 'Some("x")': Option<&str> - 112..115 '"x"': &str - 122..134 'Option::Some': extern "rust-call" Some<&str>(&str) -> Option<&str> - 122..139 'Option...e("x")': Option<&str> - 135..138 '"x"': &str + 107..111 'Some': extern "rust-call" Some<&'static str>(&'static str) -> Option<&'static str> + 107..116 'Some("x")': Option<&'static str> + 112..115 '"x"': &'static str + 122..134 'Option::Some': extern "rust-call" Some<&'static str>(&'static str) -> Option<&'static str> + 122..139 'Option...e("x")': Option<&'static str> + 135..138 '"x"': &'static str 145..149 'None': Option<{unknown}> 159..160 'x': Option<i64> 176..180 'None': Option<i64> @@ -1405,15 +1405,15 @@ fn infer_impl_generics_with_autoderef() { } "#, expect![[r#" - 77..81 'self': &Option<T> - 97..99 '{}': Option<&T> + 77..81 'self': &'? Option<T> + 97..99 '{}': Option<&'? T> 110..111 'o': Option<u32> 126..164 '{ ...f(); }': () - 132..145 '(&o).as_ref()': Option<&u32> - 133..135 '&o': &Option<u32> + 132..145 '(&o).as_ref()': Option<&'? u32> + 133..135 '&o': &'? Option<u32> 134..135 'o': Option<u32> 151..152 'o': Option<u32> - 151..161 'o.as_ref()': Option<&u32> + 151..161 'o.as_ref()': Option<&'? u32> "#]], ); } @@ -1551,16 +1551,16 @@ fn infer_type_alias() { "#, expect![[r#" 115..116 'x': A<u32, i128> - 123..124 'y': A<&str, u128> + 123..124 'y': A<&'? str, u128> 137..138 'z': A<u8, i8> 153..210 '{ ...z.y; }': () 159..160 'x': A<u32, i128> 159..162 'x.x': u32 168..169 'x': A<u32, i128> 168..171 'x.y': i128 - 177..178 'y': A<&str, u128> - 177..180 'y.x': &str - 186..187 'y': A<&str, u128> + 177..178 'y': A<&'? str, u128> + 177..180 'y.x': &'? str + 186..187 'y': A<&'? str, u128> 186..189 'y.y': u128 195..196 'z': A<u8, i8> 195..198 'z.x': u8 @@ -1572,8 +1572,8 @@ fn infer_type_alias() { 312..328 'm::Ali...Foo(0)': Enum 326..327 '0': u8 338..354 'm::Ali...Foo(x)': Enum - 352..353 'x': &u8 - 357..359 '&e': &Enum + 352..353 'x': &'? u8 + 357..359 '&e': &'? Enum 358..359 'e': Enum "#]], ) @@ -1618,10 +1618,10 @@ fn infer_type_param() { 9..10 'x': T 20..29 '{ x }': T 26..27 'x': T - 43..44 'x': &T + 43..44 'x': &'? T 55..65 '{ *x }': T 61..63 '*x': T - 62..63 'x': &T + 62..63 'x': &'? T 77..157 '{ ...(1); }': () 87..88 'y': u32 91..96 '10u32': u32 @@ -1629,9 +1629,9 @@ fn infer_type_param() { 102..107 'id(y)': u32 105..106 'y': u32 117..118 'x': bool - 127..132 'clone': fn clone<bool>(&bool) -> bool + 127..132 'clone': fn clone<bool>(&'? bool) -> bool 127..135 'clone(z)': bool - 133..134 'z': &bool + 133..134 'z': &'? bool 141..151 'id::<i128>': fn id<i128>(i128) -> i128 141..154 'id::<i128>(1)': i128 152..153 '1': i128 @@ -1842,7 +1842,7 @@ fn foo() -> &'static str { "" } fn main() { foo(); - //^^^^^ &str + //^^^^^ &'static str }"#, ); } @@ -1940,10 +1940,10 @@ fn closure_return_inferred() { "#, expect![[r#" 16..46 '{ ..." }; }': u32 - 26..27 'x': impl Fn() -> &str - 30..43 '|| { "test" }': impl Fn() -> &str - 33..43 '{ "test" }': &str - 35..41 '"test"': &str + 26..27 'x': impl Fn() -> &'static str + 30..43 '|| { "test" }': impl Fn() -> &'static str + 33..43 '{ "test" }': &'static str + 35..41 '"test"': &'static str "#]], ); } @@ -1975,10 +1975,10 @@ fn test() { 70..71 'v': i64 78..80 '{}': () 91..362 '{ ... } }': () - 101..106 'mut g': |usize| yields i64 -> &str - 109..218 '|r| { ... }': |usize| yields i64 -> &str + 101..106 'mut g': |usize| yields i64 -> &'static str + 109..218 '|r| { ... }': |usize| yields i64 -> &'static str 110..111 'r': usize - 113..218 '{ ... }': &str + 113..218 '{ ... }': &'static str 127..128 'a': usize 131..138 'yield 0': usize 137..138 '0': i64 @@ -1988,22 +1988,22 @@ fn test() { 177..178 'a': usize 181..188 'yield 2': usize 187..188 '2': i64 - 198..212 '"return value"': &str + 198..212 '"return value"': &'static str 225..360 'match ... }': () - 231..239 'Pin::new': fn new<&mut |usize| yields i64 -> &str>(&mut |usize| yields i64 -> &str) -> Pin<&mut |usize| yields i64 -> &str> - 231..247 'Pin::n...mut g)': Pin<&mut |usize| yields i64 -> &str> - 231..262 'Pin::n...usize)': CoroutineState<i64, &str> - 240..246 '&mut g': &mut |usize| yields i64 -> &str - 245..246 'g': |usize| yields i64 -> &str + 231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'static str>(&'? mut |usize| yields i64 -> &'static str) -> Pin<&'? mut |usize| yields i64 -> &'static str> + 231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'static str> + 231..262 'Pin::n...usize)': CoroutineState<i64, &'static str> + 240..246 '&mut g': &'? mut |usize| yields i64 -> &'static str + 245..246 'g': |usize| yields i64 -> &'static str 255..261 '0usize': usize - 273..299 'Corout...ded(y)': CoroutineState<i64, &str> + 273..299 'Corout...ded(y)': CoroutineState<i64, &'static str> 297..298 'y': i64 303..312 '{ f(y); }': () 305..306 'f': fn f(i64) 305..309 'f(y)': () 307..308 'y': i64 - 321..348 'Corout...ete(r)': CoroutineState<i64, &str> - 346..347 'r': &str + 321..348 'Corout...ete(r)': CoroutineState<i64, &'static str> + 346..347 'r': &'static str 352..354 '{}': () "#]], ); @@ -2050,7 +2050,7 @@ fn f(x: (&&&&i32, &&&i32)) { _ => loop {}, }; f; - //^ (&&&&i32, &&&i32) + //^ (&'? &'? &'? &'? i32, &'? &'? &'? i32) } "#, ); @@ -2059,10 +2059,10 @@ fn f(x: (&&&&i32, &&&i32)) { fn f() { let x = &&&(&&&2, &&&&&3); let (y, z) = x; - //^ &&&&i32 + //^ &'? &'? &'? &'? i32 let t @ (y, z) = x; t; - //^ &&&(&&&i32, &&&&&i32) + //^ &'? &'? &'? (&'? &'? &'? i32, &'? &'? &'? &'? &'? i32) } "#, ); @@ -2071,10 +2071,10 @@ fn f() { fn f() { let x = &&&(&&&2, &&&&&3); let (y, z) = x; - //^ &&&&i32 + //^ &'? &'? &'? &'? i32 let t @ (y, z) = x; t; - //^ &&&(&&&i32, &&&&&i32) + //^ &'? &'? &'? (&'? &'? &'? i32, &'? &'? &'? &'? &'? i32) } "#, ); @@ -2761,23 +2761,23 @@ impl S { fn f() { let x = S; let c1 = || x.read(); - //^^ impl Fn() -> &S + //^^ impl Fn() -> &'? S let c2 = || x.write(); - //^^ impl FnMut() -> &mut S + //^^ impl FnMut() -> &'? mut S let c3 = || x.consume(); //^^ impl FnOnce() -> S let c3 = || x.consume().consume().consume(); //^^ impl FnOnce() -> S let c3 = || x.consume().write().read(); - //^^ impl FnOnce() -> &S + //^^ impl FnOnce() -> &'? S let x = &mut x; let c1 = || x.write(); - //^^ impl FnMut() -> &mut S + //^^ impl FnMut() -> &'? mut S let x = S; let c1 = || { let ref t = x; t }; - //^^ impl Fn() -> &S + //^^ impl Fn() -> &'? S let c2 = || { let ref mut t = x; t }; - //^^ impl FnMut() -> &mut S + //^^ impl FnMut() -> &'? mut S let c3 = || { let t = x; t }; //^^ impl FnOnce() -> S } @@ -3074,11 +3074,11 @@ fn main() { } "#, expect![[r#" - 104..108 'self': &Box<T> - 188..192 'self': &Box<Foo<T>> - 218..220 '{}': &T - 242..246 'self': &Box<Foo<T>> - 275..277 '{}': &Foo<T> + 104..108 'self': &'? Box<T> + 188..192 'self': &'a Box<Foo<T>> + 218..220 '{}': &'a T + 242..246 'self': &'a Box<Foo<T>> + 275..277 '{}': &'a Foo<T> 297..301 'self': Box<Foo<T>> 322..324 '{}': Foo<T> 338..559 '{ ...r(); }': () @@ -3088,21 +3088,21 @@ fn main() { 360..363 'Foo': extern "rust-call" Foo<i32>(i32) -> Foo<i32> 360..370 'Foo(0_i32)': Foo<i32> 364..369 '0_i32': i32 - 382..386 'bad1': &i32 + 382..386 'bad1': &'? i32 389..394 'boxed': Box<Foo<i32>> - 389..406 'boxed....nner()': &i32 - 416..421 'good1': &i32 - 424..438 'Foo::get_inner': fn get_inner<i32, '{error}>(&Box<Foo<i32>>) -> &i32 - 424..446 'Foo::g...boxed)': &i32 - 439..445 '&boxed': &Box<Foo<i32>> + 389..406 'boxed....nner()': &'? i32 + 416..421 'good1': &'? i32 + 424..438 'Foo::get_inner': fn get_inner<i32, '?>(&'? Box<Foo<i32>>) -> &'? i32 + 424..446 'Foo::g...boxed)': &'? i32 + 439..445 '&boxed': &'? Box<Foo<i32>> 440..445 'boxed': Box<Foo<i32>> - 457..461 'bad2': &Foo<i32> + 457..461 'bad2': &'? Foo<i32> 464..469 'boxed': Box<Foo<i32>> - 464..480 'boxed....self()': &Foo<i32> - 490..495 'good2': &Foo<i32> - 498..511 'Foo::get_self': fn get_self<i32, '{error}>(&Box<Foo<i32>>) -> &Foo<i32> - 498..519 'Foo::g...boxed)': &Foo<i32> - 512..518 '&boxed': &Box<Foo<i32>> + 464..480 'boxed....self()': &'? Foo<i32> + 490..495 'good2': &'? Foo<i32> + 498..511 'Foo::get_self': fn get_self<i32, '?>(&'? Box<Foo<i32>>) -> &'? Foo<i32> + 498..519 'Foo::g...boxed)': &'? Foo<i32> + 512..518 '&boxed': &'? Box<Foo<i32>> 513..518 'boxed': Box<Foo<i32>> 530..535 'inner': Foo<i32> 538..543 'boxed': Box<Foo<i32>> @@ -3414,31 +3414,31 @@ struct TS(usize); fn main() { let x; [x,] = &[1,]; - //^^^^expected &[i32; 1], got [{unknown}; _] + //^^^^expected &'? [i32; 1], got [{unknown}; _] let x; [(x,),] = &[(1,),]; - //^^^^^^^expected &[(i32,); 1], got [{unknown}; _] + //^^^^^^^expected &'? [(i32,); 1], got [{unknown}; _] let x; ((x,),) = &((1,),); - //^^^^^^^expected &((i32,),), got (({unknown},),) + //^^^^^^^expected &'? ((i32,),), got (({unknown},),) let x; (x,) = &(1,); - //^^^^expected &(i32,), got ({unknown},) + //^^^^expected &'? (i32,), got ({unknown},) let x; (S { a: x },) = &(S { a: 42 },); - //^^^^^^^^^^^^^expected &(S,), got (S,) + //^^^^^^^^^^^^^expected &'? (S,), got (S,) let x; S { a: x } = &S { a: 42 }; - //^^^^^^^^^^expected &S, got S + //^^^^^^^^^^expected &'? S, got S let x; TS(x) = &TS(42); - //^^^^^expected &TS, got TS + //^^^^^expected &'? TS, got TS } "#, ); @@ -3548,17 +3548,17 @@ fn f<T>(t: Ark<T>) { } "#, expect![[r#" - 47..51 'self': &Ark<T> + 47..51 'self': &'? Ark<T> 65..88 '{ ... }': *const T - 75..82 '&self.0': &T - 76..80 'self': &Ark<T> + 75..82 '&self.0': &'? T + 76..80 'self': &'? Ark<T> 76..82 'self.0': T 99..100 't': Ark<T> 110..144 '{ ... (); }': () - 116..124 'Ark::foo': fn foo<T>(&Ark<T>) -> *const T + 116..124 'Ark::foo': fn foo<T>(&'? Ark<T>) -> *const T 116..128 'Ark::foo(&t)': *const T 116..141 'Ark::f...nst ()': *const () - 125..127 '&t': &Ark<T> + 125..127 '&t': &'? Ark<T> 126..127 't': Ark<T> "#]], ); @@ -3632,7 +3632,7 @@ pub struct CStr; fn main() { c"ello"; - //^^^^^^^ &CStr + //^^^^^^^ &'static CStr } "#, ); @@ -3659,7 +3659,25 @@ fn main() { let are = "are"; let count = 10; builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'{error}> + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'?> +} +"#, + ); +} + +#[test] +fn inline_const_expression() { + check( + r#" +fn main() { + let foo = 0; + const { + let bar = 1; + let unresolved = foo; + // ^^^^^^^^^^ type: {unknown} + let resolved = bar; + // ^^^^^^^^ type: i32 + } } "#, ); diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index dfcd322a39..18fc8afd18 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -238,6 +238,7 @@ fn infer_for_loop() { //- minicore: iterator //- /main.rs crate:main deps:alloc #![no_std] +extern crate alloc; use alloc::collections::Vec; fn test() { @@ -245,7 +246,7 @@ fn test() { v.push("foo"); for x in v { x; - } //^ &str + } //^ &'static str } //- /alloc.rs crate:alloc @@ -575,7 +576,7 @@ fn indexing_arrays() { "fn main() { &mut [9][2]; }", expect![[r#" 10..26 '{ &mut...[2]; }': () - 12..23 '&mut [9][2]': &mut {unknown} + 12..23 '&mut [9][2]': &'? mut {unknown} 17..20 '[9]': [i32; 1] 17..23 '[9][2]': {unknown} 18..19 '9': i32 @@ -873,7 +874,7 @@ impl<U, T: Trait<U>> O<T> { fn test(o: O<S>) { o.foo(); -} //^^^^^^^ &str +} //^^^^^^^ &'? str "#, ); } @@ -1016,15 +1017,15 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u32>) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &Self - 54..58 'self': &Self + 29..33 'self': &'? Self + 54..58 'self': &'? Self 77..78 'x': impl Trait<u16> 97..99 '{}': () 154..155 'x': impl Trait<u64> - 174..175 'y': &impl Trait<u32> + 174..175 'y': &'? impl Trait<u32> 195..323 '{ ...2(); }': () 201..202 'x': impl Trait<u64> - 208..209 'y': &impl Trait<u32> + 208..209 'y': &'? impl Trait<u32> 219..220 'z': S<u16> 223..224 'S': extern "rust-call" S<u16>(u16) -> S<u16> 223..227 'S(1)': S<u16> @@ -1034,13 +1035,13 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u32>) { 237..238 'z': S<u16> 245..246 'x': impl Trait<u64> 245..252 'x.foo()': u64 - 258..259 'y': &impl Trait<u32> + 258..259 'y': &'? impl Trait<u32> 258..265 'y.foo()': u32 271..272 'z': S<u16> 271..278 'z.foo()': u16 284..285 'x': impl Trait<u64> 284..292 'x.foo2()': i64 - 298..299 'y': &impl Trait<u32> + 298..299 'y': &'? impl Trait<u32> 298..306 'y.foo2()': i64 312..313 'z': S<u16> 312..320 'z.foo2()': i64 @@ -1204,26 +1205,26 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u64>) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &Self - 54..58 'self': &Self + 29..33 'self': &'? Self + 54..58 'self': &'? Self 98..100 '{}': () 110..111 'x': impl Trait<u64> - 130..131 'y': &impl Trait<u64> + 130..131 'y': &'? impl Trait<u64> 151..268 '{ ...2(); }': () 157..158 'x': impl Trait<u64> - 164..165 'y': &impl Trait<u64> + 164..165 'y': &'? impl Trait<u64> 175..176 'z': impl Trait<u64> 179..182 'bar': fn bar() -> impl Trait<u64> 179..184 'bar()': impl Trait<u64> 190..191 'x': impl Trait<u64> 190..197 'x.foo()': u64 - 203..204 'y': &impl Trait<u64> + 203..204 'y': &'? impl Trait<u64> 203..210 'y.foo()': u64 216..217 'z': impl Trait<u64> 216..223 'z.foo()': u64 229..230 'x': impl Trait<u64> 229..237 'x.foo2()': i64 - 243..244 'y': &impl Trait<u64> + 243..244 'y': &'? impl Trait<u64> 243..251 'y.foo2()': i64 257..258 'z': impl Trait<u64> 257..265 'z.foo2()': i64 @@ -1328,7 +1329,7 @@ fn test() { a.foo(); }"#, expect![[r#" - 29..33 'self': &Self + 29..33 'self': &'? Self 71..82 '{ loop {} }': ! 73..80 'loop {}': ! 78..80 '{}': () @@ -1366,8 +1367,8 @@ fn test() { d.foo(); }"#, expect![[r#" - 49..53 'self': &mut Self - 101..105 'self': &Self + 49..53 'self': &'? mut Self + 101..105 'self': &'? Self 184..195 '{ loop {} }': ({unknown}, {unknown}) 186..193 'loop {}': ! 191..193 '{}': () @@ -1414,10 +1415,10 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) { } "#, expect![[r#" - 134..165 '{ ...(C)) }': (impl FnOnce(&str, T), Bar<u8>) - 140..163 '(|inpu...ar(C))': (impl FnOnce(&str, T), Bar<u8>) - 141..154 '|input, t| {}': impl FnOnce(&str, T) - 142..147 'input': &str + 134..165 '{ ...(C)) }': (impl FnOnce(&'? str, T), Bar<u8>) + 140..163 '(|inpu...ar(C))': (impl FnOnce(&'? str, T), Bar<u8>) + 141..154 '|input, t| {}': impl FnOnce(&'? str, T) + 142..147 'input': &'? str 149..150 't': T 152..154 '{}': () 156..159 'Bar': extern "rust-call" Bar<u8>(u8) -> Bar<u8> @@ -1466,26 +1467,26 @@ fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) { z.foo2(); }"#, expect![[r#" - 29..33 'self': &Self - 54..58 'self': &Self + 29..33 'self': &'? Self + 54..58 'self': &'? Self 97..99 '{}': dyn Trait<u64> 109..110 'x': dyn Trait<u64> - 128..129 'y': &dyn Trait<u64> + 128..129 'y': &'? dyn Trait<u64> 148..265 '{ ...2(); }': () 154..155 'x': dyn Trait<u64> - 161..162 'y': &dyn Trait<u64> + 161..162 'y': &'? dyn Trait<u64> 172..173 'z': dyn Trait<u64> 176..179 'bar': fn bar() -> dyn Trait<u64> 176..181 'bar()': dyn Trait<u64> 187..188 'x': dyn Trait<u64> 187..194 'x.foo()': u64 - 200..201 'y': &dyn Trait<u64> + 200..201 'y': &'? dyn Trait<u64> 200..207 'y.foo()': u64 213..214 'z': dyn Trait<u64> 213..220 'z.foo()': u64 226..227 'x': dyn Trait<u64> 226..234 'x.foo2()': i64 - 240..241 'y': &dyn Trait<u64> + 240..241 'y': &'? dyn Trait<u64> 240..248 'y.foo2()': i64 254..255 'z': dyn Trait<u64> 254..262 'z.foo2()': i64 @@ -1514,16 +1515,16 @@ fn test(s: S<u32, i32>) { s.bar().baz(); }"#, expect![[r#" - 32..36 'self': &Self - 102..106 'self': &S<T, U> - 128..139 '{ loop {} }': &dyn Trait<T, U> + 32..36 'self': &'? Self + 102..106 'self': &'? S<T, U> + 128..139 '{ loop {} }': &'? dyn Trait<T, U> 130..137 'loop {}': ! 135..137 '{}': () - 175..179 'self': &Self + 175..179 'self': &'? Self 251..252 's': S<u32, i32> 267..289 '{ ...z(); }': () 273..274 's': S<u32, i32> - 273..280 's.bar()': &dyn Trait<u32, i32> + 273..280 's.bar()': &'? dyn Trait<u32, i32> 273..286 's.bar().baz()': (u32, i32) "#]], ); @@ -1548,19 +1549,19 @@ fn test(x: Trait, y: &Trait) -> u64 { z.foo(); }"#, expect![[r#" - 26..30 'self': &Self + 26..30 'self': &'? Self 60..62 '{}': dyn Trait 72..73 'x': dyn Trait - 82..83 'y': &dyn Trait + 82..83 'y': &'? dyn Trait 100..175 '{ ...o(); }': u64 106..107 'x': dyn Trait - 113..114 'y': &dyn Trait + 113..114 'y': &'? dyn Trait 124..125 'z': dyn Trait 128..131 'bar': fn bar() -> dyn Trait 128..133 'bar()': dyn Trait 139..140 'x': dyn Trait 139..146 'x.foo()': u64 - 152..153 'y': &dyn Trait + 152..153 'y': &'? dyn Trait 152..159 'y.foo()': u64 165..166 'z': dyn Trait 165..172 'z.foo()': u64 @@ -1580,14 +1581,14 @@ fn main() { } "#, expect![[r#" - 31..35 'self': &S + 31..35 'self': &'? S 37..39 '{}': () - 47..48 '_': &dyn Fn(S) + 47..48 '_': &'? dyn Fn(S) 58..60 '{}': () 71..105 '{ ...()); }': () - 77..78 'f': fn f(&dyn Fn(S)) + 77..78 'f': fn f(&'? dyn Fn(S)) 77..102 'f(&|nu...foo())': () - 79..101 '&|numb....foo()': &impl Fn(S) + 79..101 '&|numb....foo()': &'? impl Fn(S) 80..101 '|numbe....foo()': impl Fn(S) 81..87 'number': S 89..95 'number': S @@ -1790,7 +1791,7 @@ fn test<T: Trait1, U: Trait2>(x: T, y: U) { y.foo(); }"#, expect![[r#" - 53..57 'self': &Self + 53..57 'self': &'? Self 66..68 '{}': u32 185..186 'x': T 191..192 'y': U @@ -1819,11 +1820,11 @@ fn test(x: &impl Trait1) { x.foo(); }"#, expect![[r#" - 53..57 'self': &Self + 53..57 'self': &'? Self 66..68 '{}': u32 - 119..120 'x': &impl Trait1 + 119..120 'x': &'? impl Trait1 136..152 '{ ...o(); }': () - 142..143 'x': &impl Trait1 + 142..143 'x': &'? impl Trait1 142..149 'x.foo()': u32 "#]], ); @@ -1934,8 +1935,8 @@ fn test() { opt.map(f); }"#, expect![[r#" - 28..32 'self': &Self - 132..136 'self': &Bar<F> + 28..32 'self': &'? Self + 132..136 'self': &'? Bar<F> 149..160 '{ loop {} }': (A1, R) 151..158 'loop {}': ! 156..158 '{}': () @@ -1988,7 +1989,7 @@ fn test() { let r2 = lazy2.foo(); }"#, expect![[r#" - 36..40 'self': &Foo + 36..40 'self': &'? Foo 51..53 '{}': usize 131..132 'f': F 151..153 '{}': Lazy<T, F> @@ -2262,14 +2263,14 @@ impl Trait for S2 { fn f(&self, x: <Self>::Item) { let y = x; } }"#, expect![[r#" - 40..44 'self': &Self + 40..44 'self': &'? Self 46..47 'x': Trait::Item<Self> - 126..130 'self': &S + 126..130 'self': &'? S 132..133 'x': u32 147..161 '{ let y = x; }': () 153..154 'y': u32 157..158 'x': u32 - 228..232 'self': &S2 + 228..232 'self': &'? S2 234..235 'x': i32 251..265 '{ let y = x; }': () 257..258 'y': i32 @@ -2643,12 +2644,12 @@ fn main() { 72..74 '_v': F 117..120 '{ }': () 132..163 '{ ... }); }': () - 138..148 'f::<(), _>': fn f<(), impl FnOnce(&())>(impl FnOnce(&())) + 138..148 'f::<(), _>': fn f<(), impl FnOnce(&'? ())>(impl FnOnce(&'? ())) 138..160 'f::<()... z; })': () - 149..159 '|z| { z; }': impl FnOnce(&()) - 150..151 'z': &() + 149..159 '|z| { z; }': impl FnOnce(&'? ()) + 150..151 'z': &'? () 153..159 '{ z; }': () - 155..156 'z': &() + 155..156 'z': &'? () "#]], ); } @@ -2897,13 +2898,13 @@ fn test(x: &dyn Foo) { foo(x); }"#, expect![[r#" - 21..22 'x': &dyn Foo + 21..22 'x': &'? dyn Foo 34..36 '{}': () - 46..47 'x': &dyn Foo + 46..47 'x': &'? dyn Foo 59..74 '{ foo(x); }': () - 65..68 'foo': fn foo(&dyn Foo) + 65..68 'foo': fn foo(&'? dyn Foo) 65..71 'foo(x)': () - 69..70 'x': &dyn Foo + 69..70 'x': &'? dyn Foo "#]], ); } @@ -2927,7 +2928,7 @@ fn test() { (IsCopy, NotCopy).test(); }"#, expect![[r#" - 78..82 'self': &Self + 78..82 'self': &'? Self 134..235 '{ ...t(); }': () 140..146 'IsCopy': IsCopy 140..153 'IsCopy.test()': bool @@ -2969,7 +2970,7 @@ fn test() { 28..29 'T': {unknown} 36..38 '{}': T 36..38: expected T, got () - 113..117 'self': &Self + 113..117 'self': &'? Self 169..249 '{ ...t(); }': () 175..178 'foo': fn foo() 175..185 'foo.test()': bool @@ -2997,16 +2998,16 @@ fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) { f3.test(); }"#, expect![[r#" - 22..26 'self': &Self + 22..26 'self': &'? Self 76..78 'f1': fn() 86..88 'f2': fn(usize) -> u8 - 107..109 'f3': fn(u8, u8) -> &u8 + 107..109 'f3': fn(u8, u8) -> &'? u8 130..178 '{ ...t(); }': () 136..138 'f1': fn() 136..145 'f1.test()': bool 151..153 'f2': fn(usize) -> u8 151..160 'f2.test()': bool - 166..168 'f3': fn(u8, u8) -> &u8 + 166..168 'f3': fn(u8, u8) -> &'? u8 166..175 'f3.test()': bool "#]], ); @@ -3027,13 +3028,13 @@ fn test() { (1u8, *"foo").test(); // not Sized }"#, expect![[r#" - 22..26 'self': &Self + 22..26 'self': &'? Self 79..194 '{ ...ized }': () 85..88 '1u8': u8 85..95 '1u8.test()': bool 101..116 '(*"foo").test()': {unknown} 102..108 '*"foo"': str - 103..108 '"foo"': &str + 103..108 '"foo"': &'static str 135..145 '(1u8, 1u8)': (u8, u8) 135..152 '(1u8, ...test()': bool 136..139 '1u8': u8 @@ -3042,7 +3043,7 @@ fn test() { 158..178 '(1u8, ...test()': {unknown} 159..162 '1u8': u8 164..170 '*"foo"': str - 165..170 '"foo"': &str + 165..170 '"foo"': &'static str "#]], ); } @@ -3093,7 +3094,7 @@ fn foo() { 93..94 'x': Option<i32> 109..111 '{}': () 117..124 '(&f)(s)': () - 118..120 '&f': &impl Fn(Option<i32>) + 118..120 '&f': &'? impl Fn(Option<i32>) 119..120 'f': impl Fn(Option<i32>) 122..123 's': Option<i32> "#]], @@ -3170,25 +3171,25 @@ fn foo() { f(&s); }"#, expect![[r#" - 154..158 'self': &Box<T> - 166..205 '{ ... }': &T - 176..199 'unsafe...nner }': &T - 185..197 '&*self.inner': &T + 154..158 'self': &'? Box<T> + 166..205 '{ ... }': &'? T + 176..199 'unsafe...nner }': &'? T + 185..197 '&*self.inner': &'? T 186..197 '*self.inner': T - 187..191 'self': &Box<T> + 187..191 'self': &'? Box<T> 187..197 'self.inner': *mut T 218..324 '{ ...&s); }': () 228..229 's': Option<i32> 232..236 'None': Option<i32> - 246..247 'f': Box<dyn FnOnce(&Option<i32>)> - 281..310 'Box { ... {}) }': Box<dyn FnOnce(&Option<i32>)> - 294..308 '&mut (|ps| {})': &mut impl FnOnce(&Option<i32>) - 300..307 '|ps| {}': impl FnOnce(&Option<i32>) - 301..303 'ps': &Option<i32> + 246..247 'f': Box<dyn FnOnce(&'? Option<i32>)> + 281..310 'Box { ... {}) }': Box<dyn FnOnce(&'? Option<i32>)> + 294..308 '&mut (|ps| {})': &'? mut impl FnOnce(&'? Option<i32>) + 300..307 '|ps| {}': impl FnOnce(&'? Option<i32>) + 301..303 'ps': &'? Option<i32> 305..307 '{}': () - 316..317 'f': Box<dyn FnOnce(&Option<i32>)> + 316..317 'f': Box<dyn FnOnce(&'? Option<i32>)> 316..321 'f(&s)': () - 318..320 '&s': &Option<i32> + 318..320 '&s': &'? Option<i32> 319..320 's': Option<i32> "#]], ); @@ -3320,7 +3321,7 @@ fn f() { } }"#, expect![[r#" - 46..50 'self': &Self + 46..50 'self': &'? Self 58..63 '{ 0 }': u8 60..61 '0': u8 115..185 '{ ... } }': () @@ -3595,7 +3596,7 @@ fn take_u32(_: u32) {} fn minimized() { let v = V::default(); let p = v.get(&0); - //^ &u32 + //^ &'? u32 take_u32(42 + p); } "#, @@ -3625,7 +3626,7 @@ fn take_u32(_: u32) {} fn minimized() { let v = V::default(); let p = v.get(); - //^ &{unknown} + //^ &'? {unknown} take_u32(42 + p); } "#, @@ -3684,11 +3685,11 @@ fn main() { } "#, expect![[r#" - 44..48 'self': &Self - 133..137 'self': &[u8; 4] + 44..48 'self': &'? Self + 133..137 'self': &'? [u8; 4] 155..172 '{ ... }': usize 165..166 '2': usize - 236..240 'self': &[u8; 2] + 236..240 'self': &'? [u8; 2] 258..275 '{ ... }': u8 268..269 '2': u8 289..392 '{ ...g(); }': () @@ -3732,11 +3733,11 @@ fn main() { } "#, expect![[r#" - 44..48 'self': &Self - 151..155 'self': &[u8; L] + 44..48 'self': &'? Self + 151..155 'self': &'? [u8; L] 173..194 '{ ... }': [u8; L] 183..188 '*self': [u8; L] - 184..188 'self': &[u8; L] + 184..188 'self': &'? [u8; L] 208..260 '{ ...g(); }': () 218..219 'v': [u8; 2] 222..230 '[0u8; 2]': [u8; 2] @@ -4056,13 +4057,13 @@ fn g(t: &(dyn Sync + T2 + T1 + Send)) { } "#, expect![[r#" - 68..69 't': &{unknown} + 68..69 't': &'? {unknown} 101..103 '{}': () - 109..110 't': &{unknown} + 109..110 't': &'? {unknown} 142..155 '{ f(t); }': () - 148..149 'f': fn f(&{unknown}) + 148..149 'f': fn f(&'? {unknown}) 148..152 'f(t)': () - 150..151 't': &{unknown} + 150..151 't': &'? {unknown} "#]], ); @@ -4105,7 +4106,7 @@ trait Trait { } fn f(t: &dyn Trait<T = (), T = ()>) {} - //^&{unknown} + //^&'? {unknown} "#, ); } @@ -4175,27 +4176,27 @@ trait Trait { fn f<T>(v: impl Trait) { let a = v.get::<i32>().deref(); - //^ &i32 + //^ &'? i32 let a = v.get::<T>().deref(); - //^ &T + //^ &'? T } fn g<'a, T: 'a>(v: impl Trait<Assoc<T> = &'a T>) { let a = v.get::<T>(); - //^ &T + //^ &'a T let a = v.get::<()>(); - //^ Trait::Assoc<(), impl Trait<Assoc<T> = &T>> + //^ Trait::Assoc<(), impl Trait<Assoc<T> = &'a T>> } fn h<'a>(v: impl Trait<Assoc<i32> = &'a i32> + Trait<Assoc<i64> = &'a i64>) { let a = v.get::<i32>(); - //^ &i32 + //^ &'a i32 let a = v.get::<i64>(); - //^ &i64 + //^ &'a i64 } fn i<'a>(v: impl Trait<Assoc<i32> = &'a i32, Assoc<i64> = &'a i64>) { let a = v.get::<i32>(); - //^ &i32 + //^ &'a i32 let a = v.get::<i64>(); - //^ &i64 + //^ &'a i64 } "#, ); @@ -4221,12 +4222,12 @@ fn f<'a>(v: &dyn Trait<Assoc<i32> = &'a i32>) { } "#, expect![[r#" - 90..94 'self': &Self - 127..128 'v': &(dyn Trait<Assoc<i32> = &i32>) + 90..94 'self': &'? Self + 127..128 'v': &'? (dyn Trait<Assoc<i32> = &'a i32>) 164..195 '{ ...f(); }': () - 170..171 'v': &(dyn Trait<Assoc<i32> = &i32>) - 170..184 'v.get::<i32>()': &i32 - 170..192 'v.get:...eref()': &i32 + 170..171 'v': &'? (dyn Trait<Assoc<i32> = &'a i32>) + 170..184 'v.get::<i32>()': &'? i32 + 170..192 'v.get:...eref()': &'? i32 "#]], ); } @@ -4487,19 +4488,19 @@ fn derive_macro_bounds() { let x = (&Copy).clone(); //^ Copy let x = (&NotCopy).clone(); - //^ &NotCopy + //^ &'? NotCopy let x = (&Generic(Copy)).clone(); //^ Generic<Copy> let x = (&Generic(NotCopy)).clone(); - //^ &Generic<NotCopy> + //^ &'? Generic<NotCopy> let x: &AssocGeneric<Copy> = &AssocGeneric(NotCopy); let x = x.clone(); - //^ &AssocGeneric<Copy> + //^ &'? AssocGeneric<Copy> // let x: &AssocGeneric2<Copy> = &AssocGeneric2(NotCopy); // let x = x.clone(); let x: &AssocGeneric3<Copy> = &AssocGeneric3(Generic(NotCopy)); let x = x.clone(); - //^ &AssocGeneric3<Copy> + //^ &'? AssocGeneric3<Copy> let x = (&R1(Vec())).clone(); //^ R1 let x = (&R2(R1(Vec()))).clone(); @@ -4582,7 +4583,7 @@ impl B for u16 { fn ttt() { let inp = Y; x::<u16>(&inp); - //^^^^ expected &X, got &Y + //^^^^ expected &'? X, got &'? Y } "#, ); @@ -4629,7 +4630,7 @@ fn foo() { let mut map = SomeMap; map["a"] = (); map; - //^^^ SomeMap<&str> + //^^^ SomeMap<&'static str> } "#, ); @@ -4764,3 +4765,62 @@ fn test() { "#, ); } + +#[test] +fn associated_type_with_impl_trait_in_tuple() { + check_no_mismatches( + r#" +pub trait Iterator { + type Item; +} + +pub trait Value {} + +fn bar<I: Iterator<Item = (usize, impl Value)>>() {} + +fn foo() { + bar(); +} +"#, + ); +} + +#[test] +fn associated_type_with_impl_trait_in_nested_tuple() { + check_no_mismatches( + r#" +pub trait Iterator { + type Item; +} + +pub trait Value {} + +fn bar<I: Iterator<Item = ((impl Value, usize), u32)>>() {} + +fn foo() { + bar(); +} +"#, + ); +} + +#[test] +fn dyn_trait_with_lifetime_in_rpit() { + check_types( + r#" +//- minicore: future +pub struct Box<T> {} + +trait Trait {} + +pub async fn foo_async<'a>() -> Box<dyn Trait + 'a> { + Box {} +} + +fn foo() { + foo_async(); + //^^^^^^^^^^^impl Future<Output = Box<dyn Trait>> + ?Sized +} +"#, + ) +} diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 4518422d27..72272934ab 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -9,9 +9,11 @@ use hir_ty::{db::HirDatabase, diagnostics::BodyValidationDiagnostic, InferenceDi use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; use either::Either; +pub use hir_def::VariantId; use hir_def::{body::SyntheticSyntax, hir::ExprOrPatId, path::ModPath, AssocItemId, DefWithBodyId}; use hir_expand::{name::Name, HirFileId, InFile}; use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange}; +use triomphe::Arc; use crate::{AssocItem, Field, Local, MacroKind, Trait, Type}; @@ -171,7 +173,7 @@ pub struct MacroError { pub struct MacroExpansionParseError { pub node: InFile<SyntaxNodePtr>, pub precise_location: Option<TextRange>, - pub errors: Box<[SyntaxError]>, + pub errors: Arc<[SyntaxError]>, } #[derive(Debug, Clone, Eq, PartialEq)] @@ -200,6 +202,7 @@ pub struct MalformedDerive { pub struct NoSuchField { pub field: InFile<AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>>, pub private: bool, + pub variant: VariantId, } #[derive(Debug)] @@ -525,7 +528,7 @@ impl AnyDiagnostic { source_map.pat_syntax(pat).inspect_err(|_| tracing::error!("synthetic syntax")).ok() }; Some(match d { - &InferenceDiagnostic::NoSuchField { field: expr, private } => { + &InferenceDiagnostic::NoSuchField { field: expr, private, variant } => { let expr_or_pat = match expr { ExprOrPatId::ExprId(expr) => { source_map.field_syntax(expr).map(AstPtr::wrap_left) @@ -534,7 +537,7 @@ impl AnyDiagnostic { source_map.pat_field_syntax(pat).map(AstPtr::wrap_right) } }; - NoSuchField { field: expr_or_pat, private }.into() + NoSuchField { field: expr_or_pat, private, variant }.into() } &InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into() diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 84f03d111f..c276e87786 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -188,28 +188,7 @@ impl HirDisplay for Struct { StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - let fields = self.fields(f.db); - let count = fields.len().min(limit); - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - if count == 0 { - if fields.is_empty() { - f.write_str("{}")?; - } else { - f.write_str("{ /* … */ }")?; - } - } else { - f.write_str(" {\n")?; - for field in &fields[..count] { - f.write_str(" ")?; - field.hir_fmt(f)?; - f.write_str(",\n")?; - } - - if fields.len() > count { - f.write_str(" /* … */\n")?; - } - f.write_str("}")?; - } + display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, @@ -226,18 +205,10 @@ impl HirDisplay for Enum { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id)); write_generic_params(def_id, f)?; - let has_where_clause = write_where_clause(def_id, f)?; - let variants = self.variants(f.db); - if !variants.is_empty() { - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - f.write_str("{\n")?; - for variant in variants { - f.write_str(" ")?; - variant.hir_fmt(f)?; - f.write_str(",\n")?; - } - f.write_str("}")?; + let has_where_clause = write_where_clause(def_id, f)?; + if let Some(limit) = f.entity_limit { + display_variants(&self.variants(f.db), has_where_clause, limit, f)?; } Ok(()) @@ -251,22 +222,102 @@ impl HirDisplay for Union { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id)); write_generic_params(def_id, f)?; + let has_where_clause = write_where_clause(def_id, f)?; + if let Some(limit) = f.entity_limit { + display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?; + } + Ok(()) + } +} + +fn display_fields( + fields: &[Field], + has_where_clause: bool, + limit: usize, + in_line: bool, + f: &mut HirFormatter<'_>, +) -> Result<(), HirDisplayError> { + let count = fields.len().min(limit); + let (indent, separator) = if in_line { ("", ' ') } else { (" ", '\n') }; + f.write_char(if !has_where_clause { ' ' } else { separator })?; + if count == 0 { + if fields.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{ /* … */ }")?; + } + } else { + f.write_char('{')?; - let fields = self.fields(f.db); if !fields.is_empty() { - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - f.write_str("{\n")?; - for field in self.fields(f.db) { - f.write_str(" ")?; + f.write_char(separator)?; + for field in &fields[..count] { + f.write_str(indent)?; field.hir_fmt(f)?; - f.write_str(",\n")?; + f.write_char(',')?; + f.write_char(separator)?; + } + + if fields.len() > count { + f.write_str(indent)?; + f.write_str("/* … */")?; + f.write_char(separator)?; } - f.write_str("}")?; } - Ok(()) + f.write_str("}")?; + } + + Ok(()) +} + +fn display_variants( + variants: &[Variant], + has_where_clause: bool, + limit: usize, + f: &mut HirFormatter<'_>, +) -> Result<(), HirDisplayError> { + let count = variants.len().min(limit); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + if count == 0 { + if variants.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{ /* … */ }")?; + } + } else { + f.write_str("{\n")?; + for variant in &variants[..count] { + f.write_str(" ")?; + write!(f, "{}", variant.name(f.db).display(f.db.upcast()))?; + match variant.kind(f.db) { + StructKind::Tuple => { + if variant.fields(f.db).is_empty() { + f.write_str("()")?; + } else { + f.write_str("( /* … */ )")?; + } + } + StructKind::Record => { + if variant.fields(f.db).is_empty() { + f.write_str(" {}")?; + } else { + f.write_str(" { /* … */ }")?; + } + } + StructKind::Unit => {} + } + f.write_str(",\n")?; + } + + if variants.len() > count { + f.write_str(" /* … */\n")?; + } + f.write_str("}")?; } + + Ok(()) } impl HirDisplay for Field { @@ -304,21 +355,10 @@ impl HirDisplay for Variant { } f.write_char(')')?; } - VariantData::Record(fields) => { - f.write_str(" {")?; - let mut first = true; - for (_, field) in fields.iter() { - if first { - first = false; - f.write_char(' ')?; - } else { - f.write_str(", ")?; - } - // Enum variant fields must be pub. - write!(f, "{}: ", field.name.display(f.db.upcast()))?; - field.type_ref.hir_fmt(f)?; + VariantData::Record(_) => { + if let Some(limit) = f.entity_limit { + display_fields(&self.fields(f.db), false, limit, true, f)?; } - f.write_str(" }")?; } } Ok(()) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index bcd94a611a..85f33a10fc 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -59,7 +59,9 @@ use hir_def::{ ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, }; -use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind}; +use hir_expand::{ + attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, AstId, MacroCallKind, ValueResult, +}; use hir_ty::{ all_super_traits, autoderef, check_orphan_rules, consteval::{try_const_usize, unknown_const_as_generic, ConstExt}, @@ -79,7 +81,7 @@ use hir_ty::{ use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; -use span::Edition; +use span::{Edition, MacroCallId}; use stdx::{impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasName}, @@ -559,6 +561,12 @@ impl Module { emit_def_diagnostic(db, acc, diag); } + if !self.id.is_block_module() { + // These are reported by the body of block modules + let scope = &def_map[self.id.local_id].scope; + scope.all_macro_calls().for_each(|it| macro_call_diagnostics(db, it, acc)); + } + for def in self.declarations(db) { match def { ModuleDef::Module(m) => { @@ -577,6 +585,10 @@ impl Module { item.diagnostics(db, acc, style_lints); } + t.all_macro_calls(db) + .iter() + .for_each(|&(_ast, call_id)| macro_call_diagnostics(db, call_id, acc)); + acc.extend(def.diagnostics(db, style_lints)) } ModuleDef::Adt(adt) => { @@ -621,6 +633,11 @@ impl Module { // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow continue; } + impl_def + .all_macro_calls(db) + .iter() + .for_each(|&(_ast, call_id)| macro_call_diagnostics(db, call_id, acc)); + let ast_id_map = db.ast_id_map(file_id); for diag in db.impl_data_with_diagnostics(impl_def.id).1.iter() { @@ -809,6 +826,37 @@ impl Module { } } +fn macro_call_diagnostics( + db: &dyn HirDatabase, + macro_call_id: MacroCallId, + acc: &mut Vec<AnyDiagnostic>, +) { + let Some(e) = db.parse_macro_expansion_error(macro_call_id) else { + return; + }; + let ValueResult { value: parse_errors, err } = &*e; + if let Some(err) = err { + let loc = db.lookup_intern_macro_call(macro_call_id); + let (node, precise_location, macro_name, kind) = precise_macro_call_location(&loc.kind, db); + let diag = match err { + &hir_expand::ExpandError::UnresolvedProcMacro(krate) => { + UnresolvedProcMacro { node, precise_location, macro_name, kind, krate }.into() + } + err => MacroError { node, precise_location, message: err.to_string() }.into(), + }; + acc.push(diag); + } + + if !parse_errors.is_empty() { + let loc = db.lookup_intern_macro_call(macro_call_id); + let (node, precise_location, _, _) = precise_macro_call_location(&loc.kind, db); + acc.push( + MacroExpansionParseError { node, precise_location, errors: parse_errors.clone() } + .into(), + ) + } +} + fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, m: Macro) { let id = db.macro_def(m.id); if let hir_expand::db::TokenExpander::DeclarativeMacro(expander) = db.macro_expander(id) { @@ -888,16 +936,6 @@ fn emit_def_diagnostic_( .into(), ); } - DefDiagnosticKind::MacroError { ast, message } => { - let (node, precise_location, _, _) = precise_macro_call_location(ast, db); - acc.push(MacroError { node, precise_location, message: message.clone() }.into()); - } - DefDiagnosticKind::MacroExpansionParseError { ast, errors } => { - let (node, precise_location, _, _) = precise_macro_call_location(ast, db); - acc.push( - MacroExpansionParseError { node, precise_location, errors: errors.clone() }.into(), - ); - } DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { let node = ast.to_node(db.upcast()); // Must have a name, otherwise we wouldn't emit it. @@ -1489,6 +1527,14 @@ impl Adt { .map(|arena| arena.1.clone()) } + pub fn as_struct(&self) -> Option<Struct> { + if let Self::Struct(v) = self { + Some(*v) + } else { + None + } + } + pub fn as_enum(&self) -> Option<Enum> { if let Self::Enum(v) = self { Some(*v) @@ -1636,6 +1682,10 @@ impl DefWithBody { Module { id: def_map.module_id(DefMap::ROOT) }.diagnostics(db, acc, style_lints); } + source_map + .macro_calls() + .for_each(|(_ast_id, call_id)| macro_call_diagnostics(db, call_id.macro_call_id, acc)); + for diag in source_map.diagnostics() { acc.push(match diag { BodyDiagnostic::InactiveCode { node, cfg, opts } => { @@ -2437,6 +2487,14 @@ impl Trait { .filter(|(_, ty)| !count_required_only || !ty.has_default()) .count() } + + fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> { + db.trait_data(self.id) + .macro_calls + .as_ref() + .map(|it| it.as_ref().clone().into_boxed_slice()) + .unwrap_or_default() + } } impl HasVisibility for Trait { @@ -2506,6 +2564,15 @@ impl HasVisibility for TypeAlias { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct StaticLifetime; + +impl StaticLifetime { + pub fn name(self) -> Name { + known::STATIC_LIFETIME + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct BuiltinType { pub(crate) inner: hir_def::builtin_type::BuiltinType, } @@ -2535,6 +2602,20 @@ impl BuiltinType { matches!(self.inner, hir_def::builtin_type::BuiltinType::Float(_)) } + pub fn is_f32(&self) -> bool { + matches!( + self.inner, + hir_def::builtin_type::BuiltinType::Float(hir_def::builtin_type::BuiltinFloat::F32) + ) + } + + pub fn is_f64(&self) -> bool { + matches!( + self.inner, + hir_def::builtin_type::BuiltinType::Float(hir_def::builtin_type::BuiltinFloat::F64) + ) + } + pub fn is_char(&self) -> bool { matches!(self.inner, hir_def::builtin_type::BuiltinType::Char) } @@ -3743,6 +3824,14 @@ impl Impl { pub fn check_orphan_rules(self, db: &dyn HirDatabase) -> bool { check_orphan_rules(db, self.id) } + + fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> { + db.impl_data(self.id) + .macro_calls + .as_ref() + .map(|it| it.as_ref().clone().into_boxed_slice()) + .unwrap_or_default() + } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -4495,7 +4584,8 @@ impl Type { name: Option<&Name>, mut callback: impl FnMut(Function) -> Option<T>, ) -> Option<T> { - let _p = tracing::span!(tracing::Level::INFO, "iterate_method_candidates").entered(); + let _p = + tracing::span!(tracing::Level::INFO, "iterate_method_candidates_with_traits").entered(); let mut slot = None; self.iterate_method_candidates_dyn( diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index e792e159ac..6c70cc4baf 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -131,7 +131,7 @@ pub struct SemanticsImpl<'db> { pub db: &'db dyn HirDatabase, s2d_cache: RefCell<SourceToDefCache>, /// Rootnode to HirFileId cache - cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>, + root_to_file_cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>, // These 2 caches are mainly useful for semantic highlighting as nothing else descends a lot of tokens // So we might wanna move them out into something specific for semantic highlighting expansion_info_cache: RefCell<FxHashMap<MacroFileId, ExpansionInfo>>, @@ -294,7 +294,7 @@ impl<'db> SemanticsImpl<'db> { SemanticsImpl { db, s2d_cache: Default::default(), - cache: Default::default(), + root_to_file_cache: Default::default(), expansion_info_cache: Default::default(), macro_call_cache: Default::default(), } @@ -690,6 +690,7 @@ impl<'db> SemanticsImpl<'db> { exp_info }); + // FIXME: uncached parse // Create the source analyzer for the macro call scope let Some(sa) = self.analyze_no_infer(&self.parse_or_expand(expansion_info.call_file())) else { @@ -722,7 +723,7 @@ impl<'db> SemanticsImpl<'db> { mut token: SyntaxToken, f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<()>, ) { - let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros").entered(); + let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros_impl").entered(); let (sa, span, file_id) = match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) { Some(sa) => match sa.file_id.file_id() { @@ -1025,6 +1026,7 @@ impl<'db> SemanticsImpl<'db> { None => { let call_node = file_id.macro_file()?.call_node(db); // cache the node + // FIXME: uncached parse self.parse_or_expand(call_node.file_id); Some(call_node) } @@ -1370,7 +1372,7 @@ impl<'db> SemanticsImpl<'db> { offset: Option<TextSize>, infer_body: bool, ) -> Option<SourceAnalyzer> { - let _p = tracing::span!(tracing::Level::INFO, "Semantics::analyze_impl").entered(); + let _p = tracing::span!(tracing::Level::INFO, "SemanticsImpl::analyze_impl").entered(); let node = self.find_file(node); let container = self.with_ctx(|ctx| ctx.find_container(node))?; @@ -1397,7 +1399,7 @@ impl<'db> SemanticsImpl<'db> { fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { assert!(root_node.parent().is_none()); - let mut cache = self.cache.borrow_mut(); + let mut cache = self.root_to_file_cache.borrow_mut(); let prev = cache.insert(root_node, file_id); assert!(prev.is_none() || prev == Some(file_id)) } @@ -1407,7 +1409,7 @@ impl<'db> SemanticsImpl<'db> { } fn lookup(&self, root_node: &SyntaxNode) -> Option<HirFileId> { - let cache = self.cache.borrow(); + let cache = self.root_to_file_cache.borrow(); cache.get(root_node).copied() } @@ -1427,7 +1429,7 @@ impl<'db> SemanticsImpl<'db> { known nodes: {}\n\n", node, root_node, - self.cache + self.root_to_file_cache .borrow() .keys() .map(|it| format!("{it:?}")) diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 434e4b5a0c..d2bd8b0e79 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -118,10 +118,10 @@ pub(super) struct SourceToDefCtx<'a, 'b> { impl SourceToDefCtx<'_, '_> { pub(super) fn file_to_def(&self, file: FileId) -> SmallVec<[ModuleId; 1]> { - let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def").entered(); + let _p = tracing::span!(tracing::Level::INFO, "SourceToDefCtx::file_to_def").entered(); let mut mods = SmallVec::new(); for &crate_id in self.db.relevant_crates(file).iter() { - // FIXME: inner items + // Note: `mod` declarations in block modules cannot be supported here let crate_def_map = self.db.crate_def_map(crate_id); mods.extend( crate_def_map @@ -129,6 +129,9 @@ impl SourceToDefCtx<'_, '_> { .map(|local_id| crate_def_map.module_id(local_id)), ) } + if mods.is_empty() { + // FIXME: detached file + } mods } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index dc96a1b03d..057b03baef 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -28,7 +28,7 @@ use hir_expand::{ mod_path::path, name, name::{AsName, Name}, - HirFileId, InFile, MacroFileId, MacroFileIdExt, + HirFileId, InFile, InMacroFile, MacroFileId, MacroFileIdExt, }; use hir_ty::{ diagnostics::{ @@ -118,7 +118,7 @@ impl SourceAnalyzer { fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprId> { let src = match expr { ast::Expr::MacroExpr(expr) => { - self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?))? + self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?))?.into() } _ => InFile::new(self.file_id, expr.clone()), }; @@ -145,20 +145,20 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, expr: InFile<ast::MacroCall>, - ) -> Option<InFile<ast::Expr>> { + ) -> Option<InMacroFile<ast::Expr>> { let macro_file = self.body_source_map()?.node_macro_file(expr.as_ref())?; - let expanded = db.parse_or_expand(macro_file); + let expanded = db.parse_macro_expansion(macro_file).value.0.syntax_node(); let res = if let Some(stmts) = ast::MacroStmts::cast(expanded.clone()) { match stmts.expr()? { ast::Expr::MacroExpr(mac) => { - self.expand_expr(db, InFile::new(macro_file, mac.macro_call()?))? + self.expand_expr(db, InFile::new(macro_file.into(), mac.macro_call()?))? } - expr => InFile::new(macro_file, expr), + expr => InMacroFile::new(macro_file, expr), } } else if let Some(call) = ast::MacroCall::cast(expanded.clone()) { - self.expand_expr(db, InFile::new(macro_file, call))? + self.expand_expr(db, InFile::new(macro_file.into(), call))? } else { - InFile::new(macro_file, ast::Expr::cast(expanded)?) + InMacroFile::new(macro_file, ast::Expr::cast(expanded)?) }; Some(res) diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs index 93e7300491..5c5ddae19e 100644 --- a/crates/hir/src/term_search.rs +++ b/crates/hir/src/term_search.rs @@ -127,6 +127,13 @@ impl LookupTable { self.types_wishlist.insert(ty.clone()); } + // Collapse suggestions if there are many + if let Some(res) = &res { + if res.len() > self.many_threshold { + return Some(vec![Expr::Many(ty.clone())]); + } + } + res } @@ -158,6 +165,13 @@ impl LookupTable { self.types_wishlist.insert(ty.clone()); } + // Collapse suggestions if there are many + if let Some(res) = &res { + if res.len() > self.many_threshold { + return Some(vec![Expr::Many(ty.clone())]); + } + } + res } @@ -255,13 +269,13 @@ pub struct TermSearchConfig { pub enable_borrowcheck: bool, /// Indicate when to squash multiple trees to `Many` as there are too many to keep track pub many_alternatives_threshold: usize, - /// Depth of the search eg. number of cycles to run - pub depth: usize, + /// Fuel for term search in "units of work" + pub fuel: u64, } impl Default for TermSearchConfig { fn default() -> Self { - Self { enable_borrowcheck: true, many_alternatives_threshold: 1, depth: 6 } + Self { enable_borrowcheck: true, many_alternatives_threshold: 1, fuel: 400 } } } @@ -280,8 +294,7 @@ impl Default for TermSearchConfig { /// transformation tactics. For example functions take as from set of types (arguments) to some /// type (return type). Other transformations include methods on type, type constructors and /// projections to struct fields (field access). -/// 3. Once we manage to find path to type we are interested in we continue for single round to see -/// if we can find more paths that take us to the `goal` type. +/// 3. If we run out of fuel (term search takes too long) we stop iterating. /// 4. Return all the paths (type trees) that take us to the `goal` type. /// /// Note that there are usually more ways we can get to the `goal` type but some are discarded to @@ -297,21 +310,31 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> { }); let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold, ctx.goal.clone()); + let fuel = std::cell::Cell::new(ctx.config.fuel); + + let should_continue = &|| { + let remaining = fuel.get(); + fuel.set(remaining.saturating_sub(1)); + if remaining == 0 { + tracing::debug!("fuel exhausted"); + } + remaining > 0 + }; // Try trivial tactic first, also populates lookup table let mut solutions: Vec<Expr> = tactics::trivial(ctx, &defs, &mut lookup).collect(); // Use well known types tactic before iterations as it does not depend on other tactics solutions.extend(tactics::famous_types(ctx, &defs, &mut lookup)); - for _ in 0..ctx.config.depth { + while should_continue() { lookup.new_round(); - solutions.extend(tactics::type_constructor(ctx, &defs, &mut lookup)); - solutions.extend(tactics::free_function(ctx, &defs, &mut lookup)); - solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup)); - solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup)); - solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup)); - solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup)); + solutions.extend(tactics::type_constructor(ctx, &defs, &mut lookup, should_continue)); + solutions.extend(tactics::free_function(ctx, &defs, &mut lookup, should_continue)); + solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup, should_continue)); + solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup, should_continue)); + solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup, should_continue)); + solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup, should_continue)); // Discard not interesting `ScopeDef`s for speedup for def in lookup.exhausted_scopedefs() { diff --git a/crates/hir/src/term_search/expr.rs b/crates/hir/src/term_search/expr.rs index 2d0c5630e1..9f56a1ee55 100644 --- a/crates/hir/src/term_search/expr.rs +++ b/crates/hir/src/term_search/expr.rs @@ -211,13 +211,13 @@ impl Expr { } } Expr::Method { func, target, params, .. } => { - if target.contains_many_in_illegal_pos() { + if self.contains_many_in_illegal_pos(db) { return Ok(many_formatter(&target.ty(db))); } let func_name = func.name(db).display(db.upcast()).to_string(); let self_param = func.self_param(db).unwrap(); - let target = target.gen_source_code( + let target_str = target.gen_source_code( sema_scope, many_formatter, prefer_no_std, @@ -236,9 +236,12 @@ impl Expr { Some(trait_) => { let trait_name = mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_))?; let target = match self_param.access(db) { - crate::Access::Shared => format!("&{target}"), - crate::Access::Exclusive => format!("&mut {target}"), - crate::Access::Owned => target, + crate::Access::Shared if !target.is_many() => format!("&{target_str}"), + crate::Access::Exclusive if !target.is_many() => { + format!("&mut {target_str}") + } + crate::Access::Owned => target_str, + _ => many_formatter(&target.ty(db)), }; let res = match args.is_empty() { true => format!("{trait_name}::{func_name}({target})",), @@ -246,7 +249,7 @@ impl Expr { }; Ok(res) } - None => Ok(format!("{target}.{func_name}({args})")), + None => Ok(format!("{target_str}.{func_name}({args})")), } } Expr::Variant { variant, generics, params } => { @@ -381,7 +384,7 @@ impl Expr { Ok(res) } Expr::Field { expr, field } => { - if expr.contains_many_in_illegal_pos() { + if expr.contains_many_in_illegal_pos(db) { return Ok(many_formatter(&expr.ty(db))); } @@ -395,7 +398,7 @@ impl Expr { Ok(format!("{strukt}.{field}")) } Expr::Reference(expr) => { - if expr.contains_many_in_illegal_pos() { + if expr.contains_many_in_illegal_pos(db) { return Ok(many_formatter(&expr.ty(db))); } @@ -466,10 +469,15 @@ impl Expr { /// macro!().bar() /// ¯o!() /// ``` - fn contains_many_in_illegal_pos(&self) -> bool { + fn contains_many_in_illegal_pos(&self, db: &dyn HirDatabase) -> bool { match self { - Expr::Method { target, .. } => target.contains_many_in_illegal_pos(), - Expr::Field { expr, .. } => expr.contains_many_in_illegal_pos(), + Expr::Method { target, func, .. } => { + match func.as_assoc_item(db).and_then(|it| it.container_or_implemented_trait(db)) { + Some(_) => false, + None => target.is_many(), + } + } + Expr::Field { expr, .. } => expr.contains_many_in_illegal_pos(db), Expr::Reference(target) => target.is_many(), Expr::Many(_) => true, _ => false, diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index 63b2a2506f..a26728272d 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -4,6 +4,7 @@ //! * `ctx` - Context for the term search //! * `defs` - Set of items in scope at term search target location //! * `lookup` - Lookup table for types +//! * `should_continue` - Function that indicates when to stop iterating //! And they return iterator that yields type trees that unify with the `goal` type. use std::iter; @@ -97,16 +98,19 @@ pub(super) fn trivial<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn type_constructor<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, defs: &'a FxHashSet<ScopeDef>, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator<Item = Expr> + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); fn variant_helper( db: &dyn HirDatabase, lookup: &mut LookupTable, + should_continue: &dyn std::ops::Fn() -> bool, parent_enum: Enum, variant: Variant, config: &TermSearchConfig, @@ -152,6 +156,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( .chain((non_default_type_params_len == 0).then_some(Vec::new())); generic_params + .filter(|_| should_continue()) .filter_map(move |generics| { // Insert default type params let mut g = generics.into_iter(); @@ -194,8 +199,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( defs.iter() .filter_map(move |def| match def { ScopeDef::ModuleDef(ModuleDef::Variant(it)) => { - let variant_exprs = - variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.config); + let variant_exprs = variant_helper( + db, + lookup, + should_continue, + it.parent_enum(db), + *it, + &ctx.config, + ); if variant_exprs.is_empty() { return None; } @@ -213,7 +224,9 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( let exprs: Vec<(Type, Vec<Expr>)> = enum_ .variants(db) .into_iter() - .flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.config)) + .flat_map(|it| { + variant_helper(db, lookup, should_continue, *enum_, it, &ctx.config) + }) .collect(); if exprs.is_empty() { @@ -271,6 +284,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( .chain((non_default_type_params_len == 0).then_some(Vec::new())); let exprs = generic_params + .filter(|_| should_continue()) .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -345,10 +359,12 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn free_function<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, defs: &'a FxHashSet<ScopeDef>, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator<Item = Expr> + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); @@ -390,6 +406,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( .permutations(non_default_type_params_len); let exprs: Vec<_> = generic_params + .filter(|_| should_continue()) .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -474,10 +491,12 @@ pub(super) fn free_function<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn impl_method<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet<ScopeDef>, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator<Item = Expr> + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); @@ -554,6 +573,7 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( .permutations(non_default_fn_type_params_len); let exprs: Vec<_> = generic_params + .filter(|_| should_continue()) .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -645,10 +665,12 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn struct_projection<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet<ScopeDef>, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator<Item = Expr> + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); @@ -656,6 +678,7 @@ pub(super) fn struct_projection<'a, DB: HirDatabase>( .new_types(NewTypesKey::StructProjection) .into_iter() .map(|ty| (ty.clone(), lookup.find(db, &ty).expect("Expr not in lookup"))) + .filter(|_| should_continue()) .flat_map(move |(ty, targets)| { ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| { if !field.is_visible_from(db, module) { @@ -716,10 +739,12 @@ pub(super) fn famous_types<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn impl_static_method<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet<ScopeDef>, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator<Item = Expr> + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); @@ -728,6 +753,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( .clone() .into_iter() .chain(iter::once(ctx.goal.clone())) + .filter(|_| should_continue()) .flat_map(|ty| { Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp)) }) @@ -801,6 +827,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( .permutations(non_default_fn_type_params_len); let exprs: Vec<_> = generic_params + .filter(|_| should_continue()) .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -884,10 +911,12 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( /// * `ctx` - Context for the term search /// * `defs` - Set of items in scope at term search target location /// * `lookup` - Lookup table for types +/// * `should_continue` - Function that indicates when to stop iterating pub(super) fn make_tuple<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet<ScopeDef>, lookup: &'a mut LookupTable, + should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator<Item = Expr> + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); @@ -896,6 +925,7 @@ pub(super) fn make_tuple<'a, DB: HirDatabase>( .types_wishlist() .clone() .into_iter() + .filter(|_| should_continue()) .filter(|ty| ty.is_tuple()) .filter_map(move |ty| { // Double check to not contain unknown @@ -915,6 +945,7 @@ pub(super) fn make_tuple<'a, DB: HirDatabase>( let exprs: Vec<Expr> = param_exprs .into_iter() .multi_cartesian_product() + .filter(|_| should_continue()) .map(|params| { let tys: Vec<Type> = params.iter().map(|it| it.ty(db)).collect(); let tuple_ty = Type::new_tuple(module.krate().into(), &tys); diff --git a/crates/ide-assists/src/assist_config.rs b/crates/ide-assists/src/assist_config.rs index fbe17dbfd7..5d76cb0432 100644 --- a/crates/ide-assists/src/assist_config.rs +++ b/crates/ide-assists/src/assist_config.rs @@ -16,4 +16,5 @@ pub struct AssistConfig { pub prefer_no_std: bool, pub prefer_prelude: bool, pub assist_emit_must_use: bool, + pub term_search_fuel: u64, } diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index 55e0d7f3b2..f178a7e0ce 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -8,8 +8,7 @@ use ide_db::{ }; use syntax::{ ast::{self, make, AstNode, Expr::BinExpr, HasArgList}, - ted::{self, Position}, - SyntaxKind, + ted, SyntaxKind, T, }; use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; @@ -62,7 +61,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let demorganed = bin_expr.clone_subtree().clone_for_update(); ted::replace(demorganed.op_token()?, ast::make::token(inv_token)); - let mut exprs = VecDeque::from(vec![ + let mut exprs = VecDeque::from([ (bin_expr.lhs()?, demorganed.lhs()?), (bin_expr.rhs()?, demorganed.rhs()?), ]); @@ -93,58 +92,38 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti } } - let dm_lhs = demorganed.lhs()?; - acc.add_group( &GroupLabel("Apply De Morgan's law".to_owned()), AssistId("apply_demorgan", AssistKind::RefactorRewrite), "Apply De Morgan's law", op_range, |edit| { + let demorganed = ast::Expr::BinExpr(demorganed); let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast); let neg_expr = paren_expr .clone() .and_then(|paren_expr| paren_expr.syntax().parent()) .and_then(ast::PrefixExpr::cast) - .and_then(|prefix_expr| { - if prefix_expr.op_kind()? == ast::UnaryOp::Not { - Some(prefix_expr) - } else { - None - } - }); + .filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not))) + .map(ast::Expr::PrefixExpr); if let Some(paren_expr) = paren_expr { if let Some(neg_expr) = neg_expr { cov_mark::hit!(demorgan_double_negation); - edit.replace_ast(ast::Expr::PrefixExpr(neg_expr), demorganed.into()); + let parent = neg_expr.syntax().parent(); + + if parent.is_some_and(|parent| demorganed.needs_parens_in(parent)) { + cov_mark::hit!(demorgan_keep_parens_for_op_precedence2); + edit.replace_ast(neg_expr, make::expr_paren(demorganed)); + } else { + edit.replace_ast(neg_expr, demorganed); + }; } else { cov_mark::hit!(demorgan_double_parens); - ted::insert_all_raw( - Position::before(dm_lhs.syntax()), - vec![ - syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)), - syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)), - ], - ); - - ted::append_child_raw( - demorganed.syntax(), - syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::R_PAREN)), - ); - - edit.replace_ast(ast::Expr::ParenExpr(paren_expr), demorganed.into()); + edit.replace_ast(paren_expr.into(), add_bang_paren(demorganed)); } } else { - ted::insert_all_raw( - Position::before(dm_lhs.syntax()), - vec![ - syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)), - syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)), - ], - ); - ted::append_child_raw(demorganed.syntax(), ast::make::token(SyntaxKind::R_PAREN)); - edit.replace_ast(bin_expr, demorganed); + edit.replace_ast(bin_expr.into(), add_bang_paren(demorganed)); } }, ) @@ -271,6 +250,11 @@ fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) { } } +/// Add bang and parentheses to the expression. +fn add_bang_paren(expr: ast::Expr) -> ast::Expr { + make::expr_prefix(T![!], make::expr_paren(expr)) +} + #[cfg(test)] mod tests { use super::*; @@ -349,16 +333,14 @@ fn f() { !(S <= S || S < S) } check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") } - // FIXME : This needs to go. - // // https://github.com/rust-lang/rust-analyzer/issues/10963 - // #[test] - // fn demorgan_doesnt_hang() { - // check_assist( - // apply_demorgan, - // "fn f() { 1 || 3 &&$0 4 || 5 }", - // "fn f() { !(!1 || !3 || !4) || 5 }", - // ) - // } + #[test] + fn demorgan_doesnt_hang() { + check_assist( + apply_demorgan, + "fn f() { 1 || 3 &&$0 4 || 5 }", + "fn f() { 1 || !(!3 || !4) || 5 }", + ) + } #[test] fn demorgan_keep_pars_for_op_precedence() { @@ -376,6 +358,21 @@ fn f() { !(S <= S || S < S) } } #[test] + fn demorgan_keep_pars_for_op_precedence2() { + cov_mark::check!(demorgan_keep_parens_for_op_precedence2); + check_assist( + apply_demorgan, + "fn f() { (a && !(b &&$0 c); }", + "fn f() { (a && (!b || !c); }", + ); + } + + #[test] + fn demorgan_keep_pars_for_op_precedence3() { + check_assist(apply_demorgan, "fn f() { (a || !(b &&$0 c); }", "fn f() { (a || !b || !c; }"); + } + + #[test] fn demorgan_removes_pars_in_eq_precedence() { check_assist( apply_demorgan, @@ -385,6 +382,11 @@ fn f() { !(S <= S || S < S) } } #[test] + fn demorgan_removes_pars_for_op_precedence2() { + check_assist(apply_demorgan, "fn f() { (a || !(b ||$0 c); }", "fn f() { (a || !b && !c; }"); + } + + #[test] fn demorgan_iterator_any_all_reverse() { check_assist( apply_demorgan_iterator, diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 76f021ed91..43ff115886 100644 --- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -85,7 +85,7 @@ fn edit_struct_def( strukt: &Either<ast::Struct, ast::Variant>, record_fields: ast::RecordFieldList, ) { - // Note that we don't need to consider macro files in this function because this this is + // Note that we don't need to consider macro files in this function because this is // currently not triggered for struct definitions inside macro calls. let tuple_fields = record_fields .fields() diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 34326294d2..2b8de3443b 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -5623,7 +5623,7 @@ fn func<T: Debug>(i: Struct<'_, T>) { fun_name(i); } -fn $0fun_name(i: Struct<'_, T>) { +fn $0fun_name(i: Struct<T>) { foo(i); } "#, diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index db94a21a6d..0fc122d623 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -1,6 +1,6 @@ use hir::{ - Adt, AsAssocItem, HasSource, HirDisplay, HirFileIdExt, Module, PathResolution, Semantics, Type, - TypeInfo, + Adt, AsAssocItem, HasSource, HirDisplay, HirFileIdExt, Module, PathResolution, Semantics, + StructKind, Type, TypeInfo, }; use ide_db::{ base_db::FileId, @@ -15,8 +15,8 @@ use itertools::Itertools; use stdx::to_lower_snake_case; use syntax::{ ast::{ - self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, CallExpr, HasArgList, - HasGenericParams, HasModuleItem, HasTypeBounds, + self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, BlockExpr, CallExpr, + HasArgList, HasGenericParams, HasModuleItem, HasTypeBounds, }, ted, SyntaxKind, SyntaxNode, TextRange, T, }; @@ -66,7 +66,7 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } let fn_name = &*name_ref.text(); - let TargetInfo { target_module, adt_name, target, file } = + let TargetInfo { target_module, adt_info, target, file } = fn_target_info(ctx, path, &call, fn_name)?; if let Some(m) = target_module { @@ -75,15 +75,16 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } } - let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?; + let function_builder = + FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target, &adt_info)?; let text_range = call.syntax().text_range(); let label = format!("Generate {} function", function_builder.fn_name); - add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_name, label) + add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_info, label) } struct TargetInfo { target_module: Option<Module>, - adt_name: Option<hir::Name>, + adt_info: Option<AdtInfo>, target: GeneratedFunctionTarget, file: FileId, } @@ -91,11 +92,11 @@ struct TargetInfo { impl TargetInfo { fn new( target_module: Option<Module>, - adt_name: Option<hir::Name>, + adt_info: Option<AdtInfo>, target: GeneratedFunctionTarget, file: FileId, ) -> Self { - Self { target_module, adt_name, target, file } + Self { target_module, adt_info, target, file } } } @@ -157,9 +158,9 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { target, )?; let text_range = call.syntax().text_range(); - let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; + let adt_info = AdtInfo::new(adt, impl_.is_some()); let label = format!("Generate {} method", function_builder.fn_name); - add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_name, label) + add_func_to_accumulator(acc, ctx, text_range, function_builder, file, Some(adt_info), label) } fn add_func_to_accumulator( @@ -168,7 +169,7 @@ fn add_func_to_accumulator( text_range: TextRange, function_builder: FunctionBuilder, file: FileId, - adt_name: Option<hir::Name>, + adt_info: Option<AdtInfo>, label: String, ) -> Option<()> { acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |edit| { @@ -177,8 +178,14 @@ fn add_func_to_accumulator( let target = function_builder.target.clone(); let func = function_builder.render(ctx.config.snippet_cap, edit); - if let Some(name) = adt_name { - let name = make::ty_path(make::ext::ident_path(&format!("{}", name.display(ctx.db())))); + if let Some(adt) = + adt_info + .and_then(|adt_info| if adt_info.impl_exists { None } else { Some(adt_info.adt) }) + { + let name = make::ty_path(make::ext::ident_path(&format!( + "{}", + adt.name(ctx.db()).display(ctx.db()) + ))); // FIXME: adt may have generic params. let impl_ = make::impl_(None, None, name, None, None).clone_for_update(); @@ -210,6 +217,7 @@ struct FunctionBuilder { generic_param_list: Option<ast::GenericParamList>, where_clause: Option<ast::WhereClause>, params: ast::ParamList, + fn_body: BlockExpr, ret_type: Option<ast::RetType>, should_focus_return_type: bool, visibility: Visibility, @@ -225,6 +233,7 @@ impl FunctionBuilder { fn_name: &str, target_module: Option<Module>, target: GeneratedFunctionTarget, + adt_info: &Option<AdtInfo>, ) -> Option<Self> { let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).map(|it| it.module()))?; @@ -243,9 +252,27 @@ impl FunctionBuilder { let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast); let is_async = await_expr.is_some(); - let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into()); - let (ret_type, should_focus_return_type) = - make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params); + let ret_type; + let should_focus_return_type; + let fn_body; + + // If generated function has the name "new" and is an associated function, we generate fn body + // as a constructor and assume a "Self" return type. + if let Some(body) = make_fn_body_as_new_function(ctx, &fn_name.text(), adt_info) { + ret_type = Some(make::ret_type(make::ty_path(make::ext::ident_path("Self")))); + should_focus_return_type = false; + fn_body = body; + } else { + let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into()); + (ret_type, should_focus_return_type) = make_return_type( + ctx, + &expr_for_ret_ty, + target_module, + &mut necessary_generic_params, + ); + let placeholder_expr = make::ext::expr_todo(); + fn_body = make::block_expr(vec![], Some(placeholder_expr)); + }; let (generic_param_list, where_clause) = fn_generic_params(ctx, necessary_generic_params, &target)?; @@ -256,6 +283,7 @@ impl FunctionBuilder { generic_param_list, where_clause, params, + fn_body, ret_type, should_focus_return_type, visibility, @@ -294,12 +322,16 @@ impl FunctionBuilder { let (generic_param_list, where_clause) = fn_generic_params(ctx, necessary_generic_params, &target)?; + let placeholder_expr = make::ext::expr_todo(); + let fn_body = make::block_expr(vec![], Some(placeholder_expr)); + Some(Self { target, fn_name, generic_param_list, where_clause, params, + fn_body, ret_type, should_focus_return_type, visibility, @@ -308,8 +340,6 @@ impl FunctionBuilder { } fn render(self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn { - let placeholder_expr = make::ext::expr_todo(); - let fn_body = make::block_expr(vec![], Some(placeholder_expr)); let visibility = match self.visibility { Visibility::None => None, Visibility::Crate => Some(make::visibility_pub_crate()), @@ -321,7 +351,7 @@ impl FunctionBuilder { self.generic_param_list, self.where_clause, self.params, - fn_body, + self.fn_body, self.ret_type, self.is_async, false, // FIXME : const and unsafe are not handled yet. @@ -391,6 +421,53 @@ fn make_return_type( (ret_type, should_focus_return_type) } +fn make_fn_body_as_new_function( + ctx: &AssistContext<'_>, + fn_name: &str, + adt_info: &Option<AdtInfo>, +) -> Option<ast::BlockExpr> { + if fn_name != "new" { + return None; + }; + let adt_info = adt_info.as_ref()?; + + let path_self = make::ext::ident_path("Self"); + let placeholder_expr = make::ext::expr_todo(); + let tail_expr = if let Some(strukt) = adt_info.adt.as_struct() { + match strukt.kind(ctx.db()) { + StructKind::Record => { + let fields = strukt + .fields(ctx.db()) + .iter() + .map(|field| { + make::record_expr_field( + make::name_ref(&format!("{}", field.name(ctx.db()).display(ctx.db()))), + Some(placeholder_expr.clone()), + ) + }) + .collect::<Vec<_>>(); + + make::record_expr(path_self, make::record_expr_field_list(fields)).into() + } + StructKind::Tuple => { + let args = strukt + .fields(ctx.db()) + .iter() + .map(|_| placeholder_expr.clone()) + .collect::<Vec<_>>(); + + make::expr_call(make::expr_path(path_self), make::arg_list(args)) + } + StructKind::Unit => make::expr_path(path_self), + } + } else { + placeholder_expr + }; + + let fn_body = make::block_expr(vec![], Some(tail_expr)); + Some(fn_body) +} + fn get_fn_target_info( ctx: &AssistContext<'_>, target_module: Option<Module>, @@ -443,8 +520,8 @@ fn assoc_fn_target_info( } let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; let target = get_method_target(ctx, &impl_, &adt)?; - let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; - Some(TargetInfo::new(target_module, adt_name, target, file)) + let adt_info = AdtInfo::new(adt, impl_.is_some()); + Some(TargetInfo::new(target_module, Some(adt_info), target, file)) } #[derive(Clone)] @@ -560,6 +637,17 @@ impl GeneratedFunctionTarget { } } +struct AdtInfo { + adt: hir::Adt, + impl_exists: bool, +} + +impl AdtInfo { + fn new(adt: Adt, impl_exists: bool) -> Self { + Self { adt, impl_exists } + } +} + /// Computes parameter list for the generated function. fn fn_args( ctx: &AssistContext<'_>, @@ -2758,18 +2846,18 @@ fn main() { r" enum Foo {} fn main() { - Foo::new$0(); + Foo::bar$0(); } ", r" enum Foo {} impl Foo { - fn new() ${0:-> _} { + fn bar() ${0:-> _} { todo!() } } fn main() { - Foo::new(); + Foo::bar(); } ", ) @@ -2849,4 +2937,152 @@ fn main() { ", ); } + + #[test] + fn new_function_assume_self_type() { + check_assist( + generate_function, + r" +pub struct Foo { + field_1: usize, + field_2: String, +} + +fn main() { + let foo = Foo::new$0(); +} + ", + r" +pub struct Foo { + field_1: usize, + field_2: String, +} +impl Foo { + fn new() -> Self { + ${0:Self { field_1: todo!(), field_2: todo!() }} + } +} + +fn main() { + let foo = Foo::new(); +} + ", + ) + } + + #[test] + fn new_function_assume_self_type_for_tuple_struct() { + check_assist( + generate_function, + r" +pub struct Foo (usize, String); + +fn main() { + let foo = Foo::new$0(); +} + ", + r" +pub struct Foo (usize, String); +impl Foo { + fn new() -> Self { + ${0:Self(todo!(), todo!())} + } +} + +fn main() { + let foo = Foo::new(); +} + ", + ) + } + + #[test] + fn new_function_assume_self_type_for_unit_struct() { + check_assist( + generate_function, + r" +pub struct Foo; + +fn main() { + let foo = Foo::new$0(); +} + ", + r" +pub struct Foo; +impl Foo { + fn new() -> Self { + ${0:Self} + } +} + +fn main() { + let foo = Foo::new(); +} + ", + ) + } + + #[test] + fn new_function_assume_self_type_for_enum() { + check_assist( + generate_function, + r" +pub enum Foo {} + +fn main() { + let foo = Foo::new$0(); +} + ", + r" +pub enum Foo {} +impl Foo { + fn new() -> Self { + ${0:todo!()} + } +} + +fn main() { + let foo = Foo::new(); +} + ", + ) + } + + #[test] + fn new_function_assume_self_type_with_args() { + check_assist( + generate_function, + r#" +pub struct Foo { + field_1: usize, + field_2: String, +} + +struct Baz; +fn baz() -> Baz { Baz } + +fn main() { + let foo = Foo::new$0(baz(), baz(), "foo", "bar"); +} + "#, + r#" +pub struct Foo { + field_1: usize, + field_2: String, +} +impl Foo { + fn new(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) -> Self { + ${0:Self { field_1: todo!(), field_2: todo!() }} + } +} + +struct Baz; +fn baz() -> Baz { Baz } + +fn main() { + let foo = Foo::new(baz(), baz(), "foo", "bar"); +} + "#, + ) + } } diff --git a/crates/ide-assists/src/handlers/move_module_to_file.rs b/crates/ide-assists/src/handlers/move_module_to_file.rs index 048906d9d9..9af8411f4c 100644 --- a/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -1,6 +1,7 @@ use std::iter; use ast::edit::IndentLevel; +use hir::HasAttrs; use ide_db::base_db::AnchoredPathBuf; use itertools::Itertools; use stdx::format_to; @@ -50,9 +51,17 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> |builder| { let path = { let mut buf = String::from("./"); - match parent_module.name(ctx.db()) { - Some(name) if !parent_module.is_mod_rs(ctx.db()) => { - format_to!(buf, "{}/", name.display(ctx.db())) + let db = ctx.db(); + match parent_module.name(db) { + Some(name) + if !parent_module.is_mod_rs(db) + && parent_module + .attrs(db) + .by_key("path") + .string_value_unescape() + .is_none() => + { + format_to!(buf, "{}/", name.display(db)) } _ => (), } @@ -108,6 +117,72 @@ mod tests { use super::*; #[test] + fn extract_with_specified_path_attr() { + check_assist( + move_module_to_file, + r#" +//- /main.rs +#[path="parser/__mod.rs"] +mod parser; +//- /parser/__mod.rs +fn test() {} +mod $0expr { + struct A {} +} +"#, + r#" +//- /parser/__mod.rs +fn test() {} +mod expr; +//- /parser/expr.rs +struct A {} +"#, + ); + + check_assist( + move_module_to_file, + r#" +//- /main.rs +#[path="parser/a/__mod.rs"] +mod parser; +//- /parser/a/__mod.rs +fn test() {} +mod $0expr { + struct A {} +} +"#, + r#" +//- /parser/a/__mod.rs +fn test() {} +mod expr; +//- /parser/a/expr.rs +struct A {} +"#, + ); + + check_assist( + move_module_to_file, + r#" +//- /main.rs +#[path="a.rs"] +mod parser; +//- /a.rs +fn test() {} +mod $0expr { + struct A {} +} +"#, + r#" +//- /a.rs +fn test() {} +mod expr; +//- /expr.rs +struct A {} +"#, + ); + } + + #[test] fn extract_from_root() { check_assist( move_module_to_file, diff --git a/crates/ide-assists/src/handlers/raw_string.rs b/crates/ide-assists/src/handlers/raw_string.rs index 63db606336..5a197f23d0 100644 --- a/crates/ide-assists/src/handlers/raw_string.rs +++ b/crates/ide-assists/src/handlers/raw_string.rs @@ -25,7 +25,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt if token.is_raw() { return None; } - let value = token.value()?; + let value = token.value().ok()?; let target = token.syntax().text_range(); acc.add( AssistId("make_raw_string", AssistKind::RefactorRewrite), @@ -64,7 +64,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O if !token.is_raw() { return None; } - let value = token.value()?; + let value = token.value().ok()?; let target = token.syntax().text_range(); acc.add( AssistId("make_usual_string", AssistKind::RefactorRewrite), @@ -398,12 +398,12 @@ string"###; } #[test] - fn remove_hash_doesnt_work() { + fn remove_hash_does_not_work() { check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0"random string"; }"#); } #[test] - fn remove_hash_no_hash_doesnt_work() { + fn remove_hash_no_hash_does_not_work() { check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0r"random string"; }"#); } diff --git a/crates/ide-assists/src/handlers/reorder_impl_items.rs b/crates/ide-assists/src/handlers/reorder_impl_items.rs index 6666966231..cf135f83e7 100644 --- a/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -77,7 +77,7 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> ast::AssocItem::MacroCall(_) => None, }; - name.and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::max_value()) + name.and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::MAX) }) .collect(); diff --git a/crates/ide-assists/src/handlers/replace_string_with_char.rs b/crates/ide-assists/src/handlers/replace_string_with_char.rs index 6310981ccc..a48b20acbc 100644 --- a/crates/ide-assists/src/handlers/replace_string_with_char.rs +++ b/crates/ide-assists/src/handlers/replace_string_with_char.rs @@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let token = ctx.find_token_syntax_at_offset(STRING).and_then(ast::String::cast)?; - let value = token.value()?; + let value = token.value().ok()?; let target = token.syntax().text_range(); if value.chars().take(2).count() != 1 { diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs index 0f4a8e3aec..d0c6ae2198 100644 --- a/crates/ide-assists/src/handlers/term_search.rs +++ b/crates/ide-assists/src/handlers/term_search.rs @@ -1,5 +1,5 @@ //! Term search assist -use hir::term_search::TermSearchCtx; +use hir::term_search::{TermSearchConfig, TermSearchCtx}; use ide_db::{ assists::{AssistId, AssistKind, GroupLabel}, famous_defs::FamousDefs, @@ -34,7 +34,7 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< sema: &ctx.sema, scope: &scope, goal: target_ty, - config: Default::default(), + config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() }, }; let paths = hir::term_search::term_search(&term_search_ctx); diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index 32d6984102..3b6c951251 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -31,6 +31,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { prefer_no_std: false, prefer_prelude: true, assist_emit_must_use: false, + term_search_fuel: 400, }; pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig { @@ -46,6 +47,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig { prefer_no_std: false, prefer_prelude: true, assist_emit_must_use: false, + term_search_fuel: 400, }; pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig { @@ -61,6 +63,7 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig { prefer_no_std: false, prefer_prelude: true, assist_emit_must_use: false, + term_search_fuel: 400, }; pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 802e9bc3a8..1e31d65fdd 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -353,7 +353,7 @@ pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>) config: hir::term_search::TermSearchConfig { enable_borrowcheck: false, many_alternatives_threshold: 1, - depth: 6, + fuel: 200, }, }; let exprs = hir::term_search::term_search(&term_search_ctx); diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs index 3bc329ecd7..bf6747d71b 100644 --- a/crates/ide-completion/src/completions/flyimport.rs +++ b/crates/ide-completion/src/completions/flyimport.rs @@ -296,7 +296,7 @@ fn import_on_the_fly_pat_( position: SyntaxNode, potential_import_name: String, ) -> Option<()> { - let _p = tracing::span!(tracing::Level::INFO, "import_on_the_fly_pat", ?potential_import_name) + let _p = tracing::span!(tracing::Level::INFO, "import_on_the_fly_pat_", ?potential_import_name) .entered(); ImportScope::find_insert_use_container(&position, &ctx.sema)?; diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs index 04563fb0f4..809c305ed8 100644 --- a/crates/ide-completion/src/config.rs +++ b/crates/ide-completion/src/config.rs @@ -15,6 +15,7 @@ pub struct CompletionConfig { pub enable_self_on_the_fly: bool, pub enable_private_editable: bool, pub enable_term_search: bool, + pub term_search_fuel: u64, pub full_function_signatures: bool, pub callable: Option<CallableSnippets>, pub snippet_cap: Option<SnippetCap>, diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 8b435f419c..db34beadc0 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -466,7 +466,7 @@ impl CompletionContext<'_> { cov_mark::hit!(completes_if_lifetime_without_idents); TextRange::at(self.original_token.text_range().start(), TextSize::from(1)) } - IDENT | LIFETIME_IDENT | UNDERSCORE => self.original_token.text_range(), + IDENT | LIFETIME_IDENT | UNDERSCORE | INT_NUMBER => self.original_token.text_range(), _ if kind.is_keyword() => self.original_token.text_range(), _ => TextRange::empty(self.position.offset), } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index ca0424809e..7fa31e2757 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -368,7 +368,7 @@ fn render_resolution_pat( import_to_add: Option<LocatedImport>, resolution: ScopeDef, ) -> Builder { - let _p = tracing::span!(tracing::Level::INFO, "render_resolution").entered(); + let _p = tracing::span!(tracing::Level::INFO, "render_resolution_pat").entered(); use hir::ModuleDef::*; if let ScopeDef::ModuleDef(Macro(mac)) = resolution { @@ -386,7 +386,7 @@ fn render_resolution_path( import_to_add: Option<LocatedImport>, resolution: ScopeDef, ) -> Builder { - let _p = tracing::span!(tracing::Level::INFO, "render_resolution").entered(); + let _p = tracing::span!(tracing::Level::INFO, "render_resolution_path").entered(); use hir::ModuleDef::*; match resolution { @@ -494,7 +494,7 @@ fn render_resolution_simple_( import_to_add: Option<LocatedImport>, resolution: ScopeDef, ) -> Builder { - let _p = tracing::span!(tracing::Level::INFO, "render_resolution").entered(); + let _p = tracing::span!(tracing::Level::INFO, "render_resolution_simple_").entered(); let db = ctx.db(); let ctx = ctx.import_to_add(import_to_add); @@ -1731,6 +1731,51 @@ fn foo(a: A) { B { bar: a.$0 }; } } #[test] + fn tuple_field_detail() { + check( + r#" +struct S(i32); + +fn f() -> i32 { + let s = S(0); + s.0$0 +} +"#, + SymbolKind::Field, + expect![[r#" + [ + CompletionItem { + label: "0", + source_range: 56..57, + delete: 56..57, + insert: "0", + kind: SymbolKind( + Field, + ), + detail: "i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: Some( + Exact, + ), + is_local: false, + is_item_from_trait: false, + is_item_from_notable_trait: false, + is_name_already_imported: false, + requires_import: false, + is_op_method: false, + is_private_editable: false, + postfix_match: None, + is_definite: false, + function: None, + }, + }, + ] + "#]], + ); + } + + #[test] fn record_field_and_call_relevances() { check_relevance( r#" @@ -1808,8 +1853,7 @@ fn f() { A { bar: b$0 }; } fn baz() [type] ex baz() [type] ex bar() [type] - ex A { bar: baz() }.bar [type] - ex A { bar: bar() }.bar [type] + ex A { bar: ... }.bar [type] st A [] fn f() [] "#]], @@ -1947,8 +1991,8 @@ fn main() { } "#, expect![[r#" - ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify] ex core::ops::Deref::deref(&t) (use core::ops::Deref) [type_could_unify] + ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify] lc m [local] lc t [local] lc &t [type+local] @@ -1997,8 +2041,8 @@ fn main() { } "#, expect![[r#" - ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify] ex core::ops::DerefMut::deref_mut(&mut t) (use core::ops::DerefMut) [type_could_unify] + ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify] lc m [local] lc t [local] lc &mut t [type+local] diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index f52a5f7625..9c5cb1e37d 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -27,7 +27,7 @@ pub(crate) fn render_variant_lit( variant: hir::Variant, path: Option<hir::ModPath>, ) -> Option<Builder> { - let _p = tracing::span!(tracing::Level::INFO, "render_enum_variant").entered(); + let _p = tracing::span!(tracing::Level::INFO, "render_variant_lit").entered(); let db = ctx.db(); let name = local_name.unwrap_or_else(|| variant.name(db)); diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs index 540cfd03d6..8b81a95abb 100644 --- a/crates/ide-completion/src/render/macro_.rs +++ b/crates/ide-completion/src/render/macro_.rs @@ -27,7 +27,7 @@ pub(crate) fn render_macro_pat( name: hir::Name, macro_: hir::Macro, ) -> Builder { - let _p = tracing::span!(tracing::Level::INFO, "render_macro").entered(); + let _p = tracing::span!(tracing::Level::INFO, "render_macro_pat").entered(); render(ctx, false, false, false, name, macro_) } diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index 1f032c7df4..70e0aa4e9a 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -80,6 +80,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { }, snippets: Vec::new(), limit: None, + term_search_fuel: 200, }; pub(crate) fn completion_list(ra_fixture: &str) -> String { diff --git a/crates/ide-completion/src/tests/predicate.rs b/crates/ide-completion/src/tests/predicate.rs index 64a32dee3d..62eb642b3b 100644 --- a/crates/ide-completion/src/tests/predicate.rs +++ b/crates/ide-completion/src/tests/predicate.rs @@ -19,7 +19,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'{error}, {unknown}, _> + st Foo<…> Foo<{unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -92,7 +92,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'{error}, {unknown}, _> + st Foo<…> Foo<{unknown}, _> st Record Record st Tuple Tuple st Unit Unit diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 66f1bff7c1..ff38c16108 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -20,8 +20,8 @@ struct Foo<'lt, T, const C: usize> { en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'{error}, {unknown}, _> - st Foo<…> Foo<'{error}, {unknown}, _> + sp Self Foo<{unknown}, _> + st Foo<…> Foo<{unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -45,8 +45,8 @@ struct Foo<'lt, T, const C: usize>(f$0); en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'{error}, {unknown}, _> - st Foo<…> Foo<'{error}, {unknown}, _> + sp Self Foo<{unknown}, _> + st Foo<…> Foo<{unknown}, _> st Record Record st Tuple Tuple st Unit Unit diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index c0f0faba35..634277e869 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -11,8 +11,8 @@ use hir::{ Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field, Function, GenericParam, HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module, - ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TupleField, - TypeAlias, Variant, VariantDef, Visibility, + ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait, + TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use stdx::{format_to, impl_from}; use syntax::{ @@ -39,12 +39,13 @@ pub enum Definition { Trait(Trait), TraitAlias(TraitAlias), TypeAlias(TypeAlias), - BuiltinType(BuiltinType), SelfType(Impl), GenericParam(GenericParam), Local(Local), Label(Label), DeriveHelper(DeriveHelper), + BuiltinType(BuiltinType), + BuiltinLifetime(StaticLifetime), BuiltinAttr(BuiltinAttr), ToolModule(ToolModule), ExternCrateDecl(ExternCrateDecl), @@ -83,6 +84,7 @@ impl Definition { Definition::DeriveHelper(it) => it.derive().module(db), Definition::BuiltinAttr(_) | Definition::BuiltinType(_) + | Definition::BuiltinLifetime(_) | Definition::TupleField(_) | Definition::ToolModule(_) => return None, }; @@ -112,6 +114,7 @@ impl Definition { Definition::BuiltinType(_) | Definition::TupleField(_) => Visibility::Public, Definition::Macro(_) => return None, Definition::BuiltinAttr(_) + | Definition::BuiltinLifetime(_) | Definition::ToolModule(_) | Definition::SelfType(_) | Definition::Local(_) @@ -141,6 +144,7 @@ impl Definition { Definition::Local(it) => it.name(db), Definition::GenericParam(it) => it.name(db), Definition::Label(it) => it.name(db), + Definition::BuiltinLifetime(StaticLifetime) => hir::known::STATIC_LIFETIME, Definition::BuiltinAttr(_) => return None, // FIXME Definition::ToolModule(_) => return None, // FIXME Definition::DeriveHelper(it) => it.name(db), @@ -174,6 +178,7 @@ impl Definition { doc_owner.docs(fd.0.db) }) } + Definition::BuiltinLifetime(StaticLifetime) => None, Definition::Local(_) => None, Definition::SelfType(impl_def) => { impl_def.self_ty(db).as_adt().map(|adt| adt.docs(db))? @@ -228,6 +233,7 @@ impl Definition { Definition::TraitAlias(it) => it.display(db).to_string(), Definition::TypeAlias(it) => it.display(db).to_string(), Definition::BuiltinType(it) => it.name().display(db).to_string(), + Definition::BuiltinLifetime(it) => it.name().display(db).to_string(), Definition::Local(it) => { let ty = it.ty(db); let ty_display = ty.display_truncated(db, None); @@ -693,6 +699,9 @@ impl NameRefClass { ) -> Option<NameRefClass> { let _p = tracing::span!(tracing::Level::INFO, "NameRefClass::classify_lifetime", ?lifetime) .entered(); + if lifetime.text() == "'static" { + return Some(NameRefClass::Definition(Definition::BuiltinLifetime(StaticLifetime))); + } let parent = lifetime.syntax().parent()?; match parent.kind() { SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => { diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index c597555a3b..766bfcf4d0 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -209,8 +209,7 @@ impl ImportAssets { prefer_no_std: bool, prefer_prelude: bool, ) -> impl Iterator<Item = LocatedImport> { - let _p = - tracing::span!(tracing::Level::INFO, "import_assets::search_for_imports").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::search_for_imports").entered(); self.search_for(sema, Some(prefix_kind), prefer_no_std, prefer_prelude) } @@ -221,7 +220,7 @@ impl ImportAssets { prefer_no_std: bool, prefer_prelude: bool, ) -> impl Iterator<Item = LocatedImport> { - let _p = tracing::span!(tracing::Level::INFO, "import_assets::search_for_relative_paths") + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::search_for_relative_paths") .entered(); self.search_for(sema, None, prefer_no_std, prefer_prelude) } @@ -263,7 +262,7 @@ impl ImportAssets { prefer_no_std: bool, prefer_prelude: bool, ) -> impl Iterator<Item = LocatedImport> { - let _p = tracing::span!(tracing::Level::INFO, "import_assets::search_for").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::search_for").entered(); let scope = match sema.scope(&self.candidate_node) { Some(it) => it, @@ -308,7 +307,7 @@ impl ImportAssets { } fn scope_definitions(&self, sema: &Semantics<'_, RootDatabase>) -> FxHashSet<ScopeDef> { - let _p = tracing::span!(tracing::Level::INFO, "import_assets::scope_definitions").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::scope_definitions").entered(); let mut scope_definitions = FxHashSet::default(); if let Some(scope) = sema.scope(&self.candidate_node) { scope.process_all_names(&mut |_, scope_def| { @@ -327,7 +326,7 @@ fn path_applicable_imports( scope_filter: impl Fn(ItemInNs) -> bool + Copy, ) -> FxHashSet<LocatedImport> { let _p = - tracing::span!(tracing::Level::INFO, "import_assets::path_applicable_imports").entered(); + tracing::span!(tracing::Level::INFO, "ImportAssets::path_applicable_imports").entered(); match &path_candidate.qualifier { None => { @@ -374,7 +373,7 @@ fn import_for_item( original_item: ItemInNs, scope_filter: impl Fn(ItemInNs) -> bool, ) -> Option<LocatedImport> { - let _p = tracing::span!(tracing::Level::INFO, "import_assets::import_for_item").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::import_for_item").entered(); let [first_segment, ..] = unresolved_qualifier else { return None }; let item_as_assoc = item_as_assoc(db, original_item); @@ -508,8 +507,7 @@ fn trait_applicable_items( mod_path: impl Fn(ItemInNs) -> Option<ModPath>, scope_filter: impl Fn(hir::Trait) -> bool, ) -> FxHashSet<LocatedImport> { - let _p = - tracing::span!(tracing::Level::INFO, "import_assets::trait_applicable_items").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ImportAssets::trait_applicable_items").entered(); let db = sema.db; diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs index e97f1b8614..026d4e36f9 100644 --- a/crates/ide-db/src/imports/insert_use.rs +++ b/crates/ide-db/src/imports/insert_use.rs @@ -194,7 +194,7 @@ fn insert_use_with_alias_option( cfg: &InsertUseConfig, alias: Option<ast::Rename>, ) { - let _p = tracing::span!(tracing::Level::INFO, "insert_use").entered(); + let _p = tracing::span!(tracing::Level::INFO, "insert_use_with_alias_option").entered(); let mut mb = match cfg.granularity { ImportGranularity::Crate => Some(MergeBehavior::Crate), ImportGranularity::Module => Some(MergeBehavior::Module), diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs index 024e8f6ae3..58077f636b 100644 --- a/crates/ide-db/src/prime_caches.rs +++ b/crates/ide-db/src/prime_caches.rs @@ -1,6 +1,6 @@ //! rust-analyzer is lazy and doesn't compute anything unless asked. This //! sometimes is counter productive when, for example, the first goto definition -//! request takes longer to compute. This modules implemented prepopulation of +//! request takes longer to compute. This module implements prepopulation of //! various caches, it's not really advanced at the moment. mod topologic_sort; @@ -32,7 +32,7 @@ pub fn parallel_prime_caches( num_worker_threads: u8, cb: &(dyn Fn(ParallelPrimeCachesProgress) + Sync), ) { - let _p = tracing::span!(tracing::Level::INFO, "prime_caches").entered(); + let _p = tracing::span!(tracing::Level::INFO, "parallel_prime_caches").entered(); let graph = db.crate_graph(); let mut crates_to_prime = { diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 6a7042988a..288d56b534 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -196,11 +196,12 @@ impl Definition { .and_then(syn_ctx_is_root) } } - Definition::BuiltinType(_) => return None, - Definition::SelfType(_) => return None, - Definition::BuiltinAttr(_) => return None, - Definition::ToolModule(_) => return None, - Definition::TupleField(_) => return None, + Definition::BuiltinType(_) + | Definition::BuiltinLifetime(_) + | Definition::BuiltinAttr(_) + | Definition::SelfType(_) + | Definition::ToolModule(_) + | Definition::TupleField(_) => return None, // FIXME: This should be doable in theory Definition::DeriveHelper(_) => return None, }; diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index cb103313c9..8f633065f3 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -1,7 +1,7 @@ //! Implementation of find-usages functionality. //! //! It is based on the standard ide trick: first, we run a fast text search to -//! get a super-set of matches. Then, we we confirm each match using precise +//! get a super-set of matches. Then, we confirm each match using precise //! name resolution. use std::mem; diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index c65467a432..12085f9ebd 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs @@ -301,8 +301,8 @@ impl SymbolIndex { } fn range_to_map_value(start: usize, end: usize) -> u64 { - debug_assert![start <= (std::u32::MAX as usize)]; - debug_assert![end <= (std::u32::MAX as usize)]; + debug_assert![start <= (u32::MAX as usize)]; + debug_assert![end <= (u32::MAX as usize)]; ((start as u64) << 32) | end as u64 } 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 241fddbb90..b3dde977b1 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -7,7 +7,7 @@ use ide_db::{ helpers::mod_path_to_ast, imports::insert_use::{insert_use, ImportScope}, source_change::SourceChangeBuilder, - RootDatabase, + FxHashMap, RootDatabase, }; use itertools::Itertools; use stdx::{format_to, never}; @@ -22,15 +22,22 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsConfig, Severity}; #[derive(Default)] struct State { result: String, - struct_counts: usize, has_serialize: bool, has_deserialize: bool, + names: FxHashMap<String, usize>, } impl State { - fn generate_new_name(&mut self) -> ast::Name { - self.struct_counts += 1; - make::name(&format!("Struct{}", self.struct_counts)) + fn generate_new_name(&mut self, name: &str) -> ast::Name { + let name = stdx::to_camel_case(name); + let count = if let Some(count) = self.names.get_mut(&name) { + *count += 1; + *count + } else { + self.names.insert(name.clone(), 1); + 1 + }; + make::name(&format!("{}{}", name, count)) } fn serde_derive(&self) -> String { @@ -52,15 +59,21 @@ impl State { } } - fn build_struct(&mut self, value: &serde_json::Map<String, serde_json::Value>) -> ast::Type { - let name = self.generate_new_name(); + fn build_struct( + &mut self, + name: &str, + value: &serde_json::Map<String, serde_json::Value>, + ) -> ast::Type { + let name = self.generate_new_name(name); let ty = make::ty(&name.to_string()); let strukt = make::struct_( None, name, None, make::record_field_list(value.iter().sorted_unstable_by_key(|x| x.0).map( - |(name, value)| make::record_field(None, make::name(name), self.type_of(value)), + |(name, value)| { + make::record_field(None, make::name(name), self.type_of(name, value)) + }, )) .into(), ); @@ -68,7 +81,7 @@ impl State { ty } - fn type_of(&mut self, value: &serde_json::Value) -> ast::Type { + fn type_of(&mut self, name: &str, value: &serde_json::Value) -> ast::Type { match value { serde_json::Value::Null => make::ty_unit(), serde_json::Value::Bool(_) => make::ty("bool"), @@ -76,12 +89,12 @@ impl State { serde_json::Value::String(_) => make::ty("String"), serde_json::Value::Array(it) => { let ty = match it.iter().next() { - Some(x) => self.type_of(x), + Some(x) => self.type_of(name, x), None => make::ty_placeholder(), }; make::ty(&format!("Vec<{ty}>")) } - serde_json::Value::Object(x) => self.build_struct(x), + serde_json::Value::Object(x) => self.build_struct(name, x), } } } @@ -113,7 +126,7 @@ pub(crate) fn json_in_items( let serialize_resolved = scope_resolve("::serde::Serialize"); state.has_deserialize = deserialize_resolved.is_some(); state.has_serialize = serialize_resolved.is_some(); - state.build_struct(&it); + state.build_struct("Root", &it); edit.insert(range.start(), state.result); acc.push( Diagnostic::new( @@ -218,7 +231,7 @@ mod tests { } #[derive(Serialize)] - struct Struct1{ bar: f64, bay: i64, baz: (), r#box: bool, foo: String } + struct Root1{ bar: f64, bay: i64, baz: (), r#box: bool, foo: String } "#, ); @@ -237,9 +250,44 @@ mod tests { } "#, r#" - struct Struct3{ } - struct Struct2{ kind: String, value: Struct3 } - struct Struct1{ bar: Struct2, foo: String } + struct Value1{ } + struct Bar1{ kind: String, value: Value1 } + struct Root1{ bar: Bar1, foo: String } + + "#, + ); + } + + #[test] + fn naming() { + check_fix( + r#" + {$0 + "user": { + "address": { + "street": "Main St", + "house": 3 + }, + "email": "[email protected]" + }, + "another_user": { + "user": { + "address": { + "street": "Main St", + "house": 3 + }, + "email": "[email protected]" + } + } + } + "#, + r#" + struct Address1{ house: i64, street: String } + struct User1{ address: Address1, email: String } + struct AnotherUser1{ user: User1 } + struct Address2{ house: i64, street: String } + struct User2{ address: Address2, email: String } + struct Root1{ another_user: AnotherUser1, user: User2 } "#, ); @@ -276,9 +324,9 @@ mod tests { use serde::Deserialize; #[derive(Serialize, Deserialize)] - struct Struct2{ x: i64, y: i64 } + struct OfObject1{ x: i64, y: i64 } #[derive(Serialize, Deserialize)] - struct Struct1{ empty: Vec<_>, nested: Vec<Vec<Vec<i64>>>, of_object: Vec<Struct2>, of_string: Vec<String> } + struct Root1{ empty: Vec<_>, nested: Vec<Vec<Vec<i64>>>, of_object: Vec<OfObject1>, of_string: Vec<String> } "#, ); diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index 8d77e566ed..5a3206445c 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{db::ExpandDatabase, HasSource, HirDisplay, HirFileIdExt, Semantics}; +use hir::{db::ExpandDatabase, HasSource, HirDisplay, HirFileIdExt, Semantics, VariantId}; use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; use syntax::{ ast::{self, edit::IndentLevel, make}, @@ -25,7 +25,10 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) } else { Diagnostic::new_with_syntax_node_ptr( ctx, - DiagnosticCode::RustcHardError("E0559"), + match d.variant { + VariantId::EnumVariantId(_) => DiagnosticCode::RustcHardError("E0559"), + _ => DiagnosticCode::RustcHardError("E0560"), + }, "no such field", node, ) diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index 56c8181e84..656d79dc73 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -1,6 +1,6 @@ use hir::{ db::ExpandDatabase, - term_search::{term_search, TermSearchCtx}, + term_search::{term_search, TermSearchConfig, TermSearchCtx}, ClosureStyle, HirDisplay, }; use ide_db::{ @@ -47,7 +47,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist> sema: &ctx.sema, scope: &scope, goal: d.expected.clone(), - config: Default::default(), + config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() }, }; let paths = term_search(&term_search_ctx); @@ -274,7 +274,7 @@ impl Foo for Baz { } fn asd() -> Bar { let a = Baz; - Foo::foo(a) + Foo::foo(_) } ", ); @@ -363,7 +363,7 @@ impl Foo for A { } fn main() { let a = A; - let c: Bar = Foo::foo(&a); + let c: Bar = Foo::foo(_); }"#, ); } diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index c3ced36a69..15543a5d65 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -232,6 +232,7 @@ pub struct DiagnosticsConfig { pub insert_use: InsertUseConfig, pub prefer_no_std: bool, pub prefer_prelude: bool, + pub term_search_fuel: u64, } impl DiagnosticsConfig { @@ -256,6 +257,7 @@ impl DiagnosticsConfig { }, prefer_no_std: false, prefer_prelude: true, + term_search_fuel: 400, } } } @@ -297,11 +299,10 @@ pub fn diagnostics( ) -> Vec<Diagnostic> { let _p = tracing::span!(tracing::Level::INFO, "diagnostics").entered(); let sema = Semantics::new(db); - let parse = db.parse(file_id); let mut res = Vec::new(); // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily. - res.extend(parse.errors().into_iter().take(128).map(|err| { + res.extend(db.parse_errors(file_id).as_deref().into_iter().flatten().take(128).map(|err| { Diagnostic::new( DiagnosticCode::RustcHardError("syntax-error"), format!("Syntax Error: {err}"), @@ -340,7 +341,8 @@ pub fn diagnostics( AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d), AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), AnyDiagnostic::MacroExpansionParseError(d) => { - res.extend(d.errors.iter().take(32).map(|err| { + // FIXME: Point to the correct error span here, not just the macro-call name + res.extend(d.errors.iter().take(16).map(|err| { { Diagnostic::new( DiagnosticCode::RustcHardError("syntax-error"), diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index bb5c2b7913..cd5e95cc1e 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -293,6 +293,7 @@ fn minicore_smoke_test() { // This should be ignored since we conditionally remove code which creates single item use with braces config.disabled.insert("unused_braces".to_owned()); config.disabled.insert("unused_variables".to_owned()); + config.disabled.insert("remove-unnecessary-else".to_owned()); check_diagnostics_with_config(config, &source); } diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 64b4ccc5bd..8d765dfc91 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -214,8 +214,9 @@ pub(crate) fn resolve_doc_path_for_def( Definition::SelfType(it) => it.resolve_doc_path(db, link, ns), Definition::ExternCrateDecl(it) => it.resolve_doc_path(db, link, ns), Definition::BuiltinAttr(_) - | Definition::ToolModule(_) | Definition::BuiltinType(_) + | Definition::BuiltinLifetime(_) + | Definition::ToolModule(_) | Definition::TupleField(_) | Definition::Local(_) | Definition::GenericParam(_) @@ -648,6 +649,7 @@ fn filename_and_frag_for_def( | Definition::TupleField(_) | Definition::Label(_) | Definition::BuiltinAttr(_) + | Definition::BuiltinLifetime(_) | Definition::ToolModule(_) | Definition::DeriveHelper(_) => return None, }; diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index e2d629a02f..1ead045788 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -76,8 +76,6 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< return derive; } - // FIXME: Intermix attribute and bang! expansions - // currently we only recursively expand one of the two types let mut anc = tok.parent_ancestors(); let (name, expanded, kind) = loop { let node = anc.next()?; @@ -86,7 +84,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< if let Some(def) = sema.resolve_attr_macro_call(&item) { break ( def.name(db).display(db).to_string(), - expand_attr_macro_recur(&sema, &item)?, + expand_macro_recur(&sema, &item)?, SyntaxKind::MACRO_ITEMS, ); } @@ -94,11 +92,9 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< if let Some(mac) = ast::MacroCall::cast(node) { let mut name = mac.path()?.segment()?.name_ref()?.to_string(); name.push('!'); - break ( - name, - expand_macro_recur(&sema, &mac)?, - mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS), - ); + let syntax_kind = + mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS); + break (name, expand_macro_recur(&sema, &ast::Item::MacroCall(mac))?, syntax_kind); } }; @@ -112,31 +108,23 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< fn expand_macro_recur( sema: &Semantics<'_, RootDatabase>, - macro_call: &ast::MacroCall, -) -> Option<SyntaxNode> { - let expanded = sema.expand(macro_call)?.clone_for_update(); - expand(sema, expanded, ast::MacroCall::cast, expand_macro_recur) -} - -fn expand_attr_macro_recur( - sema: &Semantics<'_, RootDatabase>, - item: &ast::Item, + macro_call: &ast::Item, ) -> Option<SyntaxNode> { - let expanded = sema.expand_attr_macro(item)?.clone_for_update(); - expand(sema, expanded, ast::Item::cast, expand_attr_macro_recur) + let expanded = match macro_call { + item @ ast::Item::MacroCall(macro_call) => { + sema.expand_attr_macro(item).or_else(|| sema.expand(macro_call))?.clone_for_update() + } + item => sema.expand_attr_macro(item)?.clone_for_update(), + }; + expand(sema, expanded) } -fn expand<T: AstNode>( - sema: &Semantics<'_, RootDatabase>, - expanded: SyntaxNode, - f: impl FnMut(SyntaxNode) -> Option<T>, - exp: impl Fn(&Semantics<'_, RootDatabase>, &T) -> Option<SyntaxNode>, -) -> Option<SyntaxNode> { - let children = expanded.descendants().filter_map(f); +fn expand(sema: &Semantics<'_, RootDatabase>, expanded: SyntaxNode) -> Option<SyntaxNode> { + let children = expanded.descendants().filter_map(ast::Item::cast); let mut replacements = Vec::new(); for child in children { - if let Some(new_node) = exp(sema, &child) { + if let Some(new_node) = expand_macro_recur(sema, &child) { // check if the whole original syntax is replaced if expanded == *child.syntax() { return Some(new_node); diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index ddeeca5f7b..76b80fcefa 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -123,7 +123,7 @@ fn try_lookup_include_path( { return None; } - let path = token.value()?; + let path = token.value().ok()?; let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?; let size = sema.db.file_text(file_id).len().try_into().ok()?; @@ -179,11 +179,11 @@ fn try_filter_trait_item_definition( AssocItem::Const(..) | AssocItem::TypeAlias(..) => { let trait_ = assoc.implemented_trait(db)?; let name = def.name(db)?; - let discri_value = discriminant(&assoc); + let discriminant_value = discriminant(&assoc); trait_ .items(db) .iter() - .filter(|itm| discriminant(*itm) == discri_value) + .filter(|itm| discriminant(*itm) == discriminant_value) .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) .map(|it| it.collect()) } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 95de3c88c8..bdb56081c3 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -15,7 +15,7 @@ use ide_db::{ FxIndexSet, RootDatabase, }; use itertools::{multizip, Itertools}; -use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, T}; +use syntax::{ast, AstNode, SyntaxKind::*, SyntaxNode, T}; use crate::{ doc_links::token_as_doc_comment, @@ -33,7 +33,8 @@ pub struct HoverConfig { pub keywords: bool, pub format: HoverDocFormat, pub max_trait_assoc_items_count: Option<usize>, - pub max_struct_field_count: Option<usize>, + pub max_fields_count: Option<usize>, + pub max_enum_variants_count: Option<usize>, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -297,61 +298,8 @@ fn hover_simple( }) // tokens .or_else(|| { - let mut res = HoverResult::default(); - match_ast! { - match original_token { - ast::String(string) => { - res.markup = Markup::fenced_block_text(format_args!("{}", string.value()?)); - }, - ast::ByteString(string) => { - res.markup = Markup::fenced_block_text(format_args!("{:?}", string.value()?)); - }, - ast::CString(string) => { - let val = string.value()?; - res.markup = Markup::fenced_block_text(format_args!("{}", std::str::from_utf8(val.as_ref()).ok()?)); - }, - ast::Char(char) => { - let mut res = HoverResult::default(); - res.markup = Markup::fenced_block_text(format_args!("{}", char.value()?)); - }, - ast::Byte(byte) => { - res.markup = Markup::fenced_block_text(format_args!("0x{:X}", byte.value()?)); - }, - ast::FloatNumber(num) => { - res.markup = if num.suffix() == Some("f32") { - match num.value_f32() { - Ok(num) => { - Markup::fenced_block_text(format_args!("{num} (bits: 0x{:X})", num.to_bits())) - }, - Err(e) => { - Markup::fenced_block_text(format_args!("{e}")) - }, - } - } else { - match num.value() { - Ok(num) => { - Markup::fenced_block_text(format_args!("{num} (bits: 0x{:X})", num.to_bits())) - }, - Err(e) => { - Markup::fenced_block_text(format_args!("{e}")) - }, - } - }; - }, - ast::IntNumber(num) => { - res.markup = match num.value() { - Ok(num) => { - Markup::fenced_block_text(format_args!("{num} (0x{num:X}|0b{num:b})")) - }, - Err(e) => { - Markup::fenced_block_text(format_args!("{e}")) - }, - }; - }, - _ => return None - } - } - Some(res) + render::literal(sema, original_token.clone()) + .map(|markup| HoverResult { markup, actions: vec![] }) }); result.map(|mut res: HoverResult| { diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 3f0fc85134..3bc17f95e7 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -17,11 +17,7 @@ use ide_db::{ }; use itertools::Itertools; use stdx::format_to; -use syntax::{ - algo, - ast::{self, RecordPat}, - match_ast, AstNode, Direction, SyntaxToken, T, -}; +use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T}; use crate::{ doc_links::{remove_links, rewrite_links}, @@ -276,7 +272,7 @@ pub(super) fn keyword( pub(super) fn struct_rest_pat( sema: &Semantics<'_, RootDatabase>, _config: &HoverConfig, - pattern: &RecordPat, + pattern: &ast::RecordPat, ) -> HoverResult { let missing_fields = sema.record_pattern_missing_fields(pattern); @@ -411,8 +407,21 @@ pub(super) fn definition( Definition::Trait(trait_) => { trait_.display_limited(db, config.max_trait_assoc_items_count).to_string() } - Definition::Adt(Adt::Struct(struct_)) => { - struct_.display_limited(db, config.max_struct_field_count).to_string() + Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => { + adt.display_limited(db, config.max_fields_count).to_string() + } + Definition::Variant(variant) => { + variant.display_limited(db, config.max_fields_count).to_string() + } + Definition::Adt(adt @ Adt::Enum(_)) => { + adt.display_limited(db, config.max_enum_variants_count).to_string() + } + Definition::SelfType(impl_def) => { + let self_ty = &impl_def.self_ty(db); + match self_ty.as_adt() { + Some(adt) => adt.display_limited(db, config.max_fields_count).to_string(), + None => self_ty.display(db).to_string(), + } } Definition::Macro(it) => { let mut label = it.display(db).to_string(); @@ -513,6 +522,60 @@ pub(super) fn definition( markup(docs.map(Into::into), desc, mod_path) } +pub(super) fn literal(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Markup> { + let lit = token.parent().and_then(ast::Literal::cast)?; + let ty = if let Some(p) = lit.syntax().parent().and_then(ast::Pat::cast) { + sema.type_of_pat(&p)? + } else { + sema.type_of_expr(&ast::Expr::Literal(lit))? + } + .original; + + let value = match_ast! { + match token { + ast::String(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string), + ast::ByteString(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("{it:?}")), + ast::CString(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(|it| std::str::from_utf8(it).map_or_else(|e| format!("{e:?}"), ToOwned::to_owned)), + ast::Char(char) => char .value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string), + ast::Byte(byte) => byte .value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("0x{it:X}")), + ast::FloatNumber(num) => { + let (text, _) = num.split_into_parts(); + let text = text.replace('_', ""); + if ty.as_builtin().map(|it| it.is_f32()).unwrap_or(false) { + match text.parse::<f32>() { + Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), + Err(e) => Err(e.to_string()), + } + } else { + match text.parse::<f64>() { + Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), + Err(e) => Err(e.to_string()), + } + } + }, + ast::IntNumber(num) => match num.value() { + Ok(num) => Ok(format!("{num} (0x{num:X}|0b{num:b})")), + Err(e) => Err(e.to_string()), + }, + _ => return None + } + }; + let ty = ty.display(sema.db); + + let mut s = format!("```rust\n{ty}\n```\n___\n\n"); + match value { + Ok(value) => { + if let Some(newline) = value.find('\n') { + format_to!(s, "value of literal (truncated up to newline): {}", &value[..newline]) + } else { + format_to!(s, "value of literal: {value}") + } + } + Err(error) => format_to!(s, "invalid literal: {error}"), + } + Some(s.into()) +} + fn render_notable_trait_comment( db: &RootDatabase, notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)], @@ -693,15 +756,7 @@ fn closure_ty( } fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { - if matches!( - def, - Definition::GenericParam(_) - | Definition::BuiltinType(_) - | Definition::Local(_) - | Definition::Label(_) - | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) - ) { + if matches!(def, Definition::GenericParam(_) | Definition::Local(_) | Definition::Label(_)) { return None; } def.module(db).map(|module| path(db, module, definition_owner_name(db, def))) diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 6bbc8b380d..20d07bf991 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -18,7 +18,8 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { format: HoverDocFormat::Markdown, keywords: true, max_trait_assoc_items_count: None, - max_struct_field_count: None, + max_fields_count: Some(5), + max_enum_variants_count: Some(5), }; fn check_hover_no_result(ra_fixture: &str) { @@ -51,13 +52,43 @@ fn check(ra_fixture: &str, expect: Expect) { } #[track_caller] -fn check_hover_struct_limit(count: usize, ra_fixture: &str, expect: Expect) { +fn check_hover_fields_limit( + fields_count: impl Into<Option<usize>>, + ra_fixture: &str, + expect: Expect, +) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( &HoverConfig { links_in_hover: true, - max_struct_field_count: Some(count), + max_fields_count: fields_count.into(), + ..HOVER_BASE_CONFIG + }, + FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, + ) + .unwrap() + .unwrap(); + + let content = analysis.db.file_text(position.file_id); + let hovered_element = &content[hover.range]; + + let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); + expect.assert_eq(&actual) +} + +#[track_caller] +fn check_hover_enum_variants_limit( + variants_count: impl Into<Option<usize>>, + ra_fixture: &str, + expect: Expect, +) { + let (analysis, position) = fixture::position(ra_fixture); + let hover = analysis + .hover( + &HoverConfig { + links_in_hover: true, + max_enum_variants_count: variants_count.into(), ..HOVER_BASE_CONFIG }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, @@ -876,7 +907,9 @@ struct Foo$0 { field: u32 } ```rust // size = 4, align = 4 - struct Foo + struct Foo { + field: u32, + } ``` "#]], ); @@ -896,6 +929,9 @@ struct Foo$0 where u32: Copy { field: u32 } struct Foo where u32: Copy, + { + field: u32, + } ``` "#]], ); @@ -903,7 +939,7 @@ struct Foo$0 where u32: Copy { field: u32 } #[test] fn hover_record_struct_limit() { - check_hover_struct_limit( + check_hover_fields_limit( 3, r#" struct Foo$0 { a: u32, b: i32, c: i32 } @@ -917,7 +953,7 @@ fn hover_record_struct_limit() { ```rust // size = 12 (0xC), align = 4 - struct Foo { + struct Foo { a: u32, b: i32, c: i32, @@ -925,7 +961,7 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_struct_limit( + check_hover_fields_limit( 3, r#" struct Foo$0 { a: u32 } @@ -939,13 +975,13 @@ fn hover_record_struct_limit() { ```rust // size = 4, align = 4 - struct Foo { + struct Foo { a: u32, } ``` "#]], ); - check_hover_struct_limit( + check_hover_fields_limit( 3, r#" struct Foo$0 { a: u32, b: i32, c: i32, d: u32 } @@ -959,7 +995,7 @@ fn hover_record_struct_limit() { ```rust // size = 16 (0x10), align = 4 - struct Foo { + struct Foo { a: u32, b: i32, c: i32, @@ -968,6 +1004,338 @@ fn hover_record_struct_limit() { ``` "#]], ); + check_hover_fields_limit( + None, + r#" + struct Foo$0 { a: u32, b: i32, c: i32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4 + struct Foo + ``` + "#]], + ); + check_hover_fields_limit( + 0, + r#" + struct Foo$0 { a: u32, b: i32, c: i32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4 + struct Foo { /* … */ } + ``` + "#]], + ); + + // No extra spaces within `{}` when there are no fields + check_hover_fields_limit( + 5, + r#" + struct Foo$0 {} + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 0, align = 1 + struct Foo {} + ``` + "#]], + ); +} + +#[test] +fn hover_record_variant_limit() { + check_hover_fields_limit( + 3, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 12 (0xC), align = 4 + A { a: u32, b: i32, c: i32, } + ``` + "#]], + ); + check_hover_fields_limit( + 3, + r#" + enum Foo { A$0 { a: u32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 4, align = 4 + A { a: u32, } + ``` + "#]], + ); + check_hover_fields_limit( + 3, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32, d: u32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 16 (0x10), align = 4 + A { a: u32, b: i32, c: i32, /* … */ } + ``` + "#]], + ); + check_hover_fields_limit( + None, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 12 (0xC), align = 4 + A + ``` + "#]], + ); + check_hover_fields_limit( + 0, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 12 (0xC), align = 4 + A { /* … */ } + ``` + "#]], + ); +} + +#[test] +fn hover_enum_limit() { + check_hover_enum_variants_limit( + 5, + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo { + A, + B, + } + ``` + "#]], + ); + check_hover_enum_variants_limit( + 1, + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo { + A, + /* … */ + } + ``` + "#]], + ); + check_hover_enum_variants_limit( + 0, + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo { /* … */ } + ``` + "#]], + ); + check_hover_enum_variants_limit( + None, + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo + ``` + "#]], + ); + check_hover_enum_variants_limit( + 7, + r#"enum Enum$0 { + Variant {}, + Variant2 { field: i32 }, + Variant3 { field: i32, field2: i32 }, + Variant4(), + Variant5(i32), + Variant6(i32, i32), + Variant7, + Variant8, + }"#, + expect![[r#" + *Enum* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4, niches = 4294967288 + enum Enum { + Variant {}, + Variant2 { /* … */ }, + Variant3 { /* … */ }, + Variant4(), + Variant5( /* … */ ), + Variant6( /* … */ ), + Variant7, + /* … */ + } + ``` + "#]], + ); +} + +#[test] +fn hover_union_limit() { + check_hover_fields_limit( + 5, + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo { + a: u32, + b: i32, + } + ``` + "#]], + ); + check_hover_fields_limit( + 1, + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo { + a: u32, + /* … */ + } + ``` + "#]], + ); + check_hover_fields_limit( + 0, + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo { /* … */ } + ``` + "#]], + ); + check_hover_fields_limit( + None, + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo + ``` + "#]], + ); } #[test] @@ -1431,11 +1799,14 @@ impl Thing { ``` ```rust - struct Thing + struct Thing { + x: u32, + } ``` "#]], ); - check( + check_hover_fields_limit( + None, r#" struct Thing { x: u32 } impl Thing { @@ -1456,9 +1827,9 @@ impl Thing { ); check( r#" -enum Thing { A } +struct Thing { x: u32 } impl Thing { - pub fn new() -> Self$0 { Thing::A } + fn new() -> Self$0 { Self { x: 0 } } } "#, expect![[r#" @@ -1469,8 +1840,8 @@ impl Thing { ``` ```rust - enum Thing { - A, + struct Thing { + x: u32, } ``` "#]], @@ -1479,22 +1850,43 @@ impl Thing { r#" enum Thing { A } impl Thing { + pub fn new() -> Self$0 { Thing::A } +} +"#, + expect![[r#" + *Self* + + ```rust + test + ``` + + ```rust + enum Thing { + A, + } + ``` + "#]], + ); + check( + r#" +enum Thing { A } +impl Thing { pub fn thing(a: Self$0) {} } "#, expect![[r#" - *Self* + *Self* - ```rust - test - ``` + ```rust + test + ``` - ```rust - enum Thing { - A, - } - ``` - "#]], + ```rust + enum Thing { + A, + } + ``` + "#]], ); check( r#" @@ -2382,8 +2774,8 @@ fn test_hover_layout_of_enum() { ```rust // size = 16 (0x10), align = 8, niches = 254 enum Foo { - Variant1(u8, u16), - Variant2(i32, u8, i64), + Variant1( /* … */ ), + Variant2( /* … */ ), } ``` "#]], @@ -4049,7 +4441,7 @@ fn foo() { ```rust 'label ``` - "#]], + "#]], ); } @@ -4063,7 +4455,17 @@ fn hover_lifetime() { ```rust 'lifetime ``` - "#]], + "#]], + ); + check( + r#"fn foo(_: &'static$0 ()) {}"#, + expect![[r#" + *'static* + + ```rust + 'static + ``` + "#]], ); } @@ -6554,7 +6956,7 @@ enum Enum { ```rust // size = 4, align = 4 - RecordV { field: u32 } + RecordV { field: u32, } ``` "#]], ); @@ -7398,9 +7800,12 @@ fn main() { "#, expect![[r#" *"🦀\u{1f980}\\\x41"* - ```text - 🦀🦀\A + ```rust + &str ``` + ___ + + value of literal: 🦀🦀\A "#]], ); check( @@ -7411,9 +7816,34 @@ fn main() { "#, expect![[r#" *r"🦀\u{1f980}\\\x41"* - ```text - 🦀\u{1f980}\\\x41 + ```rust + &str ``` + ___ + + value of literal: 🦀\u{1f980}\\\x41 + "#]], + ); + check( + r#" +fn main() { + $0r"🦀\u{1f980}\\\x41 + + +fsdghs"; +} +"#, + expect![[r#" + *r"🦀\u{1f980}\\\x41 + + + fsdghs"* + ```rust + &str + ``` + ___ + + value of literal (truncated up to newline): 🦀\u{1f980}\\\x41 "#]], ); } @@ -7428,9 +7858,12 @@ fn main() { "#, expect![[r#" *c"🦀\u{1f980}\\\x41"* - ```text - 🦀🦀\A + ```rust + &{unknown} ``` + ___ + + value of literal: 🦀🦀\A "#]], ); } @@ -7445,9 +7878,12 @@ fn main() { "#, expect![[r#" *b"\xF0\x9F\xA6\x80\\"* - ```text - [240, 159, 166, 128, 92] + ```rust + &[u8; 5] ``` + ___ + + value of literal: [240, 159, 166, 128, 92] "#]], ); check( @@ -7458,9 +7894,12 @@ fn main() { "#, expect![[r#" *br"\xF0\x9F\xA6\x80\\"* - ```text - [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] + ```rust + &[u8; 18] ``` + ___ + + value of literal: [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] "#]], ); } @@ -7475,9 +7914,12 @@ fn main() { "#, expect![[r#" *b'\xF0'* - ```text - 0xF0 + ```rust + u8 ``` + ___ + + value of literal: 0xF0 "#]], ); check( @@ -7488,9 +7930,12 @@ fn main() { "#, expect![[r#" *b'\\'* - ```text - 0x5C + ```rust + u8 ``` + ___ + + value of literal: 0x5C "#]], ); } @@ -7505,7 +7950,12 @@ fn main() { "#, expect![[r#" *'\x41'* + ```rust + char + ``` + ___ + value of literal: A "#]], ); check( @@ -7516,7 +7966,12 @@ fn main() { "#, expect![[r#" *'\\'* + ```rust + char + ``` + ___ + value of literal: \ "#]], ); check( @@ -7527,7 +7982,12 @@ fn main() { "#, expect![[r#" *'\u{1f980}'* + ```rust + char + ``` + ___ + value of literal: 🦀 "#]], ); } @@ -7542,9 +8002,12 @@ fn main() { "#, expect![[r#" *1.0* - ```text - 1 (bits: 0x3FF0000000000000) + ```rust + f64 ``` + ___ + + value of literal: 1 (bits: 0x3FF0000000000000) "#]], ); check( @@ -7555,9 +8018,12 @@ fn main() { "#, expect![[r#" *1.0f32* - ```text - 1 (bits: 0x3F800000) + ```rust + f32 ``` + ___ + + value of literal: 1 (bits: 0x3F800000) "#]], ); check( @@ -7568,9 +8034,12 @@ fn main() { "#, expect![[r#" *134e12* - ```text - 134000000000000 (bits: 0x42DE77D399980000) + ```rust + f64 ``` + ___ + + value of literal: 134000000000000 (bits: 0x42DE77D399980000) "#]], ); check( @@ -7581,9 +8050,12 @@ fn main() { "#, expect![[r#" *1523527134274733643531312.0* - ```text - 1523527134274733600000000 (bits: 0x44F429E9249F629B) + ```rust + f64 ``` + ___ + + value of literal: 1523527134274733600000000 (bits: 0x44F429E9249F629B) "#]], ); check( @@ -7594,9 +8066,12 @@ fn main() { "#, expect![[r#" *0.1ea123* - ```text - invalid float literal + ```rust + f64 ``` + ___ + + invalid literal: invalid float literal "#]], ); } @@ -7611,9 +8086,12 @@ fn main() { "#, expect![[r#" *34325236457856836345234* - ```text - 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) + ```rust + i32 ``` + ___ + + value of literal: 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) "#]], ); check( @@ -7624,9 +8102,12 @@ fn main() { "#, expect![[r#" *134_123424_21* - ```text - 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) + ```rust + i32 ``` + ___ + + value of literal: 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) "#]], ); check( @@ -7637,9 +8118,12 @@ fn main() { "#, expect![[r#" *0x12423423* - ```text - 306328611 (0x12423423|0b10010010000100011010000100011) + ```rust + i32 ``` + ___ + + value of literal: 306328611 (0x12423423|0b10010010000100011010000100011) "#]], ); check( @@ -7650,9 +8134,12 @@ fn main() { "#, expect![[r#" *0b1111_1111* - ```text - 255 (0xFF|0b11111111) + ```rust + i32 ``` + ___ + + value of literal: 255 (0xFF|0b11111111) "#]], ); check( @@ -7663,9 +8150,12 @@ fn main() { "#, expect![[r#" *0o12345* - ```text - 5349 (0x14E5|0b1010011100101) + ```rust + i32 ``` + ___ + + value of literal: 5349 (0x14E5|0b1010011100101) "#]], ); check( @@ -7676,9 +8166,12 @@ fn main() { "#, expect![[r#" *0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_F* - ```text - number too large to fit in target type + ```rust + i32 ``` + ___ + + invalid literal: number too large to fit in target type "#]], ); } @@ -7864,8 +8357,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 6290..6498, - focus_range: 6355..6361, + full_range: 7791..7999, + focus_range: 7856..7862, name: "Future", kind: Trait, container_name: "future", @@ -7878,8 +8371,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 7128..7594, - focus_range: 7172..7180, + full_range: 8629..9095, + focus_range: 8673..8681, name: "Iterator", kind: Trait, container_name: "iterator", @@ -7936,7 +8429,9 @@ struct Pedro$0<'a> { ```rust // size = 16 (0x10), align = 8, niches = 1 - struct Pedro<'a> + struct Pedro<'a> { + hola: &str, + } ``` "#]], ) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 15eecd1b54..dda5a005a7 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -496,7 +496,7 @@ pub(crate) fn inlay_hints_resolve( config: &InlayHintsConfig, hasher: impl Fn(&InlayHint) -> u64, ) -> Option<InlayHint> { - let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); + let _p = tracing::span!(tracing::Level::INFO, "inlay_hints_resolve").entered(); let sema = Semantics::new(db); let file = sema.parse(file_id); let file = file.syntax(); diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 07b9f9cc1f..0cb8c485b2 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -332,6 +332,25 @@ fn main(a: SliceIter<'_, Container>) { } #[test] + fn lt_hints() { + check_types( + r#" +struct S<'lt>; + +fn f<'a>() { + let x = S::<'static>; + //^ S<'static> + let y = S::<'_>; + //^ S + let z = S::<'a>; + //^ S<'a> + +} +"#, + ); + } + + #[test] fn fn_hints() { check_types( r#" @@ -341,7 +360,7 @@ fn foo1() -> impl Fn(f64) { loop {} } fn foo2() -> impl Fn(f64, f64) { loop {} } fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} } fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} } -fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} } +fn foo5() -> &'static for<'a> dyn Fn(&'a dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} } fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} } fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} } diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 08760c0d88..68854c33ce 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -211,6 +211,7 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati } } Definition::BuiltinType(..) => Type, + Definition::BuiltinLifetime(_) => TypeParameter, Definition::SelfType(..) => TypeAlias, Definition::GenericParam(..) => TypeParameter, Definition::Local(it) => { @@ -316,6 +317,7 @@ pub(crate) fn def_to_moniker( Definition::GenericParam(_) | Definition::Label(_) | Definition::DeriveHelper(_) + | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) | Definition::ToolModule(_) => return None, diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 2123c98605..fc836d5540 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -235,9 +235,11 @@ impl TryToNav for Definition { Definition::TraitAlias(it) => it.try_to_nav(db), Definition::TypeAlias(it) => it.try_to_nav(db), Definition::ExternCrateDecl(it) => Some(it.try_to_nav(db)?), - Definition::BuiltinType(_) | Definition::TupleField(_) => None, - Definition::ToolModule(_) => None, - Definition::BuiltinAttr(_) => None, + Definition::BuiltinLifetime(_) + | Definition::BuiltinType(_) + | Definition::TupleField(_) + | Definition::ToolModule(_) + | Definition::BuiltinAttr(_) => None, // FIXME: The focus range should be set to the helper declaration Definition::DeriveHelper(it) => it.derive().try_to_nav(db), } diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index b6c6753755..64ffa59101 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -15,6 +15,7 @@ use ide_db::{ FxHashMap, FxHashSet, RootDatabase, SymbolKind, }; use itertools::Itertools; +use span::TextSize; use stdx::{always, format_to}; use syntax::{ ast::{self, AstNode}, @@ -48,16 +49,15 @@ impl fmt::Display for TestId { #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum RunnableKind { - Test { test_id: TestId, attr: TestAttr }, TestMod { path: String }, + Test { test_id: TestId, attr: TestAttr }, Bench { test_id: TestId }, DocTest { test_id: TestId }, Bin, } -#[cfg(test)] -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -enum RunnableTestKind { +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +enum RunnableDiscKind { Test, TestMod, DocTest, @@ -65,6 +65,18 @@ enum RunnableTestKind { Bin, } +impl RunnableKind { + fn disc(&self) -> RunnableDiscKind { + match self { + RunnableKind::TestMod { .. } => RunnableDiscKind::TestMod, + RunnableKind::Test { .. } => RunnableDiscKind::Test, + RunnableKind::DocTest { .. } => RunnableDiscKind::DocTest, + RunnableKind::Bench { .. } => RunnableDiscKind::Bench, + RunnableKind::Bin => RunnableDiscKind::Bin, + } + } +} + impl Runnable { // test package::module::testname pub fn label(&self, target: Option<String>) -> String { @@ -97,17 +109,6 @@ impl Runnable { s.push_str(suffix); s } - - #[cfg(test)] - fn test_kind(&self) -> RunnableTestKind { - match &self.kind { - RunnableKind::TestMod { .. } => RunnableTestKind::TestMod, - RunnableKind::Test { .. } => RunnableTestKind::Test, - RunnableKind::DocTest { .. } => RunnableTestKind::DocTest, - RunnableKind::Bench { .. } => RunnableTestKind::Bench, - RunnableKind::Bin => RunnableTestKind::Bin, - } - } } // Feature: Run @@ -193,6 +194,20 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { r }) })); + res.sort_by(|Runnable { nav, kind, .. }, Runnable { nav: nav_b, kind: kind_b, .. }| { + // full_range.start < focus_range.start < name, should give us a decent unique ordering + nav.full_range + .start() + .cmp(&nav_b.full_range.start()) + .then_with(|| { + let t_0 = || TextSize::from(0); + nav.focus_range + .map_or_else(t_0, |it| it.start()) + .cmp(&nav_b.focus_range.map_or_else(t_0, |it| it.start())) + }) + .then_with(|| kind.disc().cmp(&kind_b.disc())) + .then_with(|| nav.name.cmp(&nav_b.name)) + }); res } @@ -571,13 +586,12 @@ mod tests { fn check(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); - let mut runnables = analysis.runnables(position.file_id).unwrap(); - runnables.sort_by_key(|it| (it.nav.full_range.start(), it.nav.name.clone())); - - let result = runnables + let result = analysis + .runnables(position.file_id) + .unwrap() .into_iter() .map(|runnable| { - let mut a = format!("({:?}, {:?}", runnable.test_kind(), runnable.nav); + let mut a = format!("({:?}, {:?}", runnable.kind.disc(), runnable.nav); if runnable.use_name_in_title { a.push_str(", true"); } @@ -609,6 +623,9 @@ fn main() {} #[export_name = "main"] fn __cortex_m_rt_main_trampoline() {} +#[unsafe(export_name = "main")] +fn __cortex_m_rt_main_trampoline_unsafe() {} + #[test] fn test_foo() {} @@ -628,13 +645,14 @@ mod not_a_root { "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..253, name: \"\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..331, name: \"\", kind: Module })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 15..76, focus_range: 42..71, name: \"__cortex_m_rt_main_trampoline\", kind: Function })", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 78..102, focus_range: 89..97, name: \"test_foo\", kind: Function })", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 104..155, focus_range: 136..150, name: \"test_full_path\", kind: Function })", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 157..191, focus_range: 178..186, name: \"test_foo\", kind: Function })", - "(Bench, NavigationTarget { file_id: FileId(0), full_range: 193..215, focus_range: 205..210, name: \"bench\", kind: Function })", + "(Bin, NavigationTarget { file_id: FileId(0), full_range: 78..154, focus_range: 113..149, name: \"__cortex_m_rt_main_trampoline_unsafe\", kind: Function })", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 156..180, focus_range: 167..175, name: \"test_foo\", kind: Function })", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 182..233, focus_range: 214..228, name: \"test_full_path\", kind: Function })", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 235..269, focus_range: 256..264, name: \"test_foo\", kind: Function })", + "(Bench, NavigationTarget { file_id: FileId(0), full_range: 271..293, focus_range: 283..288, name: \"bench\", kind: Function })", ] "#]], ); diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index ca013da709..0d2311b6e9 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -167,7 +167,8 @@ impl StaticIndex<'_> { keywords: true, format: crate::HoverDocFormat::Markdown, max_trait_assoc_items_count: None, - max_struct_field_count: None, + max_fields_count: Some(5), + max_enum_variants_count: Some(5), }; let tokens = tokens.filter(|token| { matches!( diff --git a/crates/ide/src/syntax_highlighting/escape.rs b/crates/ide/src/syntax_highlighting/escape.rs index 5913ca5e45..0439e509d2 100644 --- a/crates/ide/src/syntax_highlighting/escape.rs +++ b/crates/ide/src/syntax_highlighting/escape.rs @@ -25,7 +25,7 @@ pub(super) fn highlight_escape_string<T: IsString>( } pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) { - if char.value().is_none() { + if char.value().is_err() { // We do not emit invalid escapes highlighting here. The lexer would likely be in a bad // state and this token contains junks, since `'` is not a reliable delimiter (consider // lifetimes). Nonetheless, parser errors should already be emitted. @@ -48,7 +48,7 @@ pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: } pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte, start: TextSize) { - if byte.value().is_none() { + if byte.value().is_err() { // See `highlight_escape_char` for why no error highlighting here. return; } diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index a72f505eb8..3b784ebec3 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -484,6 +484,7 @@ pub(super) fn highlight_def( h } Definition::BuiltinType(_) => Highlight::new(HlTag::BuiltinType), + Definition::BuiltinLifetime(_) => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)), Definition::Static(s) => { let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static)); @@ -542,13 +543,14 @@ pub(super) fn highlight_def( let def_crate = def.krate(db); let is_from_other_crate = def_crate != Some(krate); let is_from_builtin_crate = def_crate.map_or(false, |def_crate| def_crate.is_builtin(db)); - let is_builtin_type = matches!(def, Definition::BuiltinType(_)); - let is_public = def.visibility(db) == Some(hir::Visibility::Public); - - match (is_from_other_crate, is_builtin_type, is_public) { - (true, false, _) => h |= HlMod::Library, - (false, _, true) => h |= HlMod::Public, - _ => {} + let is_builtin = matches!( + def, + Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) + ); + match is_from_other_crate { + true if !is_builtin => h |= HlMod::Library, + false if def.visibility(db) == Some(hir::Visibility::Public) => h |= HlMod::Public, + _ => (), } if is_from_builtin_crate { diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 6bf13ffd06..f9b8a22a3c 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -30,7 +30,7 @@ pub(super) fn ra_fixture( if !active_parameter.ident().map_or(false, |name| name.text().starts_with("ra_fixture")) { return None; } - let value = literal.value()?; + let value = literal.value().ok()?; if let Some(range) = literal.open_quote_text_range() { hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None }) @@ -299,6 +299,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::Trait(_) => SymbolKind::Trait, Definition::TraitAlias(_) => SymbolKind::TraitAlias, Definition::TypeAlias(_) => SymbolKind::TypeAlias, + Definition::BuiltinLifetime(_) => SymbolKind::LifetimeParam, Definition::BuiltinType(_) => return HlTag::BuiltinType, Definition::Macro(_) => SymbolKind::Macro, Definition::Field(_) | Definition::TupleField(_) => SymbolKind::Field, diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index de902b5137..cad5a8b593 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -45,7 +45,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } </style> -<pre><code><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">allow</span><span class="parenthesis attribute">(</span><span class="none attribute">dead_code</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> +<pre><code><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">allow</span><span class="parenthesis attribute">(</span><span class="none attribute">dead_code</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute library">rustfmt</span><span class="operator attribute">::</span><span class="tool_module attribute library">skip</span><span class="attribute_bracket attribute">]</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="module attribute crate_root library">proc_macros</span><span class="operator attribute">::</span><span class="attribute attribute library">identity</span><span class="attribute_bracket attribute">]</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library">Default</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index 366895ce7e..893e3c0675 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -49,5 +49,5 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <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">foo</span> <span class="operator">=</span> <span class="enum_variant default_library library">Some</span><span class="parenthesis">(</span><span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="keyword">let</span> <span class="variable declaration">nums</span> <span class="operator">=</span> <span class="module default_library library">iter</span><span class="operator">::</span><span class="function default_library library">repeat</span><span class="parenthesis">(</span><span class="variable">foo</span><span class="operator">.</span><span class="method consuming default_library library">unwrap</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="variable declaration">nums</span> <span class="operator">=</span> <span class="module default_library library">iter</span><span class="operator">::</span><span class="function default_library library">repeat</span><span class="parenthesis">(</span><span class="variable">foo</span><span class="operator">.</span><span class="method default_library library">unwrap</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span></code></pre>
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index b2ca6e1ca4..35650bbe87 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -79,7 +79,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="comment documentation">/// # Examples</span> <span class="comment documentation">///</span> <span class="comment documentation">/// ```</span> - <span class="comment documentation">///</span><span class="comment documentation"> #</span><span class="none injected"> </span><span class="attribute_bracket attribute injected">#</span><span class="attribute_bracket attribute injected">!</span><span class="attribute_bracket attribute injected">[</span><span class="builtin_attr attribute injected library">allow</span><span class="parenthesis attribute injected">(</span><span class="none attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute_bracket attribute injected">]</span> + <span class="comment documentation">///</span><span class="comment documentation"> #</span><span class="none injected"> </span><span class="attribute_bracket attribute injected">#</span><span class="attribute_bracket attribute injected">!</span><span class="attribute_bracket attribute injected">[</span><span class="builtin_attr attribute injected">allow</span><span class="parenthesis attribute injected">(</span><span class="none attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute_bracket attribute injected">]</span> <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="keyword injected">mut</span><span class="none injected"> </span><span class="variable declaration injected mutable">foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> <span class="comment documentation">/// ```</span> <span class="keyword">pub</span> <span class="keyword const">const</span> <span class="keyword">fn</span> <span class="function associated const declaration public static">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-></span> <span class="struct">Foo</span> <span class="brace">{</span> @@ -162,12 +162,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="comment documentation">///</span> <span class="comment documentation">/// ```</span> <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute_bracket attribute">]</span> +<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> +<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute_bracket attribute">]</span> <span class="comment documentation">/// ```</span> <span class="comment documentation">///</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> +<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> +<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected macro">[</span><span class="numeric_literal injected macro">1</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">2</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">3</span><span class="bracket injected macro">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> <span class="comment documentation">/// ```</span> <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 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 5234d362c2..413edb6d65 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -49,13 +49,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span> <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration public">ops</span> <span class="brace">{</span> - <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_once"</span><span class="attribute_bracket attribute">]</span> + <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_once"</span><span class="attribute_bracket attribute">]</span> <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnOnce</span><span class="angle"><</span><span class="type_param declaration">Args</span><span class="angle">></span> <span class="brace">{</span><span class="brace">}</span> - <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_mut"</span><span class="attribute_bracket attribute">]</span> + <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_mut"</span><span class="attribute_bracket attribute">]</span> <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnMut</span><span class="angle"><</span><span class="type_param declaration">Args</span><span class="angle">></span><span class="colon">:</span> <span class="trait public">FnOnce</span><span class="angle"><</span><span class="type_param">Args</span><span class="angle">></span> <span class="brace">{</span><span class="brace">}</span> - <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn"</span><span class="attribute_bracket attribute">]</span> + <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn"</span><span class="attribute_bracket attribute">]</span> <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">Fn</span><span class="angle"><</span><span class="type_param declaration">Args</span><span class="angle">></span><span class="colon">:</span> <span class="trait public">FnMut</span><span class="angle"><</span><span class="type_param">Args</span><span class="angle">></span> <span class="brace">{</span><span class="brace">}</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 7a07d17b27..22706dea1f 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -158,9 +158,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span> diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 15d6be6334..be6176894b 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -69,7 +69,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="method associated declaration reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> <span class="brace">}</span> -<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">repr</span><span class="parenthesis attribute">(</span><span class="none attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> +<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">repr</span><span class="parenthesis attribute">(</span><span class="none attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span> <span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="brace">{</span> <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span> <span class="brace">}</span> diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index 31b0c8cdec..76940ab57a 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -17,6 +17,7 @@ use itertools::Itertools; use proc_macro_api::{MacroDylib, ProcMacroServer}; use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; use span::Span; +use tracing::{instrument, Level}; use vfs::{file_set::FileSetConfig, loader::Handle, AbsPath, AbsPathBuf, VfsPath}; pub struct LoadCargoConfig { @@ -50,6 +51,7 @@ pub fn load_workspace_at( load_workspace(workspace, &cargo_config.extra_env, load_config) } +#[instrument(skip_all)] pub fn load_workspace( ws: ProjectWorkspace, extra_env: &FxHashMap<String, String>, @@ -66,9 +68,9 @@ pub fn load_workspace( let proc_macro_server = match &load_config.with_proc_macro_server { ProcMacroServerChoice::Sysroot => ws .find_sysroot_proc_macro_srv() - .and_then(|it| ProcMacroServer::spawn(it, extra_env).map_err(Into::into)), + .and_then(|it| ProcMacroServer::spawn(&it, extra_env).map_err(Into::into)), ProcMacroServerChoice::Explicit(path) => { - ProcMacroServer::spawn(path.clone(), extra_env).map_err(Into::into) + ProcMacroServer::spawn(path, extra_env).map_err(Into::into) } ProcMacroServerChoice::None => Err(anyhow::format_err!("proc macro server disabled")), }; @@ -333,9 +335,7 @@ fn load_crate_graph( vfs: &mut vfs::Vfs, receiver: &Receiver<vfs::loader::Message>, ) -> RootDatabase { - let (ProjectWorkspace::Cargo { toolchain, target_layout, .. } - | ProjectWorkspace::Json { toolchain, target_layout, .. } - | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws; + let ProjectWorkspace { toolchain, target_layout, .. } = ws; let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); let mut db = RootDatabase::new(lru_cap); @@ -352,6 +352,8 @@ fn load_crate_graph( } } vfs::loader::Message::Loaded { files } | vfs::loader::Message::Changed { files } => { + let _p = tracing::span!(Level::INFO, "load_cargo::load_crate_craph/LoadedChanged") + .entered(); for (path, contents) in files { vfs.set_file_contents(path.into(), contents); } @@ -359,8 +361,8 @@ fn load_crate_graph( } } let changes = vfs.take_changes(); - for file in changes { - if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change { + 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)) } diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index f4bbaef7af..f73e188c79 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs @@ -10,7 +10,7 @@ use test_utils::{bench, bench_fixture, skip_slow_tests}; use crate::{ parser::{MetaVarKind, Op, RepeatKind, Separator}, - syntax_node_to_token_tree, DeclarativeMacro, DummyTestSpanMap, DUMMY, + syntax_node_to_token_tree, DeclarativeMacro, DocCommentDesugarMode, DummyTestSpanMap, DUMMY, }; #[test] @@ -78,6 +78,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree<Span>> { rule.token_tree().unwrap().syntax(), DummyTestSpanMap, DUMMY, + DocCommentDesugarMode::Mbe, ); (id, def_tt) }) diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index d5de56312a..6920832dd2 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -34,7 +34,7 @@ pub use tt::{Delimiter, DelimiterKind, Punct}; pub use crate::syntax_bridge::{ parse_exprs_with_sep, parse_to_token_tree, parse_to_token_tree_static_span, syntax_node_to_token_tree, syntax_node_to_token_tree_modified, token_tree_to_syntax_node, - SpanMapper, + DocCommentDesugarMode, SpanMapper, }; pub use crate::syntax_bridge::dummy_test_span_utils::*; diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 3230eeb5bd..412e492176 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -69,18 +69,28 @@ pub(crate) mod dummy_test_span_utils { } } +/// Doc comment desugaring differs between mbe and proc-macros. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum DocCommentDesugarMode { + /// Desugars doc comments as quoted raw strings + Mbe, + /// Desugars doc comments as quoted strings + ProcMacro, +} + /// Converts a syntax tree to a [`tt::Subtree`] using the provided span map to populate the /// subtree's spans. pub fn syntax_node_to_token_tree<Ctx, SpanMap>( node: &SyntaxNode, map: SpanMap, span: SpanData<Ctx>, + mode: DocCommentDesugarMode, ) -> tt::Subtree<SpanData<Ctx>> where SpanData<Ctx>: Copy + fmt::Debug, SpanMap: SpanMapper<SpanData<Ctx>>, { - let mut c = Converter::new(node, map, Default::default(), Default::default(), span); + let mut c = Converter::new(node, map, Default::default(), Default::default(), span, mode); convert_tokens(&mut c) } @@ -93,12 +103,13 @@ pub fn syntax_node_to_token_tree_modified<Ctx, SpanMap>( append: FxHashMap<SyntaxElement, Vec<tt::Leaf<SpanData<Ctx>>>>, remove: FxHashSet<SyntaxElement>, call_site: SpanData<Ctx>, + mode: DocCommentDesugarMode, ) -> tt::Subtree<SpanData<Ctx>> where SpanMap: SpanMapper<SpanData<Ctx>>, SpanData<Ctx>: Copy + fmt::Debug, { - let mut c = Converter::new(node, map, append, remove, call_site); + let mut c = Converter::new(node, map, append, remove, call_site, mode); convert_tokens(&mut c) } @@ -165,7 +176,8 @@ where if lexed.errors().next().is_some() { return None; } - let mut conv = RawConverter { lexed, anchor, pos: 0, ctx }; + let mut conv = + RawConverter { lexed, anchor, pos: 0, ctx, mode: DocCommentDesugarMode::ProcMacro }; Some(convert_tokens(&mut conv)) } @@ -178,7 +190,8 @@ where if lexed.errors().next().is_some() { return None; } - let mut conv = StaticRawConverter { lexed, pos: 0, span }; + let mut conv = + StaticRawConverter { lexed, pos: 0, span, mode: DocCommentDesugarMode::ProcMacro }; Some(convert_tokens(&mut conv)) } @@ -405,7 +418,7 @@ fn is_single_token_op(kind: SyntaxKind) -> bool { /// That is, strips leading `///` (or `/**`, etc) /// and strips the ending `*/` /// And then quote the string, which is needed to convert to `tt::Literal` -fn doc_comment_text(comment: &ast::Comment) -> SmolStr { +fn doc_comment_text(comment: &ast::Comment, mode: DocCommentDesugarMode) -> SmolStr { let prefix_len = comment.prefix().len(); let mut text = &comment.text()[prefix_len..]; @@ -414,26 +427,34 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr { text = &text[0..text.len() - 2]; } - let mut num_of_hashes = 0; - let mut count = 0; - for ch in text.chars() { - count = match ch { - '"' => 1, - '#' if count > 0 => count + 1, - _ => 0, - }; - num_of_hashes = num_of_hashes.max(count); - } + let text = match mode { + DocCommentDesugarMode::Mbe => { + let mut num_of_hashes = 0; + let mut count = 0; + for ch in text.chars() { + count = match ch { + '"' => 1, + '#' if count > 0 => count + 1, + _ => 0, + }; + num_of_hashes = num_of_hashes.max(count); + } - // Quote raw string with delimiters - // Note that `tt::Literal` expect an escaped string - let text = format!("r{delim}\"{text}\"{delim}", delim = "#".repeat(num_of_hashes)); + // Quote raw string with delimiters + // Note that `tt::Literal` expect an escaped string + format!(r#"r{delim}"{text}"{delim}"#, delim = "#".repeat(num_of_hashes)) + } + // Quote string with delimiters + // Note that `tt::Literal` expect an escaped string + DocCommentDesugarMode::ProcMacro => format!(r#""{}""#, text.escape_debug()), + }; text.into() } fn convert_doc_comment<S: Copy>( token: &syntax::SyntaxToken, span: S, + mode: DocCommentDesugarMode, ) -> Option<Vec<tt::TokenTree<S>>> { cov_mark::hit!(test_meta_doc_comments); let comment = ast::Comment::cast(token.clone())?; @@ -451,7 +472,7 @@ fn convert_doc_comment<S: Copy>( }; let mk_doc_literal = |comment: &ast::Comment| { - let lit = tt::Literal { text: doc_comment_text(comment), span }; + let lit = tt::Literal { text: doc_comment_text(comment, mode), span }; tt::TokenTree::from(tt::Leaf::from(lit)) }; @@ -479,12 +500,14 @@ struct RawConverter<'a, Ctx> { pos: usize, anchor: SpanAnchor, ctx: Ctx, + mode: DocCommentDesugarMode, } /// A raw token (straight from lexer) converter that gives every token the same span. struct StaticRawConverter<'a, S> { lexed: parser::LexedStr<'a>, pos: usize, span: S, + mode: DocCommentDesugarMode, } trait SrcToken<Ctx, S> { @@ -553,7 +576,7 @@ where span: SpanData<Ctx>, ) -> Option<Vec<tt::TokenTree<SpanData<Ctx>>>> { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text), span) + convert_doc_comment(&doc_comment(text), span, self.mode) } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -592,7 +615,7 @@ where fn convert_doc_comment(&self, &token: &usize, span: S) -> Option<Vec<tt::TokenTree<S>>> { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text), span) + convert_doc_comment(&doc_comment(text), span, self.mode) } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -634,6 +657,7 @@ struct Converter<SpanMap, S> { append: FxHashMap<SyntaxElement, Vec<tt::Leaf<S>>>, remove: FxHashSet<SyntaxElement>, call_site: S, + mode: DocCommentDesugarMode, } impl<SpanMap, S> Converter<SpanMap, S> { @@ -643,6 +667,7 @@ impl<SpanMap, S> Converter<SpanMap, S> { append: FxHashMap<SyntaxElement, Vec<tt::Leaf<S>>>, remove: FxHashSet<SyntaxElement>, call_site: S, + mode: DocCommentDesugarMode, ) -> Self { let mut this = Converter { current: None, @@ -654,6 +679,7 @@ impl<SpanMap, S> Converter<SpanMap, S> { remove, call_site, current_leaves: vec![], + mode, }; let first = this.next_token(); this.current = first; @@ -755,7 +781,7 @@ where { type Token = SynToken<S>; fn convert_doc_comment(&self, token: &Self::Token, span: S) -> Option<Vec<tt::TokenTree<S>>> { - convert_doc_comment(token.token(), span) + convert_doc_comment(token.token(), span, self.mode) } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { diff --git a/crates/mbe/src/syntax_bridge/tests.rs b/crates/mbe/src/syntax_bridge/tests.rs index bbfe378200..2988fb3cf1 100644 --- a/crates/mbe/src/syntax_bridge/tests.rs +++ b/crates/mbe/src/syntax_bridge/tests.rs @@ -7,11 +7,16 @@ use tt::{ Leaf, Punct, Spacing, }; -use crate::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; +use crate::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; fn check_punct_spacing(fixture: &str) { let source_file = ast::SourceFile::parse(fixture, span::Edition::CURRENT).ok().unwrap(); - let subtree = syntax_node_to_token_tree(source_file.syntax(), DummyTestSpanMap, DUMMY); + let subtree = syntax_node_to_token_tree( + source_file.syntax(), + DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::Mbe, + ); let mut annotations: FxHashMap<_, _> = extract_annotations(fixture) .into_iter() .map(|(range, annotation)| { diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs index c13a194379..82e4d66148 100644 --- a/crates/parser/src/grammar/attributes.rs +++ b/crates/parser/src/grammar/attributes.rs @@ -36,8 +36,33 @@ fn attr(p: &mut Parser<'_>, inner: bool) { attr.complete(p, ATTR); } +// test metas +// #![simple_ident] +// #![simple::path] +// #![simple_ident_expr = ""] +// #![simple::path::Expr = ""] +// #![simple_ident_tt(a b c)] +// #![simple_ident_tt[a b c]] +// #![simple_ident_tt{a b c}] +// #![simple::path::tt(a b c)] +// #![simple::path::tt[a b c]] +// #![simple::path::tt{a b c}] +// #![unsafe(simple_ident)] +// #![unsafe(simple::path)] +// #![unsafe(simple_ident_expr = "")] +// #![unsafe(simple::path::Expr = "")] +// #![unsafe(simple_ident_tt(a b c))] +// #![unsafe(simple_ident_tt[a b c])] +// #![unsafe(simple_ident_tt{a b c})] +// #![unsafe(simple::path::tt(a b c))] +// #![unsafe(simple::path::tt[a b c])] +// #![unsafe(simple::path::tt{a b c})] pub(super) fn meta(p: &mut Parser<'_>) { let meta = p.start(); + let is_unsafe = p.eat(T![unsafe]); + if is_unsafe { + p.expect(T!['(']); + } paths::use_path(p); match p.current() { @@ -50,6 +75,9 @@ pub(super) fn meta(p: &mut Parser<'_>) { T!['('] | T!['['] | T!['{'] => items::token_tree(p), _ => {} } + if is_unsafe { + p.expect(T![')']); + } meta.complete(p, META); } diff --git a/crates/parser/test_data/parser/inline/ok/0213_metas.rast b/crates/parser/test_data/parser/inline/ok/0213_metas.rast new file mode 100644 index 0000000000..b1ac60b530 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/0213_metas.rast @@ -0,0 +1,457 @@ +SOURCE_FILE + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_expr" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"\"" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "Expr" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"\"" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_PAREN "(" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_BRACK "[" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_BRACK "]" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_CURLY "{" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_CURLY "}" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_PAREN "(" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_BRACK "[" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_BRACK "]" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_CURLY "{" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_CURLY "}" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_expr" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"\"" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "Expr" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + STRING "\"\"" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_PAREN "(" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_PAREN ")" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_BRACK "[" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_BRACK "]" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple_ident_tt" + TOKEN_TREE + L_CURLY "{" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_CURLY "}" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_PAREN "(" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_PAREN ")" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_BRACK "[" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_BRACK "]" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" + ATTR + POUND "#" + BANG "!" + L_BRACK "[" + META + UNSAFE_KW "unsafe" + L_PAREN "(" + PATH + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "simple" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "path" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "tt" + TOKEN_TREE + L_CURLY "{" + IDENT "a" + WHITESPACE " " + IDENT "b" + WHITESPACE " " + IDENT "c" + R_CURLY "}" + R_PAREN ")" + R_BRACK "]" + WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0213_metas.rs b/crates/parser/test_data/parser/inline/ok/0213_metas.rs new file mode 100644 index 0000000000..57b7bb7170 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/0213_metas.rs @@ -0,0 +1,20 @@ +#![simple_ident] +#![simple::path] +#![simple_ident_expr = ""] +#![simple::path::Expr = ""] +#![simple_ident_tt(a b c)] +#![simple_ident_tt[a b c]] +#![simple_ident_tt{a b c}] +#![simple::path::tt(a b c)] +#![simple::path::tt[a b c]] +#![simple::path::tt{a b c}] +#![unsafe(simple_ident)] +#![unsafe(simple::path)] +#![unsafe(simple_ident_expr = "")] +#![unsafe(simple::path::Expr = "")] +#![unsafe(simple_ident_tt(a b c))] +#![unsafe(simple_ident_tt[a b c])] +#![unsafe(simple_ident_tt{a b c})] +#![unsafe(simple::path::tt(a b c))] +#![unsafe(simple::path::tt[a b c])] +#![unsafe(simple::path::tt{a b c})] diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index 0ab16c38c8..8749417290 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -13,7 +13,7 @@ mod version; use base_db::Env; use indexmap::IndexSet; -use paths::AbsPathBuf; +use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; use span::Span; use std::{ @@ -54,6 +54,7 @@ pub struct ProcMacroServer { /// /// Therefore, we just wrap the `ProcMacroProcessSrv` in a mutex here. process: Arc<Mutex<ProcMacroProcessSrv>>, + path: AbsPathBuf, } pub struct MacroDylib { @@ -113,15 +114,22 @@ pub struct MacroPanic { impl ProcMacroServer { /// Spawns an external process as the proc macro server and returns a client connected to it. pub fn spawn( - process_path: AbsPathBuf, + process_path: &AbsPath, env: &FxHashMap<String, String>, ) -> io::Result<ProcMacroServer> { let process = ProcMacroProcessSrv::run(process_path, env)?; - Ok(ProcMacroServer { process: Arc::new(Mutex::new(process)) }) + Ok(ProcMacroServer { + process: Arc::new(Mutex::new(process)), + path: process_path.to_owned(), + }) + } + + pub fn path(&self) -> &AbsPath { + &self.path } pub fn load_dylib(&self, dylib: MacroDylib) -> Result<Vec<ProcMacro>, ServerError> { - let _p = tracing::span!(tracing::Level::INFO, "ProcMacroClient::load_dylib").entered(); + let _p = tracing::span!(tracing::Level::INFO, "ProcMacroServer::load_dylib").entered(); let macros = self.process.lock().unwrap_or_else(|e| e.into_inner()).find_proc_macros(&dylib.path)?; diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index 35d48a1554..dce086d429 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -6,7 +6,7 @@ use std::{ sync::Arc, }; -use paths::{AbsPath, AbsPathBuf}; +use paths::AbsPath; use rustc_hash::FxHashMap; use stdx::JodChild; @@ -28,11 +28,11 @@ pub(crate) struct ProcMacroProcessSrv { impl ProcMacroProcessSrv { pub(crate) fn run( - process_path: AbsPathBuf, + process_path: &AbsPath, env: &FxHashMap<String, String>, ) -> io::Result<ProcMacroProcessSrv> { let create_srv = |null_stderr| { - let mut process = Process::run(process_path.clone(), env, null_stderr)?; + let mut process = Process::run(process_path, env, null_stderr)?; let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); io::Result::Ok(ProcMacroProcessSrv { @@ -153,11 +153,11 @@ struct Process { impl Process { fn run( - path: AbsPathBuf, + path: &AbsPath, env: &FxHashMap<String, String>, null_stderr: bool, ) -> io::Result<Process> { - let child = JodChild(mk_child(&path, env, null_stderr)?); + let child = JodChild(mk_child(path, env, null_stderr)?); Ok(Process { child }) } diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index a87b67f5c6..11a8e7af56 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -24,7 +24,7 @@ jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = tr perf-event = "=0.4.7" [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.9", features = ["processthreadsapi", "psapi"] } +windows-sys = { version = "0.52", features = ["Win32_System_Threading", "Win32_System_ProcessStatus"] } [features] cpu_profiler = [] diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs index a3fdb72a6d..2ccb1cd06a 100644 --- a/crates/profile/src/lib.rs +++ b/crates/profile/src/lib.rs @@ -26,7 +26,7 @@ thread_local!(static IN_SCOPE: RefCell<bool> = const { RefCell::new(false) }); /// A wrapper around google_cpu_profiler. /// /// Usage: -/// 1. Install gpref_tools (<https://github.com/gperftools/gperftools>), probably packaged with your Linux distro. +/// 1. Install gperf_tools (<https://github.com/gperftools/gperftools>), probably packaged with your Linux distro. /// 2. Build with `cpu_profiler` feature. /// 3. Run the code, the *raw* output would be in the `./out.profile` file. /// 4. Install pprof for visualization (<https://github.com/google/pprof>). diff --git a/crates/profile/src/memory_usage.rs b/crates/profile/src/memory_usage.rs index f089c78e0c..660afff3dc 100644 --- a/crates/profile/src/memory_usage.rs +++ b/crates/profile/src/memory_usage.rs @@ -37,8 +37,7 @@ impl MemoryUsage { // There doesn't seem to be an API for determining heap usage, so we try to // approximate that by using the Commit Charge value. - use winapi::um::processthreadsapi::*; - use winapi::um::psapi::*; + use windows_sys::Win32::System::{Threading::*, ProcessStatus::*}; use std::mem::{MaybeUninit, size_of}; let proc = unsafe { GetCurrentProcess() }; diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index fbd423c9ea..8e1f7fdcde 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -24,7 +24,7 @@ use toolchain::Tool; use crate::{ cfg::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation, - InvocationStrategy, Package, Sysroot, TargetKind, + InvocationStrategy, ManifestPath, Package, Sysroot, TargetKind, }; /// Output of the build script and proc-macro building steps for a workspace. @@ -63,9 +63,11 @@ impl WorkspaceBuildScripts { fn build_command( config: &CargoConfig, allowed_features: &FxHashSet<String>, - workspace_root: &AbsPathBuf, + manifest_path: &ManifestPath, + toolchain: Option<&Version>, sysroot: Option<&Sysroot>, ) -> io::Result<Command> { + const RUST_1_75: Version = Version::new(1, 75, 0); let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = Command::new(program); @@ -79,7 +81,7 @@ impl WorkspaceBuildScripts { cmd.args(&config.extra_args); cmd.arg("--manifest-path"); - cmd.arg(workspace_root.join("Cargo.toml")); + cmd.arg(manifest_path.as_ref()); if let Some(target_dir) = &config.target_dir { cmd.arg("--target-dir").arg(target_dir); @@ -116,6 +118,14 @@ impl WorkspaceBuildScripts { } } + if manifest_path.is_rust_manifest() { + cmd.arg("-Zscript"); + } + + if toolchain.map_or(false, |it| *it >= RUST_1_75) { + cmd.arg("--keep-going"); + } + cmd } }; @@ -138,11 +148,9 @@ impl WorkspaceBuildScripts { config: &CargoConfig, workspace: &CargoWorkspace, progress: &dyn Fn(String), - toolchain: &Option<Version>, + toolchain: Option<&Version>, sysroot: Option<&Sysroot>, ) -> io::Result<WorkspaceBuildScripts> { - const RUST_1_75: Version = Version::new(1, 75, 0); - let current_dir = match &config.invocation_location { InvocationLocation::Root(root) if config.run_build_script_command.is_some() => { root.as_path() @@ -152,37 +160,14 @@ impl WorkspaceBuildScripts { .as_ref(); let allowed_features = workspace.workspace_features(); - - match Self::run_per_ws( - Self::build_command( - config, - &allowed_features, - &workspace.workspace_root().to_path_buf(), - sysroot, - )?, - workspace, - current_dir, - progress, - ) { - Ok(WorkspaceBuildScripts { error: Some(error), .. }) - if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_75) => - { - // building build scripts failed, attempt to build with --keep-going so - // that we potentially get more build data - let mut cmd = Self::build_command( - config, - &allowed_features, - &workspace.workspace_root().to_path_buf(), - sysroot, - )?; - - cmd.args(["--keep-going"]); - let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?; - res.error = Some(error); - Ok(res) - } - res => res, - } + let cmd = Self::build_command( + config, + &allowed_features, + workspace.manifest_path(), + toolchain, + sysroot, + )?; + Self::run_per_ws(cmd, workspace, current_dir, progress) } /// Runs the build scripts by invoking the configured command *once*. @@ -204,7 +189,14 @@ impl WorkspaceBuildScripts { )) } }; - let cmd = Self::build_command(config, &Default::default(), workspace_root, None)?; + let cmd = Self::build_command( + config, + &Default::default(), + // This is not gonna be used anyways, so just construct a dummy here + &ManifestPath::try_from(workspace_root.clone()).unwrap(), + None, + None, + )?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are // exactly those from `config`. diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index ff7cf144aa..9955f2687c 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -32,6 +32,7 @@ pub struct CargoWorkspace { targets: Arena<TargetData>, workspace_root: AbsPathBuf, target_directory: AbsPathBuf, + manifest_path: ManifestPath, } impl ops::Index<Package> for CargoWorkspace { @@ -306,7 +307,7 @@ impl CargoWorkspace { ); } // The manifest is a rust file, so this means its a script manifest - if cargo_toml.extension().is_some_and(|ext| ext == "rs") { + if cargo_toml.is_rust_manifest() { // Deliberately don't set up RUSTC_BOOTSTRAP or a nightly override here, the user should // opt into it themselves. other_options.push("-Zscript".to_owned()); @@ -334,7 +335,7 @@ impl CargoWorkspace { .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command())) } - pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace { + pub fn new(mut meta: cargo_metadata::Metadata, manifest_path: ManifestPath) -> CargoWorkspace { let mut pkg_by_id = FxHashMap::default(); let mut packages = Arena::default(); let mut targets = Arena::default(); @@ -448,7 +449,7 @@ impl CargoWorkspace { let target_directory = AbsPathBuf::assert(meta.target_directory); - CargoWorkspace { packages, targets, workspace_root, target_directory } + CargoWorkspace { packages, targets, workspace_root, target_directory, manifest_path } } pub fn packages(&self) -> impl ExactSizeIterator<Item = Package> + '_ { @@ -466,6 +467,10 @@ impl CargoWorkspace { &self.workspace_root } + pub fn manifest_path(&self) -> &ManifestPath { + &self.manifest_path + } + pub fn target_directory(&self) -> &AbsPath { &self.target_directory } diff --git a/crates/project-model/src/env.rs b/crates/project-model/src/env.rs index 762e01c917..5520cdaff6 100644 --- a/crates/project-model/src/env.rs +++ b/crates/project-model/src/env.rs @@ -60,16 +60,19 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe } pub(crate) fn cargo_config_env( - cargo_toml: &ManifestPath, + manifest: &ManifestPath, extra_env: &FxHashMap<String, String>, sysroot: Option<&Sysroot>, ) -> FxHashMap<String, String> { let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo); cargo_config.envs(extra_env); cargo_config - .current_dir(cargo_toml.parent()) + .current_dir(manifest.parent()) .args(["-Z", "unstable-options", "config", "get", "env"]) .env("RUSTC_BOOTSTRAP", "1"); + if manifest.is_rust_manifest() { + cargo_config.arg("-Zscript"); + } // if successful we receive `env.key.value = "value" per entry tracing::debug!("Discovering cargo config env by {:?}", cargo_config); utf8_stdout(cargo_config).map(parse_output_cargo_config_env).unwrap_or_default() diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 7f3e35ca5d..181c07f46b 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -52,13 +52,15 @@ pub use crate::{ manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, sysroot::Sysroot, - workspace::{FileLoader, PackageRoot, ProjectWorkspace}, + workspace::{FileLoader, PackageRoot, ProjectWorkspace, ProjectWorkspaceKind}, }; +pub use cargo_metadata::Metadata; #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum ProjectManifest { ProjectJson(ManifestPath), CargoToml(ManifestPath), + CargoScript(ManifestPath), } impl ProjectManifest { @@ -71,7 +73,10 @@ impl ProjectManifest { if path.file_name().unwrap_or_default() == "Cargo.toml" { return Ok(ProjectManifest::CargoToml(path)); } - bail!("project root must point to Cargo.toml or rust-project.json: {path}"); + if path.extension().unwrap_or_default() == "rs" { + return Ok(ProjectManifest::CargoScript(path)); + } + bail!("project root must point to a Cargo.toml, rust-project.json or <script>.rs file: {path}"); } pub fn discover_single(path: &AbsPath) -> anyhow::Result<ProjectManifest> { @@ -146,15 +151,19 @@ impl ProjectManifest { res.sort(); res } + + pub fn manifest_path(&self) -> &ManifestPath { + match self { + ProjectManifest::ProjectJson(it) + | ProjectManifest::CargoToml(it) + | ProjectManifest::CargoScript(it) => it, + } + } } impl fmt::Display for ProjectManifest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - ProjectManifest::ProjectJson(it) | ProjectManifest::CargoToml(it) => { - fmt::Display::fmt(&it, f) - } - } + fmt::Display::fmt(self.manifest_path(), f) } } diff --git a/crates/project-model/src/manifest_path.rs b/crates/project-model/src/manifest_path.rs index d86e81e7e1..2331c0c36c 100644 --- a/crates/project-model/src/manifest_path.rs +++ b/crates/project-model/src/manifest_path.rs @@ -1,5 +1,5 @@ //! See [`ManifestPath`]. -use std::{fmt, ops, path::Path}; +use std::{borrow::Borrow, fmt, ops}; use paths::{AbsPath, AbsPathBuf}; @@ -38,6 +38,10 @@ impl ManifestPath { pub fn canonicalize(&self) -> ! { (**self).canonicalize() } + + pub fn is_rust_manifest(&self) -> bool { + self.file.extension().map_or(false, |ext| ext == "rs") + } } impl fmt::Display for ManifestPath { @@ -54,8 +58,14 @@ impl ops::Deref for ManifestPath { } } -impl AsRef<Path> for ManifestPath { - fn as_ref(&self) -> &Path { +impl AsRef<AbsPath> for ManifestPath { + fn as_ref(&self) -> &AbsPath { self.file.as_ref() } } + +impl Borrow<AbsPath> for ManifestPath { + fn borrow(&self) -> &AbsPath { + self.file.borrow() + } +} diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index fac6eb8ad3..5bee446f61 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -56,6 +56,7 @@ use serde::{de, Deserialize, Serialize}; use span::Edition; use crate::cfg::CfgFlag; +use crate::ManifestPath; /// Roots and crates that compose this Rust project. #[derive(Clone, Debug, Eq, PartialEq)] @@ -65,6 +66,7 @@ pub struct ProjectJson { /// e.g. `path/to/sysroot/lib/rustlib/src/rust` pub(crate) sysroot_src: Option<AbsPathBuf>, project_root: AbsPathBuf, + manifest: Option<ManifestPath>, crates: Vec<Crate>, } @@ -96,12 +98,17 @@ impl ProjectJson { /// * `base` - The path to the workspace root (i.e. the folder containing `rust-project.json`) /// * `data` - The parsed contents of `rust-project.json`, or project json that's passed via /// configuration. - pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { + pub fn new( + manifest: Option<ManifestPath>, + base: &AbsPath, + data: ProjectJsonData, + ) -> ProjectJson { let absolutize_on_base = |p| base.absolutize(p); ProjectJson { sysroot: data.sysroot.map(absolutize_on_base), sysroot_src: data.sysroot_src.map(absolutize_on_base), project_root: base.to_path_buf(), + manifest, crates: data .crates .into_iter() @@ -159,6 +166,11 @@ impl ProjectJson { pub fn path(&self) -> &AbsPath { &self.project_root } + + /// Returns the path to the project's manifest or root folder, if no manifest exists. + pub fn manifest_or_root(&self) -> &AbsPath { + self.manifest.as_ref().map_or(&self.project_root, |manifest| manifest.as_ref()) + } } #[derive(Serialize, Deserialize, Debug, Clone)] diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 1142d6243d..e6bbe6ede8 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -4,7 +4,7 @@ //! but we can't process `.rlib` and need source code instead. The source code //! is typically installed with `rustup component add rust-src` command. -use std::{env, fs, iter, ops, process::Command, sync::Arc}; +use std::{env, fs, ops, process::Command, sync::Arc}; use anyhow::{format_err, Result}; use base_db::CrateName; @@ -58,13 +58,11 @@ impl Stitched { pub(crate) fn public_deps(&self) -> impl Iterator<Item = (CrateName, SysrootCrate, bool)> + '_ { // core is added as a dependency before std in order to // mimic rustcs dependency order - ["core", "alloc", "std"] - .into_iter() - .zip(iter::repeat(true)) - .chain(iter::once(("test", false))) - .filter_map(move |(name, prelude)| { + [("core", true), ("alloc", false), ("std", true), ("test", false)].into_iter().filter_map( + move |(name, prelude)| { Some((CrateName::new(name).unwrap(), self.by_name(name)?, prelude)) - }) + }, + ) } pub(crate) fn proc_macro(&self) -> Option<SysrootCrate> { @@ -133,6 +131,24 @@ impl Sysroot { } } + pub fn check_has_core(&self) -> Result<(), String> { + let Some(Ok(src_root)) = &self.src_root else { return Ok(()) }; + let has_core = match &self.mode { + SysrootMode::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"), + SysrootMode::Stitched(stitched) => stitched.by_name("core").is_some(), + }; + if !has_core { + let var_note = if env::var_os("RUST_SRC_PATH").is_some() { + " (`RUST_SRC_PATH` might be incorrect, try unsetting it)" + } else { + " try running `rustup component add rust-src` to possible fix this" + }; + Err(format!("could not find libcore in loaded sysroot at `{}`{var_note}", src_root,)) + } else { + Ok(()) + } + } + pub fn num_packages(&self) -> usize { match &self.mode { SysrootMode::Workspace(ws) => ws.packages().count(), @@ -349,7 +365,7 @@ impl Sysroot { .filter(|&package| RELEVANT_SYSROOT_CRATES.contains(&&*package.name)) .map(|package| package.id.clone()) .collect(); - let cargo_workspace = CargoWorkspace::new(res); + let cargo_workspace = CargoWorkspace::new(res, sysroot_cargo_toml); Some(Sysroot { root: sysroot_dir.clone(), src_root: Some(Ok(sysroot_src_dir.clone())), @@ -368,7 +384,7 @@ impl Sysroot { .into_iter() .map(|it| sysroot_src_dir.join(it)) .filter_map(|it| ManifestPath::try_from(it).ok()) - .find(|it| fs::metadata(it).is_ok()); + .find(|it| fs::metadata(it.as_ref()).is_ok()); if let Some(root) = root { stitched.crates.alloc(SysrootCrateData { @@ -468,7 +484,7 @@ fn get_rustc_src(sysroot_path: &AbsPath) -> Option<ManifestPath> { let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml"); let rustc_src = ManifestPath::try_from(rustc_src).ok()?; tracing::debug!("checking for rustc source code: {rustc_src}"); - if fs::metadata(&rustc_src).is_ok() { + if fs::metadata(rustc_src.as_ref()).is_ok() { Some(rustc_src) } else { None diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index fd09dff503..3d5a934fc9 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -1,6 +1,7 @@ use std::ops::Deref; use base_db::{CrateGraph, FileId, ProcMacroPaths}; +use cargo_metadata::Metadata; use cfg::{CfgAtom, CfgDiff}; use expect_test::{expect_file, ExpectFile}; use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; @@ -9,8 +10,8 @@ use serde::de::DeserializeOwned; use triomphe::Arc; use crate::{ - CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, - WorkspaceBuildScripts, + workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides, ManifestPath, ProjectJson, + ProjectJsonData, ProjectWorkspace, Sysroot, WorkspaceBuildScripts, }; fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) { @@ -21,18 +22,22 @@ fn load_cargo_with_overrides( file: &str, cfg_overrides: CfgOverrides, ) -> (CrateGraph, ProcMacroPaths) { - let meta = get_test_json_file(file); - let cargo_workspace = CargoWorkspace::new(meta); - let project_workspace = ProjectWorkspace::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), + let meta: Metadata = get_test_json_file(file); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); + let cargo_workspace = CargoWorkspace::new(meta, manifest_path); + let project_workspace = ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + rustc: Err(None), + cargo_config_extra_env: Default::default(), + }, + cfg_overrides, sysroot: Err(None), - rustc: Err(None), rustc_cfg: Vec::new(), - cfg_overrides, toolchain: None, target_layout: Err("target_data_layout not loaded".into()), - cargo_config_extra_env: Default::default(), }; to_crate_graph(project_workspace) } @@ -41,18 +46,22 @@ fn load_cargo_with_fake_sysroot( file_map: &mut FxHashMap<AbsPathBuf, FileId>, file: &str, ) -> (CrateGraph, ProcMacroPaths) { - let meta = get_test_json_file(file); - let cargo_workspace = CargoWorkspace::new(meta); - let project_workspace = ProjectWorkspace::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), + let meta: Metadata = get_test_json_file(file); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); + let cargo_workspace = CargoWorkspace::new(meta, manifest_path); + let project_workspace = ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + rustc: Err(None), + cargo_config_extra_env: Default::default(), + }, sysroot: Ok(get_fake_sysroot()), - rustc: Err(None), rustc_cfg: Vec::new(), cfg_overrides: Default::default(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), - cargo_config_extra_env: Default::default(), }; project_workspace.to_crate_graph( &mut { @@ -69,8 +78,8 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { let data = get_test_json_file(file); let project = rooted_project_json(data); let sysroot = Ok(get_fake_sysroot()); - let project_workspace = ProjectWorkspace::Json { - project, + let project_workspace = ProjectWorkspace { + kind: ProjectWorkspaceKind::Json(project), sysroot, rustc_cfg: Vec::new(), toolchain: None, @@ -143,7 +152,7 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { replace_root(&mut root, true); let path = Utf8Path::new(&root); let base = AbsPath::assert(path); - ProjectJson::new(base, data) + ProjectJson::new(None, base, data) } fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacroPaths) { @@ -268,9 +277,10 @@ fn smoke_test_real_sysroot_cargo() { return; } let file_map = &mut FxHashMap::<AbsPathBuf, FileId>::default(); - let meta = get_test_json_file("hello-world-metadata.json"); - - let cargo_workspace = CargoWorkspace::new(meta); + let meta: Metadata = get_test_json_file("hello-world-metadata.json"); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); + let cargo_workspace = CargoWorkspace::new(meta, manifest_path); let sysroot = Ok(Sysroot::discover( AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))), &Default::default(), @@ -278,16 +288,18 @@ fn smoke_test_real_sysroot_cargo() { ) .unwrap()); - let project_workspace = ProjectWorkspace::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), + let project_workspace = ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + rustc: Err(None), + cargo_config_extra_env: Default::default(), + }, sysroot, - rustc: Err(None), rustc_cfg: Vec::new(), cfg_overrides: Default::default(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), - cargo_config_extra_env: Default::default(), }; project_workspace.to_crate_graph( &mut { diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 98c5a02dcd..85621444e3 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -4,7 +4,7 @@ use std::{collections::VecDeque, fmt, fs, iter, sync}; -use anyhow::{format_err, Context}; +use anyhow::Context; use base_db::{ CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileId, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, @@ -14,8 +14,8 @@ use paths::{AbsPath, AbsPathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; use span::Edition; -use stdx::always; use toolchain::Tool; +use tracing::instrument; use triomphe::Arc; use crate::{ @@ -45,50 +45,39 @@ pub struct PackageRoot { } #[derive(Clone)] -pub enum ProjectWorkspace { +pub struct ProjectWorkspace { + pub kind: ProjectWorkspaceKind, + /// The sysroot loaded for this workspace. + pub sysroot: Result<Sysroot, Option<String>>, + /// Holds cfg flags for the current target. We get those by running + /// `rustc --print cfg`. + // FIXME: make this a per-crate map, as, eg, build.rs might have a + // different target. + pub rustc_cfg: Vec<CfgFlag>, + /// The toolchain version used by this workspace. + pub toolchain: Option<Version>, + /// The target data layout queried for workspace. + pub target_layout: TargetLayoutLoadResult, + /// A set of cfg overrides for this workspace. + pub cfg_overrides: CfgOverrides, +} + +#[derive(Clone)] +pub enum ProjectWorkspaceKind { /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. Cargo { /// The workspace as returned by `cargo metadata`. cargo: CargoWorkspace, /// The build script results for the workspace. build_scripts: WorkspaceBuildScripts, - /// The sysroot loaded for this workspace. - sysroot: Result<Sysroot, Option<String>>, /// The rustc workspace loaded for this workspace. An `Err(None)` means loading has been /// disabled or was otherwise not requested. rustc: Result<Box<(CargoWorkspace, WorkspaceBuildScripts)>, Option<String>>, - /// Holds cfg flags for the current target. We get those by running - /// `rustc --print cfg`. - // FIXME: make this a per-crate map, as, eg, build.rs might have a - // different target. - rustc_cfg: Vec<CfgFlag>, - /// A set of cfg overrides for this workspace. - cfg_overrides: CfgOverrides, - /// The toolchain version used by this workspace. - toolchain: Option<Version>, - /// The target data layout queried for workspace. - target_layout: TargetLayoutLoadResult, /// Environment variables set in the `.cargo/config` file. cargo_config_extra_env: FxHashMap<String, String>, }, /// Project workspace was manually specified using a `rust-project.json` file. - Json { - /// The loaded project json file. - project: ProjectJson, - /// The sysroot loaded for this workspace. - sysroot: Result<Sysroot, Option<String>>, - /// Holds cfg flags for the current target. We get those by running - /// `rustc --print cfg`. - // FIXME: make this a per-crate map, as, eg, build.rs might have a - // different target. - rustc_cfg: Vec<CfgFlag>, - /// The toolchain version used by this workspace. - toolchain: Option<Version>, - /// The target data layout queried for workspace. - target_layout: TargetLayoutLoadResult, - /// A set of cfg overrides for this workspace. - cfg_overrides: CfgOverrides, - }, + Json(ProjectJson), // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning. // That's not the end user experience we should strive for. // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working. @@ -101,38 +90,23 @@ pub enum ProjectWorkspace { /// Backed by basic sysroot crates for basic completion and highlighting. DetachedFile { /// The file in question. - file: AbsPathBuf, - /// The sysroot loaded for this workspace. - sysroot: Result<Sysroot, Option<String>>, - /// Holds cfg flags for the current target. We get those by running - /// `rustc --print cfg`. - // FIXME: make this a per-crate map, as, eg, build.rs might have a - // different target. - rustc_cfg: Vec<CfgFlag>, - /// The toolchain version used by this workspace. - toolchain: Option<Version>, - /// The target data layout queried for workspace. - target_layout: TargetLayoutLoadResult, - /// A set of cfg overrides for the files. - cfg_overrides: CfgOverrides, + file: ManifestPath, /// Is this file a cargo script file? - cargo_script: Option<CargoWorkspace>, + cargo: Option<(CargoWorkspace, WorkspaceBuildScripts)>, + /// Environment variables set in the `.cargo/config` file. + cargo_config_extra_env: FxHashMap<String, String>, }, } impl fmt::Debug for ProjectWorkspace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Make sure this isn't too verbose. - match self { - ProjectWorkspace::Cargo { + let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides } = self; + match kind { + ProjectWorkspaceKind::Cargo { cargo, build_scripts: _, - sysroot, rustc, - rustc_cfg, - cfg_overrides, - toolchain, - target_layout, cargo_config_extra_env, } => f .debug_struct("Cargo") @@ -149,14 +123,7 @@ impl fmt::Debug for ProjectWorkspace { .field("data_layout", &target_layout) .field("cargo_config_extra_env", &cargo_config_extra_env) .finish(), - ProjectWorkspace::Json { - project, - sysroot, - rustc_cfg, - toolchain, - target_layout: data_layout, - cfg_overrides, - } => { + ProjectWorkspaceKind::Json(project) => { let mut debug_struct = f.debug_struct("Json"); debug_struct.field("n_crates", &project.n_crates()); if let Ok(sysroot) = sysroot { @@ -165,18 +132,14 @@ impl fmt::Debug for ProjectWorkspace { debug_struct .field("n_rustc_cfg", &rustc_cfg.len()) .field("toolchain", &toolchain) - .field("data_layout", &data_layout) + .field("data_layout", &target_layout) .field("n_cfg_overrides", &cfg_overrides.len()); debug_struct.finish() } - ProjectWorkspace::DetachedFile { + ProjectWorkspaceKind::DetachedFile { file, - sysroot, - rustc_cfg, - toolchain, - target_layout, - cfg_overrides, - cargo_script, + cargo: cargo_script, + cargo_config_extra_env, } => f .debug_struct("DetachedFiles") .field("file", &file) @@ -187,6 +150,7 @@ impl fmt::Debug for ProjectWorkspace { .field("toolchain", &toolchain) .field("data_layout", &target_layout) .field("n_cfg_overrides", &cfg_overrides.len()) + .field("cargo_config_extra_env", &cargo_config_extra_env) .finish(), } } @@ -230,12 +194,13 @@ impl ProjectWorkspace { ) -> anyhow::Result<ProjectWorkspace> { let res = match manifest { ProjectManifest::ProjectJson(project_json) => { - let file = fs::read_to_string(project_json) + let file = fs::read_to_string(project_json.as_ref()) .with_context(|| format!("Failed to read json file {project_json}"))?; let data = serde_json::from_str(&file) .with_context(|| format!("Failed to deserialize json file {project_json}"))?; let project_location = project_json.parent().to_path_buf(); - let project_json: ProjectJson = ProjectJson::new(&project_location, data); + let project_json: ProjectJson = + ProjectJson::new(Some(project_json.clone()), &project_location, data); ProjectWorkspace::load_inline( project_json, config.target.as_deref(), @@ -243,6 +208,9 @@ impl ProjectWorkspace { &config.cfg_overrides, ) } + ProjectManifest::CargoScript(rust_file) => { + ProjectWorkspace::load_detached_file(rust_file, config)? + } ProjectManifest::CargoToml(cargo_toml) => { let sysroot = match (&config.sysroot, &config.sysroot_src) { (Some(RustLibSource::Path(path)), None) => { @@ -299,7 +267,7 @@ impl ProjectWorkspace { progress, ) { Ok(meta) => { - let workspace = CargoWorkspace::new(meta); + let workspace = CargoWorkspace::new(meta, cargo_toml.clone()); let buildscripts = WorkspaceBuildScripts::rustc_crates( &workspace, cargo_toml.parent(), @@ -355,22 +323,24 @@ impl ProjectWorkspace { "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}", ) })?; - let cargo = CargoWorkspace::new(meta); + let cargo = CargoWorkspace::new(meta, cargo_toml.clone()); let cargo_config_extra_env = cargo_config_env(cargo_toml, &config.extra_env, sysroot_ref); - ProjectWorkspace::Cargo { - cargo, - build_scripts: WorkspaceBuildScripts::default(), + ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo, + build_scripts: WorkspaceBuildScripts::default(), + rustc, + cargo_config_extra_env, + }, sysroot, - rustc, rustc_cfg, cfg_overrides, toolchain, target_layout: data_layout .map(Arc::from) .map_err(|it| Arc::from(it.to_string())), - cargo_config_extra_env, } } }; @@ -423,8 +393,8 @@ impl ProjectWorkspace { let rustc_cfg = rustc_cfg::get(target, extra_env, cfg_config); let data_layout = target_data_layout::get(data_layout_config, target, extra_env); - ProjectWorkspace::Json { - project: project_json, + ProjectWorkspace { + kind: ProjectWorkspaceKind::Json(project_json), sysroot, rustc_cfg, toolchain, @@ -433,82 +403,76 @@ impl ProjectWorkspace { } } - pub fn load_detached_files( - detached_files: Vec<AbsPathBuf>, + pub fn load_detached_file( + detached_file: &ManifestPath, config: &CargoConfig, - ) -> Vec<anyhow::Result<ProjectWorkspace>> { - detached_files - .into_iter() - .map(|detached_file| { - let dir = detached_file - .parent() - .ok_or_else(|| format_err!("detached file has no parent"))?; - let sysroot = match &config.sysroot { - Some(RustLibSource::Path(path)) => { - Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata) - .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}"))) - } - Some(RustLibSource::Discover) => { - Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata) - .map_err(|e| { - Some(format!( - "Failed to find sysroot for {dir}. Is rust-src installed? {e}" - )) - }) - } - None => Err(None), - }; + ) -> anyhow::Result<ProjectWorkspace> { + let dir = detached_file.parent(); + let sysroot = match &config.sysroot { + Some(RustLibSource::Path(path)) => { + Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata) + .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}"))) + } + Some(RustLibSource::Discover) => Sysroot::discover( + dir, + &config.extra_env, + config.sysroot_query_metadata, + ) + .map_err(|e| { + Some(format!("Failed to find sysroot for {dir}. Is rust-src installed? {e}")) + }), + None => Err(None), + }; - let sysroot_ref = sysroot.as_ref().ok(); - let toolchain = match get_toolchain_version( - dir, - sysroot_ref, - Tool::Rustc, - &config.extra_env, - "rustc ", - ) { - Ok(it) => it, - Err(e) => { - tracing::error!("{e}"); - None - } - }; + let sysroot_ref = sysroot.as_ref().ok(); + let toolchain = + match get_toolchain_version(dir, sysroot_ref, Tool::Rustc, &config.extra_env, "rustc ") + { + Ok(it) => it, + Err(e) => { + tracing::error!("{e}"); + None + } + }; - let rustc_cfg = - rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref)); - let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Rustc(sysroot_ref), - None, - &config.extra_env, - ); + let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref)); + let data_layout = target_data_layout::get( + RustcDataLayoutConfig::Rustc(sysroot_ref), + None, + &config.extra_env, + ); - let cargo_script = ManifestPath::try_from(detached_file.clone()) - .ok() - .and_then(|file| { - CargoWorkspace::fetch_metadata( - &file, - file.parent(), - config, - sysroot_ref, - &|_| (), - ) - .ok() - }) - .map(CargoWorkspace::new); + let cargo_script = + CargoWorkspace::fetch_metadata(detached_file, dir, config, sysroot_ref, &|_| ()) + .ok() + .map(|ws| { + ( + CargoWorkspace::new(ws, detached_file.clone()), + WorkspaceBuildScripts::default(), + ) + }); - Ok(ProjectWorkspace::DetachedFile { - file: detached_file, - sysroot, - rustc_cfg, - toolchain, - target_layout: data_layout - .map(Arc::from) - .map_err(|it| Arc::from(it.to_string())), - cfg_overrides: config.cfg_overrides.clone(), - cargo_script, - }) - }) - .collect() + let cargo_config_extra_env = + cargo_config_env(detached_file, &config.extra_env, sysroot_ref); + Ok(ProjectWorkspace { + kind: ProjectWorkspaceKind::DetachedFile { + file: detached_file.to_owned(), + cargo: cargo_script, + cargo_config_extra_env, + }, + sysroot, + rustc_cfg, + toolchain, + target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + cfg_overrides: config.cfg_overrides.clone(), + }) + } + + pub fn load_detached_files( + detached_files: Vec<ManifestPath>, + config: &CargoConfig, + ) -> Vec<anyhow::Result<ProjectWorkspace>> { + detached_files.into_iter().map(|file| Self::load_detached_file(&file, config)).collect() } /// Runs the build scripts for this [`ProjectWorkspace`]. @@ -517,27 +481,22 @@ impl ProjectWorkspace { config: &CargoConfig, progress: &dyn Fn(String), ) -> anyhow::Result<WorkspaceBuildScripts> { - match self { - ProjectWorkspace::DetachedFile { - cargo_script: Some(cargo), - toolchain, - sysroot, - .. - } - | ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => { + match &self.kind { + ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } + | ProjectWorkspaceKind::Cargo { cargo, .. } => { WorkspaceBuildScripts::run_for_workspace( config, cargo, progress, - toolchain, - sysroot.as_ref().ok(), + self.toolchain.as_ref(), + self.sysroot.as_ref().ok(), ) .with_context(|| { format!("Failed to run build scripts for {}", cargo.workspace_root()) }) } - ProjectWorkspace::DetachedFile { cargo_script: None, .. } - | ProjectWorkspace::Json { .. } => Ok(WorkspaceBuildScripts::default()), + ProjectWorkspaceKind::DetachedFile { cargo: None, .. } + | ProjectWorkspaceKind::Json { .. } => Ok(WorkspaceBuildScripts::default()), } } @@ -557,8 +516,8 @@ impl ProjectWorkspace { let cargo_ws: Vec<_> = workspaces .iter() - .filter_map(|it| match it { - ProjectWorkspace::Cargo { cargo, .. } => Some(cargo), + .filter_map(|it| match &it.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } => Some(cargo), _ => None, }) .collect(); @@ -572,8 +531,8 @@ impl ProjectWorkspace { workspaces .iter() - .map(|it| match it { - ProjectWorkspace::Cargo { cargo, .. } => match outputs { + .map(|it| match &it.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } => match outputs { Ok(outputs) => Ok(outputs.next().unwrap()), Err(e) => Err(e.clone()).with_context(|| { format!("Failed to run build scripts for {}", cargo.workspace_root()) @@ -585,39 +544,33 @@ impl ProjectWorkspace { } pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) { - match self { - ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs, - _ => { - always!(bs == WorkspaceBuildScripts::default()); + match &mut self.kind { + ProjectWorkspaceKind::Cargo { build_scripts, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((_, build_scripts)), .. } => { + *build_scripts = bs } + _ => assert_eq!(bs, WorkspaceBuildScripts::default()), } } - pub fn workspace_definition_path(&self) -> &AbsPath { - match self { - ProjectWorkspace::Cargo { cargo, .. } => cargo.workspace_root(), - ProjectWorkspace::Json { project, .. } => project.path(), - ProjectWorkspace::DetachedFile { file, .. } => file, + pub fn manifest_or_root(&self) -> &AbsPath { + match &self.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } => cargo.manifest_path(), + ProjectWorkspaceKind::Json(project) => project.manifest_or_root(), + ProjectWorkspaceKind::DetachedFile { file, .. } => file, } } pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result<AbsPathBuf> { - match self { - ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. } - | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } - | ProjectWorkspace::DetachedFile { sysroot: Ok(sysroot), .. } => { - sysroot.discover_proc_macro_srv() - } - ProjectWorkspace::DetachedFile { .. } => { - Err(anyhow::format_err!("cannot find proc-macro server, no sysroot was found")) - } - ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::format_err!( - "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot", - cargo.workspace_root() + match &self.sysroot { + Ok(sysroot) => sysroot.discover_proc_macro_srv(), + Err(None) => Err(anyhow::format_err!( + "cannot find proc-macro server, the workspace `{}` is missing a sysroot", + self.manifest_or_root() )), - ProjectWorkspace::Json { project, .. } => Err(anyhow::format_err!( - "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot", - project.path() + Err(Some(e)) => Err(anyhow::format_err!( + "cannot find proc-macro server, the workspace `{}` is missing a sysroot: {e}", + self.manifest_or_root() )), } } @@ -626,8 +579,8 @@ impl ProjectWorkspace { /// The return type contains the path and whether or not /// the root is a member of the current workspace pub fn to_roots(&self) -> Vec<PackageRoot> { - let mk_sysroot = |sysroot: Result<_, _>| { - sysroot.into_iter().flat_map(move |sysroot: &Sysroot| { + let mk_sysroot = || { + self.sysroot.as_ref().into_iter().flat_map(move |sysroot: &Sysroot| { let mut r = match sysroot.mode() { SysrootMode::Workspace(ws) => ws .packages() @@ -661,15 +614,8 @@ impl ProjectWorkspace { r }) }; - match self { - ProjectWorkspace::Json { - project, - sysroot, - rustc_cfg: _, - toolchain: _, - target_layout: _, - cfg_overrides: _, - } => project + match &self.kind { + ProjectWorkspaceKind::Json(project) => project .crates() .map(|(_, krate)| PackageRoot { is_local: krate.is_workspace_member, @@ -678,17 +624,12 @@ impl ProjectWorkspace { }) .collect::<FxHashSet<_>>() .into_iter() - .chain(mk_sysroot(sysroot.as_ref())) + .chain(mk_sysroot()) .collect::<Vec<_>>(), - ProjectWorkspace::Cargo { + ProjectWorkspaceKind::Cargo { cargo, - sysroot, rustc, - rustc_cfg: _, - cfg_overrides: _, build_scripts, - toolchain: _, - target_layout: _, cargo_config_extra_env: _, } => { cargo @@ -729,7 +670,7 @@ impl ProjectWorkspace { } PackageRoot { is_local, include, exclude } }) - .chain(mk_sysroot(sysroot.as_ref())) + .chain(mk_sysroot()) .chain(rustc.iter().map(|a| a.as_ref()).flat_map(|(rustc, _)| { rustc.packages().map(move |krate| PackageRoot { is_local: false, @@ -739,18 +680,21 @@ impl ProjectWorkspace { })) .collect() } - ProjectWorkspace::DetachedFile { file, cargo_script, sysroot, .. } => { + ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => { iter::once(PackageRoot { is_local: true, - include: vec![file.clone()], + include: vec![file.as_ref().to_owned()], exclude: Vec::new(), }) - .chain(cargo_script.iter().flat_map(|cargo| { + .chain(cargo_script.iter().flat_map(|(cargo, build_scripts)| { cargo.packages().map(|pkg| { let is_local = cargo[pkg].is_local; let pkg_root = cargo[pkg].manifest.parent().to_path_buf(); let mut include = vec![pkg_root.clone()]; + let out_dir = + build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone()); + include.extend(out_dir); // In case target's path is manually set in Cargo.toml to be // outside the package root, add its parent as an extra include. @@ -780,28 +724,28 @@ impl ProjectWorkspace { PackageRoot { is_local, include, exclude } }) })) - .chain(mk_sysroot(sysroot.as_ref())) + .chain(mk_sysroot()) .collect() } } } pub fn n_packages(&self) -> usize { - match self { - ProjectWorkspace::Json { project, sysroot, .. } => { - let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); + match &self.kind { + ProjectWorkspaceKind::Json(project) => { + let sysroot_package_len = self.sysroot.as_ref().map_or(0, |it| it.num_packages()); sysroot_package_len + project.n_crates() } - ProjectWorkspace::Cargo { cargo, sysroot, rustc, .. } => { + ProjectWorkspaceKind::Cargo { cargo, rustc, .. } => { let rustc_package_len = rustc.as_ref().map(|a| a.as_ref()).map_or(0, |(it, _)| it.packages().len()); - let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); + let sysroot_package_len = self.sysroot.as_ref().map_or(0, |it| it.num_packages()); cargo.packages().len() + sysroot_package_len + rustc_package_len } - ProjectWorkspace::DetachedFile { sysroot, cargo_script, .. } => { - let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); + ProjectWorkspaceKind::DetachedFile { cargo: cargo_script, .. } => { + let sysroot_package_len = self.sysroot.as_ref().map_or(0, |it| it.num_packages()); sysroot_package_len - + cargo_script.as_ref().map_or(1, |cargo| cargo.packages().len()) + + cargo_script.as_ref().map_or(1, |(cargo, _)| cargo.packages().len()) } } } @@ -813,15 +757,9 @@ impl ProjectWorkspace { ) -> (CrateGraph, ProcMacroPaths) { let _p = tracing::span!(tracing::Level::INFO, "ProjectWorkspace::to_crate_graph").entered(); - let ((mut crate_graph, proc_macros), sysroot) = match self { - ProjectWorkspace::Json { - project, - sysroot, - rustc_cfg, - toolchain: _, - target_layout: _, - cfg_overrides, - } => ( + let Self { kind, sysroot, cfg_overrides, rustc_cfg, .. } = self; + let ((mut crate_graph, proc_macros), sysroot) = match kind { + ProjectWorkspaceKind::Json(project) => ( project_json_to_crate_graph( rustc_cfg.clone(), load, @@ -832,15 +770,10 @@ impl ProjectWorkspace { ), sysroot, ), - ProjectWorkspace::Cargo { + ProjectWorkspaceKind::Cargo { cargo, - sysroot, rustc, - rustc_cfg, - cfg_overrides, build_scripts, - toolchain: _, - target_layout: _, cargo_config_extra_env: _, } => ( cargo_to_crate_graph( @@ -854,16 +787,8 @@ impl ProjectWorkspace { ), sysroot, ), - ProjectWorkspace::DetachedFile { - file, - sysroot, - rustc_cfg, - toolchain: _, - target_layout: _, - cfg_overrides, - cargo_script, - } => ( - if let Some(cargo) = cargo_script { + ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => ( + if let Some((cargo, build_scripts)) = cargo_script { cargo_to_crate_graph( &mut |path| load(path), None, @@ -871,7 +796,7 @@ impl ProjectWorkspace { sysroot.as_ref().ok(), rustc_cfg.clone(), cfg_overrides, - &WorkspaceBuildScripts::default(), + build_scripts, ) } else { detached_file_to_crate_graph( @@ -897,93 +822,60 @@ impl ProjectWorkspace { } pub fn eq_ignore_build_data(&self, other: &Self) -> bool { - match (self, other) { + let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides, .. } = self; + let Self { + kind: o_kind, + sysroot: o_sysroot, + rustc_cfg: o_rustc_cfg, + toolchain: o_toolchain, + target_layout: o_target_layout, + cfg_overrides: o_cfg_overrides, + .. + } = other; + (match (kind, o_kind) { ( - Self::Cargo { + ProjectWorkspaceKind::Cargo { cargo, - sysroot, rustc, - rustc_cfg, - cfg_overrides, - toolchain, cargo_config_extra_env, build_scripts: _, - target_layout: _, }, - Self::Cargo { + ProjectWorkspaceKind::Cargo { cargo: o_cargo, - sysroot: o_sysroot, rustc: o_rustc, - rustc_cfg: o_rustc_cfg, - cfg_overrides: o_cfg_overrides, - toolchain: o_toolchain, cargo_config_extra_env: o_cargo_config_extra_env, build_scripts: _, - target_layout: _, }, ) => { cargo == o_cargo && rustc == o_rustc - && rustc_cfg == o_rustc_cfg - && cfg_overrides == o_cfg_overrides - && toolchain == o_toolchain - && sysroot == o_sysroot && cargo_config_extra_env == o_cargo_config_extra_env } - ( - Self::Json { - project, - sysroot, - rustc_cfg, - toolchain, - target_layout: _, - cfg_overrides, - }, - Self::Json { - project: o_project, - sysroot: o_sysroot, - rustc_cfg: o_rustc_cfg, - toolchain: o_toolchain, - target_layout: _, - cfg_overrides: o_cfg_overrides, - }, - ) => { + (ProjectWorkspaceKind::Json(project), ProjectWorkspaceKind::Json(o_project)) => { project == o_project - && rustc_cfg == o_rustc_cfg - && sysroot == o_sysroot - && toolchain == o_toolchain - && cfg_overrides == o_cfg_overrides } ( - Self::DetachedFile { + ProjectWorkspaceKind::DetachedFile { file, - sysroot, - rustc_cfg, - cargo_script, - toolchain, - target_layout, - cfg_overrides, + cargo: Some((cargo_script, _)), + cargo_config_extra_env, }, - Self::DetachedFile { + ProjectWorkspaceKind::DetachedFile { file: o_file, - sysroot: o_sysroot, - rustc_cfg: o_rustc_cfg, - cargo_script: o_cargo_script, - toolchain: o_toolchain, - target_layout: o_target_layout, - cfg_overrides: o_cfg_overrides, + cargo: Some((o_cargo_script, _)), + cargo_config_extra_env: o_cargo_config_extra_env, }, ) => { file == o_file - && sysroot == o_sysroot - && rustc_cfg == o_rustc_cfg - && toolchain == o_toolchain - && target_layout == o_target_layout - && cfg_overrides == o_cfg_overrides && cargo_script == o_cargo_script + && cargo_config_extra_env == o_cargo_config_extra_env } - _ => false, - } + _ => return false, + }) && sysroot == o_sysroot + && rustc_cfg == o_rustc_cfg + && toolchain == o_toolchain + && target_layout == o_target_layout + && cfg_overrides == o_cfg_overrides } /// Returns `true` if the project workspace is [`Json`]. @@ -991,10 +883,11 @@ impl ProjectWorkspace { /// [`Json`]: ProjectWorkspace::Json #[must_use] pub fn is_json(&self) -> bool { - matches!(self, Self::Json { .. }) + matches!(self.kind, ProjectWorkspaceKind::Json { .. }) } } +#[instrument(skip_all)] fn project_json_to_crate_graph( rustc_cfg: Vec<CfgFlag>, load: FileLoader<'_>, @@ -1294,11 +1187,11 @@ fn cargo_to_crate_graph( fn detached_file_to_crate_graph( rustc_cfg: Vec<CfgFlag>, load: FileLoader<'_>, - detached_file: &AbsPathBuf, + detached_file: &ManifestPath, sysroot: Option<&Sysroot>, override_cfg: &CfgOverrides, ) -> (CrateGraph, ProcMacroPaths) { - let _p = tracing::span!(tracing::Level::INFO, "detached_files_to_crate_graph").entered(); + let _p = tracing::span!(tracing::Level::INFO, "detached_file_to_crate_graph").entered(); let mut crate_graph = CrateGraph::default(); let (public_deps, _libproc_macro) = match sysroot { Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load), @@ -1520,7 +1413,7 @@ impl SysrootPublicDeps { /// Makes `from` depend on the public sysroot crates. fn add_to_crate_graph(&self, crate_graph: &mut CrateGraph, from: CrateId) { for (name, krate, prelude) in &self.deps { - add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude); + add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude, true); } } } @@ -1573,7 +1466,7 @@ fn sysroot_to_crate_graph( | LangCrateOrigin::Std => pub_deps.push(( CrateName::normalize_dashes(&lang_crate.to_string()), cid, - !matches!(lang_crate, LangCrateOrigin::Test), + !matches!(lang_crate, LangCrateOrigin::Test | LangCrateOrigin::Alloc), )), LangCrateOrigin::ProcMacro => libproc_macro = Some(cid), LangCrateOrigin::Other => (), @@ -1674,12 +1567,20 @@ fn add_dep_with_prelude( name: CrateName, to: CrateId, prelude: bool, + sysroot: bool, ) { - add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude)) + add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude, sysroot)) } fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId, prelude: bool) { - add_dep_with_prelude(crate_graph, from, CrateName::new("proc_macro").unwrap(), to, prelude); + add_dep_with_prelude( + crate_graph, + from, + CrateName::new("proc_macro").unwrap(), + to, + prelude, + true, + ); } fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) { diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index c2a2d6ed91..3401d7f7e4 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -50,6 +50,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { @@ -111,6 +112,7 @@ "hello_world", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(4), @@ -118,6 +120,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { @@ -179,6 +182,7 @@ "hello_world", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(4), @@ -186,6 +190,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { @@ -247,6 +252,7 @@ "hello_world", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(4), @@ -254,6 +260,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index c2a2d6ed91..3401d7f7e4 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -50,6 +50,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { @@ -111,6 +112,7 @@ "hello_world", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(4), @@ -118,6 +120,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { @@ -179,6 +182,7 @@ "hello_world", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(4), @@ -186,6 +190,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { @@ -247,6 +252,7 @@ "hello_world", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(4), @@ -254,6 +260,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index c291ffcca7..491568d4b7 100644 --- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -49,6 +49,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { @@ -109,6 +110,7 @@ "hello_world", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(4), @@ -116,6 +118,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { @@ -176,6 +179,7 @@ "hello_world", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(4), @@ -183,6 +187,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { @@ -243,6 +248,7 @@ "hello_world", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(4), @@ -250,6 +256,7 @@ "libc", ), prelude: true, + sysroot: false, }, ], origin: Local { diff --git a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt index 80c9136589..c123df80a6 100644 --- a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt +++ b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt @@ -30,6 +30,7 @@ "core", ), prelude: true, + sysroot: false, }, ], origin: Lang( @@ -158,6 +159,7 @@ "std", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(1), @@ -165,6 +167,7 @@ "core", ), prelude: true, + sysroot: false, }, ], origin: Lang( @@ -233,6 +236,7 @@ "alloc", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(3), @@ -240,6 +244,7 @@ "panic_unwind", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(2), @@ -247,6 +252,7 @@ "panic_abort", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(1), @@ -254,6 +260,7 @@ "core", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(5), @@ -261,6 +268,7 @@ "profiler_builtins", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(9), @@ -268,6 +276,7 @@ "unwind", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(7), @@ -275,6 +284,7 @@ "std_detect", ), prelude: true, + sysroot: false, }, Dependency { crate_id: Idx::<CrateData>(8), @@ -282,6 +292,7 @@ "test", ), prelude: true, + sysroot: false, }, ], origin: Lang( @@ -409,13 +420,15 @@ "core", ), prelude: true, + sysroot: true, }, Dependency { crate_id: Idx::<CrateData>(0), name: CrateName( "alloc", ), - prelude: true, + prelude: false, + sysroot: true, }, Dependency { crate_id: Idx::<CrateData>(6), @@ -423,6 +436,7 @@ "std", ), prelude: true, + sysroot: true, }, Dependency { crate_id: Idx::<CrateData>(8), @@ -430,6 +444,7 @@ "test", ), prelude: false, + sysroot: true, }, Dependency { crate_id: Idx::<CrateData>(4), @@ -437,6 +452,7 @@ "proc_macro", ), prelude: false, + sysroot: true, }, ], origin: Local { diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index cd3349899e..34b3e49314 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "rust-analyzer" version = "0.0.0" -homepage = "https://github.com/rust-analyzer/rust-analyzer" +homepage = "https://rust-analyzer.github.io/" +repository = "https://github.com/rust-analyzer/rust-analyzer" description = "A language server for the Rust programming language" documentation = "https://rust-analyzer.github.io/manual.html" autobins = false @@ -69,7 +70,7 @@ vfs.workspace = true paths.workspace = true [target.'cfg(windows)'.dependencies] -winapi = "0.3.9" +windows-sys = { version = "0.52", features = ["Win32_System_Threading"] } [target.'cfg(not(target_env = "msvc"))'.dependencies] jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = true } diff --git a/crates/rust-analyzer/build.rs b/crates/rust-analyzer/build.rs index 15935e2da8..72b741de00 100644 --- a/crates/rust-analyzer/build.rs +++ b/crates/rust-analyzer/build.rs @@ -5,6 +5,7 @@ use std::{env, path::PathBuf, process::Command}; fn main() { set_rerun(); set_commit_info(); + println!("cargo::rustc-check-cfg=cfg(rust_analyzer)"); if option_env!("CFG_RELEASE").is_none() { println!("cargo:rustc-env=POKE_RA_DEVS=1"); } diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 78920f3aba..9daae914d7 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -259,9 +259,15 @@ fn run_server() -> anyhow::Result<()> { config.rediscover_workspaces(); } - rust_analyzer::main_loop(config, connection)?; + // If the io_threads have an error, there's usually an error on the main + // loop too because the channels are closed. Ensure we report both errors. + match (rust_analyzer::main_loop(config, connection), io_threads.join()) { + (Err(loop_e), Err(join_e)) => anyhow::bail!("{loop_e}\n{join_e}"), + (Ok(_), Err(join_e)) => anyhow::bail!("{join_e}"), + (Err(loop_e), Ok(_)) => anyhow::bail!("{loop_e}"), + (Ok(_), Ok(_)) => {} + } - io_threads.join()?; tracing::info!("server did shut down"); Ok(()) } diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 6a5f7b0262..693a35b91e 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -211,7 +211,7 @@ mod tests { use super::*; use ide::Edition; - use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; + use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; use syntax::{ ast::{self, AstNode}, SmolStr, @@ -221,7 +221,12 @@ mod tests { let cfg_expr = { let source_file = ast::SourceFile::parse(cfg, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let tt = syntax_node_to_token_tree(tt.syntax(), &DummyTestSpanMap, DUMMY); + let tt = syntax_node_to_token_tree( + tt.syntax(), + &DummyTestSpanMap, + DUMMY, + DocCommentDesugarMode::Mbe, + ); CfgExpr::parse(&tt) }; diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index a1eea8839e..5208aa9bf0 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -986,6 +986,7 @@ impl flags::AnalysisStats { prefer_no_std: false, prefer_prelude: true, style_lints: false, + term_search_fuel: 400, }, ide::AssistResolveStrategy::All, file_id, diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 79d6226deb..d5eac49ad3 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -13,6 +13,17 @@ use crate::cli::flags; impl flags::Diagnostics { pub fn run(self) -> anyhow::Result<()> { + const STACK_SIZE: usize = 1024 * 1024 * 8; + + let handle = stdx::thread::Builder::new(stdx::thread::ThreadIntent::LatencySensitive) + .name("BIG_STACK_THREAD".into()) + .stack_size(STACK_SIZE) + .spawn(|| self.run_()) + .unwrap(); + + handle.join() + } + fn run_(self) -> anyhow::Result<()> { let cargo_config = CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv { diff --git a/crates/rust-analyzer/src/cli/parse.rs b/crates/rust-analyzer/src/cli/parse.rs index ead4d706e6..ed048aa635 100644 --- a/crates/rust-analyzer/src/cli/parse.rs +++ b/crates/rust-analyzer/src/cli/parse.rs @@ -6,7 +6,7 @@ use crate::cli::{flags, read_stdin}; impl flags::Parse { pub fn run(self) -> anyhow::Result<()> { - let _p = tracing::span!(tracing::Level::INFO, "parsing").entered(); + let _p = tracing::span!(tracing::Level::INFO, "flags::Parse::run").entered(); let text = read_stdin()?; let file = SourceFile::parse(&text, Edition::CURRENT).tree(); if !self.no_dump { diff --git a/crates/rust-analyzer/src/cli/progress_report.rs b/crates/rust-analyzer/src/cli/progress_report.rs index 6964977840..8b143daf2a 100644 --- a/crates/rust-analyzer/src/cli/progress_report.rs +++ b/crates/rust-analyzer/src/cli/progress_report.rs @@ -96,7 +96,7 @@ impl<'a> ProgressReport<'a> { } fn set_value(&mut self, value: f32) { - self.curr = f32::max(0.0, f32::min(1.0, value)); + self.curr = value.clamp(0.0, 1.0); } fn clear(&mut self) { diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index 2f9394d0ee..85f964b1dd 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -10,7 +10,10 @@ use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig}; use itertools::Either; use profile::StopWatch; use project_model::target_data_layout::RustcDataLayoutConfig; -use project_model::{target_data_layout, CargoConfig, ProjectWorkspace, RustLibSource, Sysroot}; +use project_model::{ + target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, + RustLibSource, Sysroot, +}; use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice}; use rustc_hash::FxHashMap; @@ -75,14 +78,17 @@ impl Tester { &cargo_config.extra_env, ); - let workspace = ProjectWorkspace::DetachedFile { - file: tmp_file, + let workspace = ProjectWorkspace { + kind: ProjectWorkspaceKind::DetachedFile { + file: ManifestPath::try_from(tmp_file).unwrap(), + cargo: None, + cargo_config_extra_env: Default::default(), + }, sysroot, rustc_cfg: vec![], toolchain: None, target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), cfg_overrides: Default::default(), - cargo_script: None, }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: false, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index e956791d9d..6c332ae1cb 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -230,7 +230,7 @@ config_data! { /// If `$saved_file` is part of the command, rust-analyzer will pass /// the absolute path of the saved file to the provided command. This is /// intended to be used with non-Cargo build systems. - /// Note that `$saved_file` is experimental and may be removed in the futureg. + /// Note that `$saved_file` is experimental and may be removed in the future. /// /// An example command would be: /// @@ -315,8 +315,10 @@ config_data! { /// How to render the size information in a memory layout hover. hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Both), - /// How many fields of a struct to display when hovering a struct. - hover_show_structFields: Option<usize> = None, + /// 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. + 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, @@ -356,7 +358,8 @@ config_data! { /// of projects. /// /// Elements must be paths pointing to `Cargo.toml`, - /// `rust-project.json`, or JSON objects in `rust-project.json` format. + /// `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. @@ -451,6 +454,9 @@ config_data! { /// Local configurations can be overridden for every crate by placing a `rust-analyzer.toml` on crate root. /// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle. local: struct LocalDefaultConfigData <- LocalConfigInput -> { + /// Term search fuel in "units of work" for assists (Defaults to 400). + assist_termSearch_fuel: usize = 400, + /// 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, @@ -512,6 +518,8 @@ config_data! { }"#).unwrap(), /// Whether to 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 200). + completion_termSearch_fuel: usize = 200, /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. highlightRelated_breakPoints_enable: bool = true, @@ -1012,6 +1020,7 @@ impl Config { prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), assist_emit_must_use: self.assist_emitMustUse().to_owned(), prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), + term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64, } } @@ -1045,6 +1054,7 @@ impl Config { snippets: self.snippets.clone().to_vec(), limit: self.completion_limit(source_root).to_owned(), enable_term_search: self.completion_termSearch_enable(source_root).to_owned(), + term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64, prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), } } @@ -1064,6 +1074,7 @@ impl Config { prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), style_lints: self.diagnostics_styleLints_enable().to_owned(), + term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64, } } pub fn expand_proc_attr_macros(&self) -> bool { @@ -1125,7 +1136,8 @@ impl Config { }, keywords: self.hover_documentation_keywords_enable().to_owned(), max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(), - max_struct_field_count: self.hover_show_structFields().to_owned(), + max_fields_count: self.hover_show_fields().to_owned(), + max_enum_variants_count: self.hover_show_enumVariants().to_owned(), } } @@ -1301,12 +1313,9 @@ impl Config { self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect(); self.discovered_projects .iter() - .filter( - |(ProjectManifest::ProjectJson(path) - | ProjectManifest::CargoToml(path))| { - !exclude_dirs.iter().any(|p| path.starts_with(p)) - }, - ) + .filter(|project| { + !exclude_dirs.iter().any(|p| project.manifest_path().starts_with(p)) + }) .cloned() .map(LinkedProject::from) .collect() @@ -1322,7 +1331,7 @@ impl Config { .map(Into::into) } ManifestOrProjectJson::ProjectJson(it) => { - Some(ProjectJson::new(&self.root_path, it.clone()).into()) + Some(ProjectJson::new(None, &self.root_path, it.clone()).into()) } }) .collect(), @@ -1775,7 +1784,7 @@ impl Config { pub fn lens(&self) -> LensConfig { LensConfig { - run: *self.lens_run_enable(), + run: *self.lens_enable() && *self.lens_run_enable(), debug: *self.lens_enable() && *self.lens_debug_enable(), interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(), implementations: *self.lens_enable() && *self.lens_implementations_enable(), diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 7adaef4ff6..cf3b8d331d 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs @@ -95,45 +95,8 @@ impl RequestDispatcher<'_> { self } - /// Dispatches a non-latency-sensitive request onto the thread pool - /// without retrying it if it panics. - pub(crate) fn on_no_retry<R>( - &mut self, - f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>, - ) -> &mut Self - where - R: lsp_types::request::Request + 'static, - R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, - R::Result: Serialize, - { - let (req, params, panic_context) = match self.parse::<R>() { - Some(it) => it, - None => return self, - }; - - self.global_state.task_pool.handle.spawn(ThreadIntent::Worker, { - let world = self.global_state.snapshot(); - move || { - let result = panic::catch_unwind(move || { - let _pctx = stdx::panic_context::enter(panic_context); - f(world, params) - }); - match thread_result_to_response::<R>(req.id.clone(), result) { - Ok(response) => Task::Response(response), - Err(_) => Task::Response(lsp_server::Response::new_err( - req.id, - lsp_server::ErrorCode::ContentModified as i32, - "content modified".to_owned(), - )), - } - } - }); - - self - } - /// Dispatches a non-latency-sensitive request onto the thread pool. - pub(crate) fn on<R>( + pub(crate) fn on<const ALLOW_RETRYING: bool, R>( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>, ) -> &mut Self @@ -142,11 +105,11 @@ impl RequestDispatcher<'_> { R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, R::Result: Serialize, { - self.on_with_thread_intent::<true, R>(ThreadIntent::Worker, f) + self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::Worker, f) } /// Dispatches a latency-sensitive request onto the thread pool. - pub(crate) fn on_latency_sensitive<R>( + pub(crate) fn on_latency_sensitive<const ALLOW_RETRYING: bool, R>( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>, ) -> &mut Self @@ -155,7 +118,7 @@ impl RequestDispatcher<'_> { R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, R::Result: Serialize, { - self.on_with_thread_intent::<true, R>(ThreadIntent::LatencySensitive, f) + self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::LatencySensitive, f) } /// Formatting requests should never block on waiting a for task thread to open up, editors will wait @@ -170,7 +133,7 @@ impl RequestDispatcher<'_> { R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, R::Result: Serialize, { - self.on_with_thread_intent::<false, R>(ThreadIntent::LatencySensitive, f) + self.on_with_thread_intent::<false, false, R>(ThreadIntent::LatencySensitive, f) } pub(crate) fn finish(&mut self) { @@ -185,7 +148,7 @@ impl RequestDispatcher<'_> { } } - fn on_with_thread_intent<const MAIN_POOL: bool, R>( + fn on_with_thread_intent<const MAIN_POOL: bool, const ALLOW_RETRYING: bool, R>( &mut self, intent: ThreadIntent, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>, @@ -215,7 +178,12 @@ impl RequestDispatcher<'_> { }); match thread_result_to_response::<R>(req.id.clone(), result) { Ok(response) => Task::Response(response), - Err(_) => Task::Retry(req), + Err(_cancelled) if ALLOW_RETRYING => Task::Retry(req), + Err(_cancelled) => Task::Response(lsp_server::Response::new_err( + req.id, + lsp_server::ErrorCode::ContentModified as i32, + "content modified".to_owned(), + )), } }); diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index e9bca19af6..f64e66183d 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -3,7 +3,7 @@ //! //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. -use std::{collections::hash_map::Entry, time::Instant}; +use std::time::Instant; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; @@ -18,10 +18,14 @@ use parking_lot::{ RwLockWriteGuard, }; use proc_macro_api::ProcMacroServer; -use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts}; +use project_model::{ + CargoWorkspace, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, Target, + WorkspaceBuildScripts, +}; use rustc_hash::{FxHashMap, FxHashSet}; +use tracing::{span, Level}; use triomphe::Arc; -use vfs::{AnchoredPathBuf, ChangedFile, Vfs}; +use vfs::{AnchoredPathBuf, Vfs}; use crate::{ config::{Config, ConfigError}, @@ -125,7 +129,7 @@ pub(crate) struct GlobalState { /// to invalidate any salsa caches. pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, pub(crate) crate_graph_file_dependencies: FxHashSet<vfs::VfsPath>, - pub(crate) detached_files: FxHashSet<vfs::AbsPathBuf>, + pub(crate) detached_files: FxHashSet<ManifestPath>, // op queues pub(crate) fetch_workspaces_queue: @@ -249,9 +253,7 @@ impl GlobalState { } pub(crate) fn process_changes(&mut self) -> bool { - let _p = tracing::span!(tracing::Level::INFO, "GlobalState::process_changes").entered(); - - let mut file_changes = FxHashMap::<_, (bool, ChangedFile)>::default(); + let _p = span!(Level::INFO, "GlobalState::process_changes").entered(); let (change, modified_rust_files, workspace_structure_change) = { let mut change = ChangeWithProcMacros::new(); let mut guard = self.vfs.write(); @@ -263,58 +265,16 @@ impl GlobalState { // downgrade to read lock to allow more readers while we are normalizing text let guard = RwLockWriteGuard::downgrade_to_upgradable(guard); let vfs: &Vfs = &guard.0; - // We need to fix up the changed events a bit. If we have a create or modify for a file - // id that is followed by a delete we actually skip observing the file text from the - // earlier event, to avoid problems later on. - for changed_file in changed_files { - use vfs::Change::*; - match file_changes.entry(changed_file.file_id) { - Entry::Occupied(mut o) => { - let (just_created, change) = o.get_mut(); - match (&mut change.change, just_created, changed_file.change) { - // latter `Delete` wins - (change, _, Delete) => *change = Delete, - // merge `Create` with `Create` or `Modify` - (Create(prev), _, Create(new) | Modify(new)) => *prev = new, - // collapse identical `Modify`es - (Modify(prev), _, Modify(new)) => *prev = new, - // equivalent to `Modify` - (change @ Delete, just_created, Create(new)) => { - *change = Modify(new); - *just_created = true; - } - // shouldn't occur, but collapse into `Create` - (change @ Delete, just_created, Modify(new)) => { - *change = Create(new); - *just_created = true; - } - // shouldn't occur, but keep the Create - (prev @ Modify(_), _, new @ Create(_)) => *prev = new, - } - } - Entry::Vacant(v) => { - _ = v.insert((matches!(&changed_file.change, Create(_)), changed_file)) - } - } - } - - let changed_files: Vec<_> = file_changes - .into_iter() - .filter(|(_, (just_created, change))| { - !(*just_created && matches!(change.change, vfs::Change::Delete)) - }) - .map(|(file_id, (_, change))| vfs::ChangedFile { file_id, ..change }) - .collect(); let mut workspace_structure_change = None; // A file was added or deleted let mut has_structure_changes = false; let mut bytes = vec![]; let mut modified_rust_files = vec![]; - for file in changed_files { + for file in changed_files.into_values() { let vfs_path = vfs.file_path(file.file_id); if let Some(path) = vfs_path.as_path() { - has_structure_changes = file.is_created_or_deleted(); + has_structure_changes |= file.is_created_or_deleted(); if file.is_modified() && path.extension() == Some("rs") { modified_rust_files.push(file.file_id); @@ -334,16 +294,17 @@ impl GlobalState { self.diagnostics.clear_native_for(file.file_id); } - let text = if let vfs::Change::Create(v) | vfs::Change::Modify(v) = file.change { - String::from_utf8(v).ok().map(|text| { - // FIXME: Consider doing normalization in the `vfs` instead? That allows - // getting rid of some locking - let (text, line_endings) = LineEndings::normalize(text); - (text, line_endings) - }) - } else { - None - }; + let text = + if let vfs::Change::Create(v, _) | vfs::Change::Modify(v, _) = file.change { + String::from_utf8(v).ok().map(|text| { + // FIXME: Consider doing normalization in the `vfs` instead? That allows + // getting rid of some locking + let (text, line_endings) = LineEndings::normalize(text); + (text, line_endings) + }) + } else { + None + }; // delay `line_endings_map` changes until we are done normalizing the text // this allows delaying the re-acquisition of the write lock bytes.push((file.file_id, text)); @@ -363,6 +324,7 @@ impl GlobalState { (change, modified_rust_files, workspace_structure_change) }; + let _p = span!(Level::INFO, "GlobalState::process_changes/apply_change").entered(); self.analysis_host.apply_change(change); { @@ -376,6 +338,9 @@ impl GlobalState { // but something's going wrong with the source root business when we add a new local // crate see https://github.com/rust-lang/rust-analyzer/issues/13029 if let Some((path, force_crate_graph_reload)) = workspace_structure_change { + let _p = span!(Level::INFO, "GlobalState::process_changes/ws_structure_change") + .entered(); + self.fetch_workspaces_queue.request_op( format!("workspace vfs file change: {path}"), force_crate_graph_reload, @@ -492,6 +457,10 @@ impl GlobalStateSnapshot { Ok(res) } + pub(crate) fn file_version(&self, file_id: FileId) -> Option<i32> { + Some(self.mem_docs.get(self.vfs_read().file_path(file_id))?.version) + } + pub(crate) fn url_file_version(&self, url: &Url) -> Option<i32> { let path = from_proto::vfs_path(url).ok()?; Some(self.mem_docs.get(&path)?.version) @@ -516,12 +485,13 @@ impl GlobalStateSnapshot { let file_id = self.analysis.crate_root(crate_id).ok()?; let path = self.vfs_read().file_path(file_id).clone(); let path = path.as_path()?; - self.workspaces.iter().find_map(|ws| match ws { - ProjectWorkspace::Cargo { cargo, .. } => { + self.workspaces.iter().find_map(|ws| match &ws.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => { cargo.target_by_root(path).map(|it| (cargo, it)) } - ProjectWorkspace::Json { .. } => None, - ProjectWorkspace::DetachedFile { .. } => None, + ProjectWorkspaceKind::Json { .. } => None, + ProjectWorkspaceKind::DetachedFile { .. } => None, }) } diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 4b8c3d06ce..9d30063ccc 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -289,17 +289,19 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { // Find all workspaces that have at least one target containing the saved file let workspace_ids = world.workspaces.iter().enumerate().filter_map(|(idx, ws)| { - let package = match ws { - project_model::ProjectWorkspace::Cargo { cargo, .. } => { - cargo.packages().find_map(|pkg| { - let has_target_with_root = cargo[pkg] - .targets - .iter() - .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())); - has_target_with_root.then(|| cargo[pkg].name.clone()) - }) - } - project_model::ProjectWorkspace::Json { project, .. } => { + let package = match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { cargo, .. } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _)), + .. + } => cargo.packages().find_map(|pkg| { + let has_target_with_root = cargo[pkg] + .targets + .iter() + .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())); + has_target_with_root.then(|| cargo[pkg].name.clone()) + }), + project_model::ProjectWorkspaceKind::Json(project) => { if !project.crates().any(|(_, krate)| { crate_root_paths.contains(&krate.root_module.as_path()) }) { @@ -307,8 +309,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } None } - // FIXME - project_model::ProjectWorkspace::DetachedFile { .. } => return None, + project_model::ProjectWorkspaceKind::DetachedFile { .. } => return None, }; Some((idx, package)) }); @@ -348,7 +349,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { - let _p = tracing::span!(tracing::Level::INFO, "handle_stop_flycheck").entered(); + let _p = tracing::span!(tracing::Level::INFO, "handle_cancel_flycheck").entered(); state.flycheck.iter().for_each(|flycheck| flycheck.cancel()); Ok(()) } diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index cf97d7d9d2..5ee0456c15 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -27,7 +27,7 @@ use lsp_types::{ SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, }; use paths::Utf8PathBuf; -use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; +use project_model::{ManifestPath, ProjectWorkspaceKind, TargetKind}; use serde_json::json; use stdx::{format_to, never}; use syntax::{algo, ast, AstNode, TextRange, TextSize}; @@ -99,10 +99,7 @@ pub(crate) fn handle_analyzer_status( format_to!( buf, "Workspace root folders: {:?}", - snap.workspaces - .iter() - .map(|ws| ws.workspace_definition_path()) - .collect::<Vec<&AbsPath>>() + snap.workspaces.iter().map(|ws| ws.manifest_or_root()).collect::<Vec<&AbsPath>>() ); } buf.push_str("\nAnalysis:\n"); @@ -228,7 +225,7 @@ pub(crate) fn handle_run_test( }; let mut handles = vec![]; for ws in &*state.workspaces { - if let ProjectWorkspace::Cargo { cargo, .. } = ws { + if let ProjectWorkspaceKind::Cargo { cargo, .. } = &ws.kind { let handle = flycheck::CargoTestHandle::new( test_path, state.config.cargo_test_options(), @@ -769,8 +766,11 @@ pub(crate) fn handle_parent_module( let links: Vec<LocationLink> = snap .workspaces .iter() - .filter_map(|ws| match ws { - ProjectWorkspace::Cargo { cargo, .. } => cargo.parent_manifests(&manifest_path), + .filter_map(|ws| match &ws.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => { + cargo.parent_manifests(&manifest_path) + } _ => None, }) .flatten() @@ -855,10 +855,14 @@ pub(crate) fn handle_runnables( let config = snap.config.runnables(); match cargo_spec { Some(spec) => { - let all_targets = !snap.analysis.is_crate_no_std(spec.crate_id)?; - for cmd in ["check", "test"] { + let is_crate_no_std = snap.analysis.is_crate_no_std(spec.crate_id)?; + for cmd in ["check", "run", "test"] { + if cmd == "run" && spec.target_kind != TargetKind::Bin { + continue; + } let mut cargo_args = vec![cmd.to_owned(), "--package".to_owned(), spec.package.clone()]; + let all_targets = cmd != "run" && !is_crate_no_std; if all_targets { cargo_args.push("--all-targets".to_owned()); } @@ -930,16 +934,18 @@ pub(crate) fn handle_related_tests( pub(crate) fn handle_completion( snap: GlobalStateSnapshot, - params: lsp_types::CompletionParams, + lsp_types::CompletionParams { text_document_position, context,.. }: lsp_types::CompletionParams, ) -> anyhow::Result<Option<lsp_types::CompletionResponse>> { let _p = tracing::span!(tracing::Level::INFO, "handle_completion").entered(); - let text_document_position = params.text_document_position.clone(); - let position = from_proto::file_position(&snap, params.text_document_position)?; + let mut position = from_proto::file_position(&snap, text_document_position.clone())?; + let line_index = snap.file_line_index(position.file_id)?; let completion_trigger_character = - params.context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next()); + context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next()); let source_root = snap.analysis.source_root(position.file_id)?; let completion_config = &snap.config.completion(Some(source_root)); + // FIXME: We should fix up the position when retrying the cancelled request instead + position.offset = position.offset.min(line_index.index.len()); let items = match snap.analysis.completions( completion_config, position, @@ -948,10 +954,14 @@ pub(crate) fn handle_completion( None => return Ok(None), Some(items) => items, }; - let line_index = snap.file_line_index(position.file_id)?; - let items = - to_proto::completion_items(&snap.config, &line_index, text_document_position, items); + let items = to_proto::completion_items( + &snap.config, + &line_index, + snap.file_version(position.file_id), + text_document_position, + items, + ); let completion_list = lsp_types::CompletionList { is_incomplete: true, items }; Ok(Some(completion_list.into())) @@ -970,16 +980,16 @@ pub(crate) fn handle_completion_resolve( .into()); } - let data = match original_completion.data.take() { - Some(it) => it, - None => return Ok(original_completion), - }; + let Some(data) = original_completion.data.take() else { return Ok(original_completion) }; let resolve_data: lsp_ext::CompletionResolveData = serde_json::from_value(data)?; let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; - let offset = from_proto::offset(&line_index, resolve_data.position.position)?; + // FIXME: We should fix up the position when retrying the cancelled request instead + let Ok(offset) = from_proto::offset(&line_index, resolve_data.position.position) else { + return Ok(original_completion); + }; let source_root = snap.analysis.source_root(file_id)?; let additional_edits = snap @@ -1236,8 +1246,11 @@ pub(crate) fn handle_code_action( frange, )?; for (index, assist) in assists.into_iter().enumerate() { - let resolve_data = - if code_action_resolve_cap { Some((index, params.clone())) } else { None }; + let resolve_data = if code_action_resolve_cap { + Some((index, params.clone(), snap.file_version(file_id))) + } else { + None + }; let code_action = to_proto::code_action(&snap, assist, resolve_data)?; // Check if the client supports the necessary `ResourceOperation`s. @@ -1276,12 +1289,14 @@ pub(crate) fn handle_code_action_resolve( mut code_action: lsp_ext::CodeAction, ) -> anyhow::Result<lsp_ext::CodeAction> { let _p = tracing::span!(tracing::Level::INFO, "handle_code_action_resolve").entered(); - let params = match code_action.data.take() { - Some(it) => it, - None => return Err(invalid_params_error("code action without data".to_owned()).into()), + let Some(params) = code_action.data.take() else { + return Err(invalid_params_error("code action without data".to_owned()).into()); }; let file_id = from_proto::file_id(&snap, ¶ms.code_action_params.text_document.uri)?; + if snap.file_version(file_id) != params.version { + return Err(invalid_params_error("stale code action".to_owned()).into()); + } let line_index = snap.file_line_index(file_id)?; let range = from_proto::text_range(&line_index, params.code_action_params.range)?; let frange = FileRange { file_id, range }; @@ -1407,12 +1422,11 @@ pub(crate) fn handle_code_lens( pub(crate) fn handle_code_lens_resolve( snap: GlobalStateSnapshot, - code_lens: CodeLens, + mut code_lens: CodeLens, ) -> anyhow::Result<CodeLens> { - if code_lens.data.is_none() { - return Ok(code_lens); - } - let Some(annotation) = from_proto::annotation(&snap, code_lens.clone())? else { + let Some(data) = code_lens.data.take() else { return Ok(code_lens) }; + let resolve = serde_json::from_value::<lsp_ext::CodeLensResolveData>(data)?; + let Some(annotation) = from_proto::annotation(&snap, code_lens.range, resolve)? else { return Ok(code_lens); }; let annotation = snap.analysis.resolve_annotation(annotation)?; @@ -1491,6 +1505,10 @@ pub(crate) fn handle_inlay_hints( )?; let line_index = snap.file_line_index(file_id)?; let source_root = snap.analysis.source_root(file_id)?; + let range = TextRange::new( + range.start().min(line_index.index.len()), + range.end().min(line_index.index.len()), + ); let inlay_hints_config = snap.config.inlay_hints(Some(source_root)); Ok(Some( @@ -1518,8 +1536,12 @@ pub(crate) fn handle_inlay_hints_resolve( let Some(data) = original_hint.data.take() else { return Ok(original_hint) }; let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?; - let Some(hash) = resolve_data.hash.parse().ok() else { return Ok(original_hint) }; let file_id = FileId::from_raw(resolve_data.file_id); + if resolve_data.version != snap.file_version(file_id) { + tracing::warn!("Inlay hint resolve data is outdated"); + return Ok(original_hint); + } + let Some(hash) = resolve_data.hash.parse().ok() else { return Ok(original_hint) }; anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data"); let line_index = snap.file_line_index(file_id)?; @@ -1758,12 +1780,13 @@ pub(crate) fn handle_open_docs( let _p = tracing::span!(tracing::Level::INFO, "handle_open_docs").entered(); let position = from_proto::file_position(&snap, params)?; - let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match ws { - ProjectWorkspace::Cargo { cargo, sysroot, .. } => Some((cargo, sysroot.as_ref().ok())), - ProjectWorkspace::Json { .. } => None, - ProjectWorkspace::DetachedFile { cargo_script, sysroot, .. } => { - cargo_script.as_ref().zip(Some(sysroot.as_ref().ok())) + let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match &ws.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } => { + Some((cargo, ws.sysroot.as_ref().ok())) } + ProjectWorkspaceKind::Json { .. } => None, + ProjectWorkspaceKind::DetachedFile { .. } => None, }); let (cargo, sysroot) = match ws_and_sysroot { @@ -1836,7 +1859,7 @@ pub(crate) fn handle_view_recursive_memory_layout( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, ) -> anyhow::Result<Option<lsp_ext::RecursiveMemoryLayout>> { - let _p = tracing::span!(tracing::Level::INFO, "view_recursive_memory_layout").entered(); + let _p = tracing::span!(tracing::Level::INFO, "handle_view_recursive_memory_layout").entered(); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; let offset = from_proto::offset(&line_index, params.position)?; diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 7b385ca9d9..cc83d6246b 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -153,6 +153,7 @@ fn integrated_completion_benchmark() { prefer_no_std: false, prefer_prelude: true, limit: None, + term_search_fuel: 200, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -197,6 +198,7 @@ fn integrated_completion_benchmark() { prefer_no_std: false, prefer_prelude: true, limit: None, + term_search_fuel: 200, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -239,6 +241,7 @@ fn integrated_completion_benchmark() { prefer_no_std: false, prefer_prelude: true, limit: None, + term_search_fuel: 200, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -295,6 +298,7 @@ fn integrated_diagnostics_benchmark() { }, prefer_no_std: false, prefer_prelude: false, + term_search_fuel: 400, }; host.analysis() .diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id) diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs index 12f8e71c98..2cf9b53f7c 100644 --- a/crates/rust-analyzer/src/lsp/ext.rs +++ b/crates/rust-analyzer/src/lsp/ext.rs @@ -2,6 +2,7 @@ #![allow(clippy::disallowed_types)] +use std::ops; use std::path::PathBuf; use ide_db::line_index::WideEncoding; @@ -494,10 +495,12 @@ impl Notification for ServerStatusNotification { } #[derive(Deserialize, Serialize, PartialEq, Eq, Clone)] +#[serde(rename_all = "camelCase")] pub struct ServerStatusParams { pub health: Health, pub quiescent: bool, pub message: Option<String>, + pub workspace_info: Option<String>, } #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] @@ -508,6 +511,16 @@ pub enum Health { Error, } +impl ops::BitOrAssign for Health { + fn bitor_assign(&mut self, rhs: Self) { + *self = match (*self, rhs) { + (Health::Error, _) | (_, Health::Error) => Health::Error, + (Health::Warning, _) | (_, Health::Warning) => Health::Warning, + _ => Health::Ok, + } + } +} + pub enum CodeActionRequest {} impl Request for CodeActionRequest { @@ -548,6 +561,7 @@ pub struct CodeAction { pub struct CodeActionData { pub code_action_params: lsp_types::CodeActionParams, pub id: String, + pub version: Option<i32>, } #[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)] @@ -789,6 +803,7 @@ impl Request for OnTypeFormatting { pub struct CompletionResolveData { pub position: lsp_types::TextDocumentPositionParams, pub imports: Vec<CompletionImport>, + pub version: Option<i32>, } #[derive(Debug, Serialize, Deserialize)] @@ -796,6 +811,7 @@ pub struct InlayHintResolveData { pub file_id: u32, // This is a string instead of a u64 as javascript can't represent u64 fully pub hash: String, + pub version: Option<i32>, } #[derive(Debug, Serialize, Deserialize)] diff --git a/crates/rust-analyzer/src/lsp/from_proto.rs b/crates/rust-analyzer/src/lsp/from_proto.rs index f42985a916..b6b20296d8 100644 --- a/crates/rust-analyzer/src/lsp/from_proto.rs +++ b/crates/rust-analyzer/src/lsp/from_proto.rs @@ -9,10 +9,8 @@ use syntax::{TextRange, TextSize}; use vfs::AbsPathBuf; use crate::{ - from_json, global_state::GlobalStateSnapshot, line_index::{LineIndex, PositionEncoding}, - lsp::utils::invalid_params_error, lsp_ext, }; @@ -105,16 +103,13 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind> pub(crate) fn annotation( snap: &GlobalStateSnapshot, - code_lens: lsp_types::CodeLens, + range: lsp_types::Range, + data: lsp_ext::CodeLensResolveData, ) -> anyhow::Result<Option<Annotation>> { - let data = - code_lens.data.ok_or_else(|| invalid_params_error("code lens without data".to_owned()))?; - let resolve = from_json::<lsp_ext::CodeLensResolveData>("CodeLensResolveData", &data)?; - - match resolve.kind { + match data.kind { lsp_ext::CodeLensResolveDataKind::Impls(params) => { if snap.url_file_version(¶ms.text_document_position_params.text_document.uri) - != Some(resolve.version) + != Some(data.version) { return Ok(None); } @@ -123,19 +118,19 @@ pub(crate) fn annotation( let line_index = snap.file_line_index(file_id)?; Ok(Annotation { - range: text_range(&line_index, code_lens.range)?, + range: text_range(&line_index, range)?, kind: AnnotationKind::HasImpls { pos, data: None }, }) } lsp_ext::CodeLensResolveDataKind::References(params) => { - if snap.url_file_version(¶ms.text_document.uri) != Some(resolve.version) { + if snap.url_file_version(¶ms.text_document.uri) != Some(data.version) { return Ok(None); } let pos @ FilePosition { file_id, .. } = file_position(snap, params)?; let line_index = snap.file_line_index(file_id)?; Ok(Annotation { - range: text_range(&line_index, code_lens.range)?, + range: text_range(&line_index, range)?, kind: AnnotationKind::HasReferences { pos, data: None }, }) } diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index d02f4612dc..03daccc99c 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -225,13 +225,14 @@ pub(crate) fn snippet_text_edit_vec( pub(crate) fn completion_items( config: &Config, line_index: &LineIndex, + version: Option<i32>, tdpp: lsp_types::TextDocumentPositionParams, items: Vec<CompletionItem>, ) -> Vec<lsp_types::CompletionItem> { let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default(); let mut res = Vec::with_capacity(items.len()); for item in items { - completion_item(&mut res, config, line_index, &tdpp, max_relevance, item); + completion_item(&mut res, config, line_index, version, &tdpp, max_relevance, item); } if let Some(limit) = config.completion(None).limit { @@ -246,6 +247,7 @@ fn completion_item( acc: &mut Vec<lsp_types::CompletionItem>, config: &Config, line_index: &LineIndex, + version: Option<i32>, tdpp: &lsp_types::TextDocumentPositionParams, max_relevance: u32, item: CompletionItem, @@ -328,7 +330,7 @@ fn completion_item( }) .collect::<Vec<_>>(); if !imports.is_empty() { - let data = lsp_ext::CompletionResolveData { position: tdpp.clone(), imports }; + let data = lsp_ext::CompletionResolveData { position: tdpp.clone(), imports, version }; lsp_item.data = Some(to_value(data).unwrap()); } } @@ -483,6 +485,7 @@ pub(crate) fn inlay_hint( to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index(), hash: hash.to_string(), + version: snap.file_version(file_id), }) .unwrap(), ), @@ -1318,7 +1321,7 @@ pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind { pub(crate) fn code_action( snap: &GlobalStateSnapshot, assist: Assist, - resolve_data: Option<(usize, lsp_types::CodeActionParams)>, + resolve_data: Option<(usize, lsp_types::CodeActionParams, Option<i32>)>, ) -> Cancellable<lsp_ext::CodeAction> { let mut res = lsp_ext::CodeAction { title: assist.label.to_string(), @@ -1336,10 +1339,11 @@ pub(crate) fn code_action( match (assist.source_change, resolve_data) { (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?), - (None, Some((index, code_action_params))) => { + (None, Some((index, code_action_params, version))) => { res.data = Some(lsp_ext::CodeActionData { id: format!("{}:{}:{index}", assist.id.0, assist.id.1.name()), code_action_params, + version, }); } (None, None) => { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index f37b25fb95..5435be3dc2 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -12,6 +12,7 @@ use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath}; use lsp_server::{Connection, Notification, Request}; use lsp_types::{notification::Notification as _, TextDocumentIdentifier}; use stdx::thread::ThreadIntent; +use tracing::{span, Level}; use vfs::FileId; use crate::{ @@ -44,7 +45,7 @@ pub fn main_loop(config: Config, connection: Connection) -> anyhow::Result<()> { // https://github.com/rust-lang/rust-analyzer/issues/2835 #[cfg(windows)] unsafe { - use winapi::um::processthreadsapi::*; + use windows_sys::Win32::System::Threading::*; let thread = GetCurrentThread(); let thread_priority_above_normal = 1; SetThreadPriority(thread, thread_priority_above_normal); @@ -229,8 +230,7 @@ impl GlobalState { fn handle_event(&mut self, event: Event) -> anyhow::Result<()> { let loop_start = Instant::now(); // NOTE: don't count blocking select! call as a loop-turn time - let _p = tracing::span!(tracing::Level::INFO, "GlobalState::handle_event", event = %event) - .entered(); + let _p = tracing::span!(Level::INFO, "GlobalState::handle_event", event = %event).entered(); let event_dbg_msg = format!("{event:?}"); tracing::debug!(?loop_start, ?event, "handle_event"); @@ -669,9 +669,12 @@ impl GlobalState { } fn handle_vfs_msg(&mut self, message: vfs::loader::Message) { + let _p = tracing::span!(Level::INFO, "GlobalState::handle_vfs_msg").entered(); let is_changed = matches!(message, vfs::loader::Message::Changed { .. }); match message { vfs::loader::Message::Changed { files } | vfs::loader::Message::Loaded { files } => { + let _p = tracing::span!(Level::INFO, "GlobalState::handle_vfs_msg{changed/load}") + .entered(); let vfs = &mut self.vfs.write().0; for (path, contents) in files { let path = VfsPath::from(path); @@ -685,6 +688,8 @@ impl GlobalState { } } vfs::loader::Message::Progress { n_total, n_done, dir, config_version } => { + let _p = + tracing::span!(Level::INFO, "GlobalState::handle_vfs_mgs/progress").entered(); always!(config_version <= self.vfs_config_version); let state = match n_done { @@ -867,6 +872,8 @@ impl GlobalState { /// Registers and handles a request. This should only be called once per incoming request. fn on_new_request(&mut self, request_received: Instant, req: Request) { + let _p = + span!(Level::INFO, "GlobalState::on_new_request", req.method = ?req.method).entered(); self.register_request(&req, request_received); self.on_request(req); } @@ -894,6 +901,10 @@ impl GlobalState { use crate::handlers::request as handlers; use lsp_types::request as lsp_request; + const RETRY: bool = true; + const NO_RETRY: bool = false; + + #[rustfmt::skip] dispatcher // Request handlers that must run on the main thread // because they mutate GlobalState: @@ -919,67 +930,65 @@ impl GlobalState { // analysis on the main thread because that would block other // requests. Instead, we run these request handlers on higher priority // threads in the threadpool. - .on_latency_sensitive::<lsp_request::Completion>(handlers::handle_completion) - .on_latency_sensitive::<lsp_request::ResolveCompletionItem>( - handlers::handle_completion_resolve, - ) - .on_latency_sensitive::<lsp_request::SemanticTokensFullRequest>( - handlers::handle_semantic_tokens_full, - ) - .on_latency_sensitive::<lsp_request::SemanticTokensFullDeltaRequest>( - handlers::handle_semantic_tokens_full_delta, - ) - .on_latency_sensitive::<lsp_request::SemanticTokensRangeRequest>( - handlers::handle_semantic_tokens_range, - ) + // FIXME: Retrying can make the result of this stale? + .on_latency_sensitive::<RETRY, lsp_request::Completion>(handlers::handle_completion) + // FIXME: Retrying can make the result of this stale + .on_latency_sensitive::<RETRY, lsp_request::ResolveCompletionItem>(handlers::handle_completion_resolve) + .on_latency_sensitive::<RETRY, lsp_request::SemanticTokensFullRequest>(handlers::handle_semantic_tokens_full) + .on_latency_sensitive::<RETRY, lsp_request::SemanticTokensFullDeltaRequest>(handlers::handle_semantic_tokens_full_delta) + .on_latency_sensitive::<NO_RETRY, lsp_request::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range) + // FIXME: Some of these NO_RETRY could be retries if the file they are interested didn't change. // All other request handlers - .on::<lsp_ext::FetchDependencyList>(handlers::fetch_dependency_list) - .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status) - .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) - .on::<lsp_ext::ViewHir>(handlers::handle_view_hir) - .on::<lsp_ext::ViewMir>(handlers::handle_view_mir) - .on::<lsp_ext::InterpretFunction>(handlers::handle_interpret_function) - .on::<lsp_ext::ViewFileText>(handlers::handle_view_file_text) - .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph) - .on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree) - .on::<lsp_ext::DiscoverTest>(handlers::handle_discover_test) - .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro) - .on::<lsp_ext::ParentModule>(handlers::handle_parent_module) - .on::<lsp_ext::Runnables>(handlers::handle_runnables) - .on::<lsp_ext::RelatedTests>(handlers::handle_related_tests) - .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action) - .on::<lsp_ext::CodeActionResolveRequest>(handlers::handle_code_action_resolve) - .on::<lsp_ext::HoverRequest>(handlers::handle_hover) - .on::<lsp_ext::ExternalDocs>(handlers::handle_open_docs) - .on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml) - .on::<lsp_ext::MoveItem>(handlers::handle_move_item) - .on::<lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol) - .on::<lsp_request::DocumentSymbolRequest>(handlers::handle_document_symbol) - .on::<lsp_request::GotoDefinition>(handlers::handle_goto_definition) - .on::<lsp_request::GotoDeclaration>(handlers::handle_goto_declaration) - .on::<lsp_request::GotoImplementation>(handlers::handle_goto_implementation) - .on::<lsp_request::GotoTypeDefinition>(handlers::handle_goto_type_definition) - .on_no_retry::<lsp_request::InlayHintRequest>(handlers::handle_inlay_hints) - .on::<lsp_request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve) - .on::<lsp_request::CodeLensRequest>(handlers::handle_code_lens) - .on::<lsp_request::CodeLensResolve>(handlers::handle_code_lens_resolve) - .on::<lsp_request::FoldingRangeRequest>(handlers::handle_folding_range) - .on::<lsp_request::SignatureHelpRequest>(handlers::handle_signature_help) - .on::<lsp_request::PrepareRenameRequest>(handlers::handle_prepare_rename) - .on::<lsp_request::Rename>(handlers::handle_rename) - .on::<lsp_request::References>(handlers::handle_references) - .on::<lsp_request::DocumentHighlightRequest>(handlers::handle_document_highlight) - .on::<lsp_request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare) - .on::<lsp_request::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming) - .on::<lsp_request::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing) - .on::<lsp_request::WillRenameFiles>(handlers::handle_will_rename_files) - .on::<lsp_ext::Ssr>(handlers::handle_ssr) - .on::<lsp_ext::ViewRecursiveMemoryLayout>(handlers::handle_view_recursive_memory_layout) + .on::<RETRY, lsp_request::DocumentSymbolRequest>(handlers::handle_document_symbol) + .on::<RETRY, lsp_request::FoldingRangeRequest>(handlers::handle_folding_range) + .on::<NO_RETRY, lsp_request::SignatureHelpRequest>(handlers::handle_signature_help) + .on::<RETRY, lsp_request::WillRenameFiles>(handlers::handle_will_rename_files) + .on::<NO_RETRY, lsp_request::GotoDefinition>(handlers::handle_goto_definition) + .on::<NO_RETRY, lsp_request::GotoDeclaration>(handlers::handle_goto_declaration) + .on::<NO_RETRY, lsp_request::GotoImplementation>(handlers::handle_goto_implementation) + .on::<NO_RETRY, lsp_request::GotoTypeDefinition>(handlers::handle_goto_type_definition) + .on::<RETRY, lsp_request::InlayHintRequest>(handlers::handle_inlay_hints) + .on::<RETRY, lsp_request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve) + .on::<NO_RETRY, lsp_request::CodeLensRequest>(handlers::handle_code_lens) + .on::<RETRY, lsp_request::CodeLensResolve>(handlers::handle_code_lens_resolve) + .on::<NO_RETRY, lsp_request::PrepareRenameRequest>(handlers::handle_prepare_rename) + .on::<NO_RETRY, lsp_request::Rename>(handlers::handle_rename) + .on::<NO_RETRY, lsp_request::References>(handlers::handle_references) + .on::<NO_RETRY, lsp_request::DocumentHighlightRequest>(handlers::handle_document_highlight) + .on::<NO_RETRY, lsp_request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare) + .on::<NO_RETRY, lsp_request::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming) + .on::<NO_RETRY, lsp_request::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing) + // All other request handlers (lsp extension) + .on::<RETRY, lsp_ext::FetchDependencyList>(handlers::fetch_dependency_list) + .on::<RETRY, lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status) + .on::<RETRY, lsp_ext::ViewFileText>(handlers::handle_view_file_text) + .on::<RETRY, lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph) + .on::<RETRY, lsp_ext::ViewItemTree>(handlers::handle_view_item_tree) + .on::<RETRY, lsp_ext::DiscoverTest>(handlers::handle_discover_test) + .on::<RETRY, lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol) + .on::<NO_RETRY, lsp_ext::Ssr>(handlers::handle_ssr) + .on::<NO_RETRY, lsp_ext::ViewRecursiveMemoryLayout>(handlers::handle_view_recursive_memory_layout) + .on::<NO_RETRY, lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) + .on::<NO_RETRY, lsp_ext::ViewHir>(handlers::handle_view_hir) + .on::<NO_RETRY, lsp_ext::ViewMir>(handlers::handle_view_mir) + .on::<NO_RETRY, lsp_ext::InterpretFunction>(handlers::handle_interpret_function) + .on::<NO_RETRY, lsp_ext::ExpandMacro>(handlers::handle_expand_macro) + .on::<NO_RETRY, lsp_ext::ParentModule>(handlers::handle_parent_module) + .on::<NO_RETRY, lsp_ext::Runnables>(handlers::handle_runnables) + .on::<NO_RETRY, lsp_ext::RelatedTests>(handlers::handle_related_tests) + .on::<NO_RETRY, lsp_ext::CodeActionRequest>(handlers::handle_code_action) + .on::<RETRY, lsp_ext::CodeActionResolveRequest>(handlers::handle_code_action_resolve) + .on::<NO_RETRY, lsp_ext::HoverRequest>(handlers::handle_hover) + .on::<NO_RETRY, lsp_ext::ExternalDocs>(handlers::handle_open_docs) + .on::<NO_RETRY, lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml) + .on::<NO_RETRY, lsp_ext::MoveItem>(handlers::handle_move_item) .finish(); } /// Handles an incoming notification. fn on_notification(&mut self, not: Notification) -> anyhow::Result<()> { + let _p = + span!(Level::INFO, "GlobalState::on_notification", not.method = ?not.method).entered(); use crate::handlers::notification as handlers; use lsp_types::notification as notifs; diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 5d8a66cabc..fd14efa1da 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -25,7 +25,7 @@ use ide_db::{ use itertools::Itertools; use load_cargo::{load_proc_macro, ProjectFolders}; use proc_macro_api::ProcMacroServer; -use project_model::{ProjectWorkspace, WorkspaceBuildScripts}; +use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts}; use stdx::{format_to, thread::ThreadIntent}; use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, ChangeKind}; @@ -103,81 +103,48 @@ impl GlobalState { health: lsp_ext::Health::Ok, quiescent: self.is_quiescent(), message: None, + workspace_info: None, }; let mut message = String::new(); + if !self.config.cargo_autoreload() + && self.is_quiescent() + && self.fetch_workspaces_queue.op_requested() + { + status.health |= lsp_ext::Health::Warning; + message.push_str("Auto-reloading is disabled and the workspace has changed, a manual workspace reload is required.\n\n"); + } + if self.build_deps_changed { - status.health = lsp_ext::Health::Warning; + status.health |= lsp_ext::Health::Warning; message.push_str( "Proc-macros and/or build scripts have changed and need to be rebuilt.\n\n", ); } if self.fetch_build_data_error().is_err() { - status.health = lsp_ext::Health::Warning; + status.health |= lsp_ext::Health::Warning; message.push_str("Failed to run build scripts of some packages.\n\n"); } - if self.proc_macro_clients.iter().any(|it| it.is_err()) { - status.health = lsp_ext::Health::Warning; - message.push_str("Failed to spawn one or more proc-macro servers.\n\n"); - for err in self.proc_macro_clients.iter() { - if let Err(err) = err { - format_to!(message, "- {err}\n"); - } - } - } - if !self.config.cargo_autoreload() - && self.is_quiescent() - && self.fetch_workspaces_queue.op_requested() - { - status.health = lsp_ext::Health::Warning; - message.push_str("Auto-reloading is disabled and the workspace has changed, a manual workspace reload is required.\n\n"); - } - if self.config.linked_or_discovered_projects().is_empty() - && self.config.detached_files().is_empty() - && self.config.notifications().cargo_toml_not_found - { - status.health = lsp_ext::Health::Warning; - message.push_str("Failed to discover workspace.\n"); - message.push_str("Consider adding the `Cargo.toml` of the workspace to the [`linkedProjects`](https://rust-analyzer.github.io/manual.html#rust-analyzer.linkedProjects) setting.\n\n"); - } + if let Some(err) = &self.config_errors { - status.health = lsp_ext::Health::Warning; + status.health |= lsp_ext::Health::Warning; format_to!(message, "{err}\n"); } if let Some(err) = &self.last_flycheck_error { - status.health = lsp_ext::Health::Warning; + status.health |= lsp_ext::Health::Warning; message.push_str(err); message.push('\n'); } - for ws in self.workspaces.iter() { - let (ProjectWorkspace::Cargo { sysroot, .. } - | ProjectWorkspace::Json { sysroot, .. } - | ProjectWorkspace::DetachedFile { sysroot, .. }) = ws; - match sysroot { - Err(None) => (), - Err(Some(e)) => { - status.health = lsp_ext::Health::Warning; - message.push_str(e); - message.push_str("\n\n"); - } - Ok(s) => { - if let Some(e) = s.loading_warning() { - status.health = lsp_ext::Health::Warning; - message.push_str(&e); - message.push_str("\n\n"); - } - } - } - if let ProjectWorkspace::Cargo { rustc: Err(Some(e)), .. } = ws { - status.health = lsp_ext::Health::Warning; - message.push_str(e); - message.push_str("\n\n"); - } + if self.config.linked_or_discovered_projects().is_empty() + && self.config.detached_files().is_empty() + { + status.health |= lsp_ext::Health::Warning; + message.push_str("Failed to discover workspace.\n"); + message.push_str("Consider adding the `Cargo.toml` of the workspace to the [`linkedProjects`](https://rust-analyzer.github.io/manual.html#rust-analyzer.linkedProjects) setting.\n\n"); } - if self.fetch_workspace_error().is_err() { - status.health = lsp_ext::Health::Error; + status.health |= lsp_ext::Health::Error; message.push_str("Failed to load workspaces."); if self.config.has_linked_projects() { @@ -193,9 +160,63 @@ impl GlobalState { message.push_str("\n\n"); } + if !self.workspaces.is_empty() { + let proc_macro_clients = + self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None)); + + let mut workspace_info = "Loaded workspaces:\n".to_owned(); + for (ws, proc_macro_client) in self.workspaces.iter().zip(proc_macro_clients) { + format_to!(workspace_info, "- `{}`\n", ws.manifest_or_root()); + format_to!(workspace_info, " - sysroot:"); + + match ws.sysroot.as_ref() { + Err(None) => format_to!(workspace_info, " None"), + Err(Some(e)) => { + status.health |= lsp_ext::Health::Warning; + format_to!(workspace_info, " {e}"); + } + Ok(s) => { + format_to!(workspace_info, " `{}`", s.root().to_string()); + if let Some(err) = s + .check_has_core() + .err() + .inspect(|_| status.health |= lsp_ext::Health::Warning) + { + format_to!(workspace_info, " ({err})"); + } + if let Some(src_root) = s.src_root() { + format_to!( + workspace_info, + "\n - sysroot source: `{}`", + src_root + ); + } + format_to!(workspace_info, "\n"); + } + } + + if let ProjectWorkspaceKind::Cargo { rustc: Err(Some(e)), .. } = &ws.kind { + status.health |= lsp_ext::Health::Warning; + format_to!(workspace_info, " - rustc workspace: {e}\n"); + }; + if let Some(proc_macro_client) = proc_macro_client { + format_to!(workspace_info, " - proc-macro server: "); + match proc_macro_client { + Ok(it) => format_to!(workspace_info, "`{}`\n", it.path()), + Err(e) => { + status.health |= lsp_ext::Health::Warning; + format_to!(workspace_info, "{e}\n") + } + } + } + } + status.workspace_info = Some(workspace_info); + } + if !message.is_empty() { status.message = Some(message.trim_end().to_owned()); } + status } @@ -204,7 +225,14 @@ impl GlobalState { self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, { let linked_projects = self.config.linked_or_discovered_projects(); - let detached_files = self.config.detached_files().to_vec(); + let detached_files: Vec<_> = self + .config + .detached_files() + .iter() + .cloned() + .map(ManifestPath::try_from) + .filter_map(Result::ok) + .collect(); let cargo_config = self.config.cargo(); move |sender| { @@ -494,23 +522,28 @@ impl GlobalState { None => ws.find_sysroot_proc_macro_srv()?, }; - let env = - match ws { - ProjectWorkspace::Cargo { cargo_config_extra_env, sysroot, .. } => { - cargo_config_extra_env - .iter() - .chain(self.config.extra_env()) - .map(|(a, b)| (a.clone(), b.clone())) - .chain(sysroot.as_ref().map(|it| { - ("RUSTUP_TOOLCHAIN".to_owned(), it.root().to_string()) - })) - .collect() - } - _ => Default::default(), - }; + let env = match &ws.kind { + ProjectWorkspaceKind::Cargo { cargo_config_extra_env, .. } + | ProjectWorkspaceKind::DetachedFile { + cargo: Some(_), + cargo_config_extra_env, + .. + } => cargo_config_extra_env + .iter() + .chain(self.config.extra_env()) + .map(|(a, b)| (a.clone(), b.clone())) + .chain( + ws.sysroot + .as_ref() + .map(|it| ("RUSTUP_TOOLCHAIN".to_owned(), it.root().to_string())), + ) + .collect(), + + _ => Default::default(), + }; tracing::info!("Using proc-macro server at {path}"); - ProcMacroServer::spawn(path.clone(), &env).map_err(|err| { + ProcMacroServer::spawn(&path, &env).map_err(|err| { tracing::error!( "Failed to run proc-macro server from path {path}, error: {err:?}", ); @@ -554,8 +587,8 @@ impl GlobalState { self.detached_files = self .workspaces .iter() - .filter_map(|ws| match ws { - ProjectWorkspace::DetachedFile { file, .. } => Some(file.clone()), + .filter_map(|ws| match &ws.kind { + ProjectWorkspaceKind::DetachedFile { file, .. } => Some(file.clone()), _ => None, }) .collect(); @@ -659,33 +692,37 @@ impl GlobalState { config, None, self.config.root_path().clone(), + None, )], flycheck::InvocationStrategy::PerWorkspace => { self.workspaces .iter() .enumerate() - .filter_map(|(id, w)| match w { - ProjectWorkspace::Cargo { cargo, sysroot, .. } => Some(( + .filter_map(|(id, ws)| { + Some(( id, - cargo.workspace_root(), - sysroot.as_ref().ok().map(|sysroot| sysroot.root().to_owned()), - )), - ProjectWorkspace::Json { project, sysroot, .. } => { - // Enable flychecks for json projects if a custom flycheck command was supplied - // in the workspace configuration. - match config { - FlycheckConfig::CustomCommand { .. } => Some(( - id, - project.path(), - sysroot.as_ref().ok().map(|sysroot| sysroot.root().to_owned()), - )), - _ => None, - } - } - // FIXME - ProjectWorkspace::DetachedFile { .. } => None, + match &ws.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _)), + .. + } => (cargo.workspace_root(), Some(cargo.manifest_path())), + ProjectWorkspaceKind::Json(project) => { + // Enable flychecks for json projects if a custom flycheck command was supplied + // in the workspace configuration. + match config { + FlycheckConfig::CustomCommand { .. } => { + (project.path(), None) + } + _ => return None, + } + } + ProjectWorkspaceKind::DetachedFile { .. } => return None, + }, + ws.sysroot.as_ref().ok().map(|sysroot| sysroot.root().to_owned()), + )) }) - .map(|(id, root, sysroot_root)| { + .map(|(id, (root, manifest_path), sysroot_root)| { let sender = sender.clone(); FlycheckHandle::spawn( id, @@ -693,6 +730,7 @@ impl GlobalState { config.clone(), sysroot_root, root.to_path_buf(), + manifest_path.map(|it| it.to_path_buf()), ) }) .collect() @@ -722,9 +760,7 @@ pub fn ws_to_crate_graph( let (other, mut crate_proc_macros) = ws.to_crate_graph(&mut load, extra_env); let num_layouts = layouts.len(); let num_toolchains = toolchains.len(); - let (ProjectWorkspace::Cargo { toolchain, target_layout, .. } - | ProjectWorkspace::Json { toolchain, target_layout, .. } - | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws; + let ProjectWorkspace { toolchain, target_layout, .. } = ws; let mapping = crate_graph.extend( other, diff --git a/crates/rust-analyzer/src/task_pool.rs b/crates/rust-analyzer/src/task_pool.rs index f7de5fb2ff..2bcd8505e8 100644 --- a/crates/rust-analyzer/src/task_pool.rs +++ b/crates/rust-analyzer/src/task_pool.rs @@ -45,7 +45,7 @@ impl<T> TaskPool<T> { /// `TaskQueue`, like its name suggests, queues tasks. /// -/// This should only be used used if a task must run after [`GlobalState::process_changes`] +/// This should only be used if a task must run after [`GlobalState::process_changes`] /// has been called. pub(crate) struct TaskQueue { pub(crate) sender: crossbeam_channel::Sender<QueuedTask>, diff --git a/crates/rust-analyzer/tests/crate_graph.rs b/crates/rust-analyzer/tests/crate_graph.rs index cf38032b94..59b229cd06 100644 --- a/crates/rust-analyzer/tests/crate_graph.rs +++ b/crates/rust-analyzer/tests/crate_graph.rs @@ -1,24 +1,31 @@ use std::path::PathBuf; -use project_model::{CargoWorkspace, ProjectWorkspace, Sysroot, WorkspaceBuildScripts}; +use project_model::{ + CargoWorkspace, ManifestPath, Metadata, ProjectWorkspace, ProjectWorkspaceKind, Sysroot, + WorkspaceBuildScripts, +}; use rust_analyzer::ws_to_crate_graph; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; use vfs::{AbsPathBuf, FileId}; fn load_cargo_with_fake_sysroot(file: &str) -> ProjectWorkspace { - let meta = get_test_json_file(file); - let cargo_workspace = CargoWorkspace::new(meta); - ProjectWorkspace::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), + let meta: Metadata = get_test_json_file(file); + let manifest_path = + ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); + let cargo_workspace = CargoWorkspace::new(meta, manifest_path); + ProjectWorkspace { + kind: ProjectWorkspaceKind::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + rustc: Err(None), + cargo_config_extra_env: Default::default(), + }, sysroot: Ok(get_fake_sysroot()), - rustc: Err(None), rustc_cfg: Vec::new(), cfg_overrides: Default::default(), toolchain: None, target_layout: Err("target_data_layout not loaded".into()), - cargo_config_extra_env: Default::default(), } } diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index b87f02947b..5a1397bbb0 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -150,6 +150,7 @@ use dependency2::Spam; ) .with_config(serde_json::json!({ "cargo": { "sysroot": null }, + "linkedProjects": ["src/lib.rs"], })) .server() .wait_until_workspace_is_loaded(); diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index f04962a7a2..cf27cc7eef 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -185,11 +185,7 @@ impl Project<'_> { roots, None, ); - // TODO: don't hardcode src/lib.rs as detached file - let mut c = self.config; - let p = tmp_dir_path.join("src/lib.rs").to_string(); - c["detachedFiles"] = serde_json::json!([p]); - config.update(c).expect("invalid config"); + config.update(self.config).expect("invalid config"); config.rediscover_workspaces(); Server::new(tmp_dir.keep(), config) diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs index 3443939133..4a7415b016 100644 --- a/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -144,6 +144,7 @@ MIT OR Apache-2.0 MIT OR Apache-2.0 OR Zlib MIT OR Zlib OR Apache-2.0 MIT/Apache-2.0 +MPL-2.0 Unlicense OR MIT Unlicense/MIT Zlib OR Apache-2.0 OR MIT diff --git a/crates/salsa/src/lib.rs b/crates/salsa/src/lib.rs index f86683ee77..5dde0d560f 100644 --- a/crates/salsa/src/lib.rs +++ b/crates/salsa/src/lib.rs @@ -443,7 +443,7 @@ pub trait QueryDb<'d>: Sized { /// Trait implements by all of the "special types" associated with /// each of your queries. pub trait Query: Debug + Default + Sized + for<'d> QueryDb<'d> { - /// Type that you you give as a parameter -- for queries with zero + /// Type that you give as a parameter -- for queries with zero /// or more than one input, this will be a tuple. type Key: Clone + Debug + Hash + Eq; diff --git a/crates/salsa/src/lru.rs b/crates/salsa/src/lru.rs index edad551842..f63f4c1e98 100644 --- a/crates/salsa/src/lru.rs +++ b/crates/salsa/src/lru.rs @@ -285,7 +285,7 @@ where impl Default for LruIndex { fn default() -> Self { - Self { index: AtomicUsize::new(std::usize::MAX) } + Self { index: AtomicUsize::new(usize::MAX) } } } @@ -299,11 +299,11 @@ impl LruIndex { } fn clear(&self) { - self.store(std::usize::MAX); + self.store(usize::MAX); } fn is_in_lru(&self) -> bool { - self.load() != std::usize::MAX + self.load() != usize::MAX } } diff --git a/crates/salsa/src/revision.rs b/crates/salsa/src/revision.rs index 204c0883b8..7f4c333fb1 100644 --- a/crates/salsa/src/revision.rs +++ b/crates/salsa/src/revision.rs @@ -60,7 +60,7 @@ impl AtomicRevision { /// Increment by 1, returning previous value. pub(crate) fn fetch_then_increment(&self) -> Revision { let v = self.data.fetch_add(1, Ordering::SeqCst); - assert!(v != u32::max_value(), "revision overflow"); + assert!(v != u32::MAX, "revision overflow"); Revision::from(v) } } diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index 6cca116335..99824df1f6 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -22,7 +22,7 @@ itertools.workspace = true [target.'cfg(windows)'.dependencies] miow = "0.6.0" -winapi = { version = "0.3.9", features = ["winerror"] } +windows-sys = { version = "0.52", features = ["Win32_Foundation"] } [features] # Uncomment to enable for the whole crate graph diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 0504ca50b8..54f10df42a 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -22,6 +22,10 @@ pub fn is_ci() -> bool { option_env!("CI").is_some() } +pub fn hash_once<Hasher: std::hash::Hasher + Default>(thing: impl std::hash::Hash) -> u64 { + std::hash::BuildHasher::hash_one(&std::hash::BuildHasherDefault::<Hasher>::default(), thing) +} + #[must_use] #[allow(clippy::print_stderr)] pub fn timeit(label: &'static str) -> impl Drop { diff --git a/crates/stdx/src/process.rs b/crates/stdx/src/process.rs index e6935f06b2..c54d850d7b 100644 --- a/crates/stdx/src/process.rs +++ b/crates/stdx/src/process.rs @@ -162,7 +162,7 @@ mod imp { pipe::NamedPipe, Overlapped, }; - use winapi::shared::winerror::ERROR_BROKEN_PIPE; + use windows_sys::Win32::Foundation::ERROR_BROKEN_PIPE; struct Pipe<'a> { dst: &'a mut Vec<u8>, diff --git a/crates/syntax/fuzz/Cargo.toml b/crates/syntax/fuzz/Cargo.toml index a235e3e17c..c2c6dac72d 100644 --- a/crates/syntax/fuzz/Cargo.toml +++ b/crates/syntax/fuzz/Cargo.toml @@ -3,7 +3,7 @@ name = "syntax-fuzz" version = "0.0.1" publish = false edition = "2021" -rust-version = "1.76" +rust-version = "1.78" [package.metadata] cargo-fuzz = true diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index e1765b25fd..8c772b9c7a 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -314,7 +314,9 @@ Attr = '#' '!'? '[' Meta ']' Meta = - Path ('=' Expr | TokenTree)? + 'unsafe' '(' Path ('=' Expr | TokenTree)? ')' +| Path ('=' Expr | TokenTree)? + //****************************// // Statements and Expressions // diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index c82bc4151a..98186c5473 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -778,7 +778,10 @@ impl Meta { pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } pub fn path(&self) -> Option<Path> { support::child(&self.syntax) } pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) } + pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } + pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 7cd1f1550b..16599881d6 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -6,7 +6,7 @@ use std::{ }; use rustc_lexer::unescape::{ - unescape_byte, unescape_char, unescape_mixed, unescape_unicode, MixedUnit, Mode, + unescape_byte, unescape_char, unescape_mixed, unescape_unicode, EscapeError, MixedUnit, Mode, }; use crate::{ @@ -180,10 +180,7 @@ pub trait IsString: AstToken { fn close_quote_text_range(&self) -> Option<TextRange> { self.quote_offsets().map(|it| it.quotes.1) } - fn escaped_char_ranges( - &self, - cb: &mut dyn FnMut(TextRange, Result<char, rustc_lexer::unescape::EscapeError>), - ) { + fn escaped_char_ranges(&self, cb: &mut dyn FnMut(TextRange, Result<char, EscapeError>)) { let text_range_no_quotes = match self.text_range_between_quotes() { Some(it) => it, None => return, @@ -212,20 +209,17 @@ impl IsString for ast::String { } impl ast::String { - pub fn value(&self) -> Option<Cow<'_, str>> { + pub fn value(&self) -> Result<Cow<'_, str>, EscapeError> { + let text = self.text(); + let text_range = self.text_range_between_quotes().ok_or(EscapeError::LoneSlash)?; + let text = &text[text_range - self.syntax().text_range().start()]; if self.is_raw() { - let text = self.text(); - let text = - &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; - return Some(Cow::Borrowed(text)); + return Ok(Cow::Borrowed(text)); } - let text = self.text(); - let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; - let mut buf = String::new(); let mut prev_end = 0; - let mut has_error = false; + let mut has_error = None; unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match ( unescaped_char, buf.capacity() == 0, @@ -239,13 +233,13 @@ impl ast::String { buf.push_str(&text[..prev_end]); buf.push(c); } - (Err(_), _) => has_error = true, + (Err(e), _) => has_error = Some(e), }); match (has_error, buf.capacity() == 0) { - (true, _) => None, - (false, true) => Some(Cow::Borrowed(text)), - (false, false) => Some(Cow::Owned(buf)), + (Some(e), _) => Err(e), + (None, true) => Ok(Cow::Borrowed(text)), + (None, false) => Ok(Cow::Owned(buf)), } } } @@ -256,20 +250,17 @@ impl IsString for ast::ByteString { } impl ast::ByteString { - pub fn value(&self) -> Option<Cow<'_, [u8]>> { + pub fn value(&self) -> Result<Cow<'_, [u8]>, EscapeError> { + let text = self.text(); + let text_range = self.text_range_between_quotes().ok_or(EscapeError::LoneSlash)?; + let text = &text[text_range - self.syntax().text_range().start()]; if self.is_raw() { - let text = self.text(); - let text = - &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; - return Some(Cow::Borrowed(text.as_bytes())); + return Ok(Cow::Borrowed(text.as_bytes())); } - let text = self.text(); - let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; - let mut buf: Vec<u8> = Vec::new(); let mut prev_end = 0; - let mut has_error = false; + let mut has_error = None; unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match ( unescaped_char, buf.capacity() == 0, @@ -283,13 +274,13 @@ impl ast::ByteString { buf.extend_from_slice(text[..prev_end].as_bytes()); buf.push(c as u8); } - (Err(_), _) => has_error = true, + (Err(e), _) => has_error = Some(e), }); match (has_error, buf.capacity() == 0) { - (true, _) => None, - (false, true) => Some(Cow::Borrowed(text.as_bytes())), - (false, false) => Some(Cow::Owned(buf)), + (Some(e), _) => Err(e), + (None, true) => Ok(Cow::Borrowed(text.as_bytes())), + (None, false) => Ok(Cow::Owned(buf)), } } } @@ -298,10 +289,7 @@ impl IsString for ast::CString { const RAW_PREFIX: &'static str = "cr"; const MODE: Mode = Mode::CStr; - fn escaped_char_ranges( - &self, - cb: &mut dyn FnMut(TextRange, Result<char, rustc_lexer::unescape::EscapeError>), - ) { + fn escaped_char_ranges(&self, cb: &mut dyn FnMut(TextRange, Result<char, EscapeError>)) { let text_range_no_quotes = match self.text_range_between_quotes() { Some(it) => it, None => return, @@ -322,20 +310,17 @@ impl IsString for ast::CString { } impl ast::CString { - pub fn value(&self) -> Option<Cow<'_, [u8]>> { + pub fn value(&self) -> Result<Cow<'_, [u8]>, EscapeError> { + let text = self.text(); + let text_range = self.text_range_between_quotes().ok_or(EscapeError::LoneSlash)?; + let text = &text[text_range - self.syntax().text_range().start()]; if self.is_raw() { - let text = self.text(); - let text = - &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; - return Some(Cow::Borrowed(text.as_bytes())); + return Ok(Cow::Borrowed(text.as_bytes())); } - let text = self.text(); - let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; - let mut buf = Vec::new(); let mut prev_end = 0; - let mut has_error = false; + let mut has_error = None; let extend_unit = |buf: &mut Vec<u8>, unit: MixedUnit| match unit { MixedUnit::Char(c) => buf.extend(c.encode_utf8(&mut [0; 4]).as_bytes()), MixedUnit::HighByte(b) => buf.push(b), @@ -353,13 +338,13 @@ impl ast::CString { buf.extend(text[..prev_end].as_bytes()); extend_unit(&mut buf, u); } - (Err(_), _) => has_error = true, + (Err(e), _) => has_error = Some(e), }); match (has_error, buf.capacity() == 0) { - (true, _) => None, - (false, true) => Some(Cow::Borrowed(text.as_bytes())), - (false, false) => Some(Cow::Owned(buf)), + (Some(e), _) => Err(e), + (None, true) => Ok(Cow::Borrowed(text.as_bytes())), + (None, false) => Ok(Cow::Owned(buf)), } } } @@ -478,34 +463,34 @@ impl Radix { } impl ast::Char { - pub fn value(&self) -> Option<char> { + pub fn value(&self) -> Result<char, EscapeError> { let mut text = self.text(); if text.starts_with('\'') { text = &text[1..]; } else { - return None; + return Err(EscapeError::ZeroChars); } if text.ends_with('\'') { text = &text[0..text.len() - 1]; } - unescape_char(text).ok() + unescape_char(text) } } impl ast::Byte { - pub fn value(&self) -> Option<u8> { + pub fn value(&self) -> Result<u8, EscapeError> { let mut text = self.text(); if text.starts_with("b\'") { text = &text[2..]; } else { - return None; + return Err(EscapeError::ZeroChars); } if text.ends_with('\'') { text = &text[0..text.len() - 1]; } - unescape_byte(text).ok() + unescape_byte(text) } } @@ -559,7 +544,10 @@ mod tests { fn check_string_value<'a>(lit: &str, expected: impl Into<Option<&'a str>>) { assert_eq!( - ast::String { syntax: make::tokens::literal(&format!("\"{lit}\"")) }.value().as_deref(), + ast::String { syntax: make::tokens::literal(&format!("\"{lit}\"")) } + .value() + .as_deref() + .ok(), expected.into() ); } @@ -584,7 +572,8 @@ bcde", "abcde", assert_eq!( ast::ByteString { syntax: make::tokens::literal(&format!("b\"{lit}\"")) } .value() - .as_deref(), + .as_deref() + .ok(), expected.into().map(|value| &value[..]) ); } diff --git a/crates/syntax/src/validation/block.rs b/crates/syntax/src/validation/block.rs index 8eb4a10a3f..fe3d61bef1 100644 --- a/crates/syntax/src/validation/block.rs +++ b/crates/syntax/src/validation/block.rs @@ -9,7 +9,7 @@ use crate::{ pub(crate) fn validate_block_expr(block: ast::BlockExpr, errors: &mut Vec<SyntaxError>) { if let Some(parent) = block.syntax().parent() { match parent.kind() { - FN | EXPR_STMT | STMT_LIST => return, + FN | EXPR_STMT | STMT_LIST | MACRO_STMTS => return, _ => {} } } diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs index 89ed6a6157..be9961120d 100644 --- a/crates/test-fixture/src/lib.rs +++ b/crates/test-fixture/src/lib.rs @@ -240,7 +240,12 @@ impl ChangeFixture { crate_graph .add_dep( from_id, - Dependency::with_prelude(CrateName::new(&to).unwrap(), to_id, prelude), + Dependency::with_prelude( + CrateName::new(&to).unwrap(), + to_id, + prelude, + false, + ), ) .unwrap(); } @@ -275,7 +280,15 @@ impl ChangeFixture { for krate in all_crates { crate_graph - .add_dep(krate, Dependency::new(CrateName::new("core").unwrap(), core_crate)) + .add_dep( + krate, + Dependency::with_prelude( + CrateName::new("core").unwrap(), + core_crate, + true, + true, + ), + ) .unwrap(); } } diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index f125792d12..0257ed9ab4 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -28,7 +28,7 @@ //! env: option //! eq: sized //! error: fmt -//! fmt: option, result, transmute, coerce_unsized +//! fmt: option, result, transmute, coerce_unsized, copy, clone, derive //! fn: //! from: sized //! future: pin @@ -913,11 +913,13 @@ pub mod fmt { } mod rt { + use super::*; extern "C" { type Opaque; } + #[derive(Copy, Clone)] #[lang = "format_argument"] pub struct Argument<'a> { value: &'a Opaque, @@ -930,8 +932,8 @@ pub mod fmt { unsafe { Argument { formatter: transmute(f), value: transmute(x) } } } - pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'_> { - Self::new(x, Display::fmt) + pub fn new_display<'b, T: crate::fmt::Display>(x: &'b T) -> Argument<'_> { + Self::new(x, crate::fmt::Display::fmt) } } @@ -968,7 +970,9 @@ pub mod fmt { flags: u32, precision: Count, width: Count, - ) -> Self; + ) -> Self { + Placeholder { position, fill, align, flags, precision, width } + } } #[lang = "format_unsafe_arg"] @@ -977,10 +981,13 @@ pub mod fmt { } impl UnsafeArg { - pub unsafe fn new() -> Self; + pub unsafe fn new() -> Self { + UnsafeArg { _private: () } + } } } + #[derive(Copy, Clone)] #[lang = "format_arguments"] pub struct Arguments<'a> { pieces: &'a [&'static str], @@ -1005,6 +1012,14 @@ pub mod fmt { ) -> Arguments<'a> { Arguments { pieces, fmt: Some(fmt), args } } + + pub const fn as_str(&self) -> Option<&'static str> { + match (self.pieces, self.args) { + ([], []) => Some(""), + ([s], []) => Some(s), + _ => None, + } + } } // region:derive @@ -1154,8 +1169,8 @@ pub mod pin { pointer: P, } impl<P> Pin<P> { - pub fn new(_pointer: P) -> Pin<P> { - loop {} + pub fn new(pointer: P) -> Pin<P> { + Pin { pointer } } } // region:deref @@ -1356,18 +1371,48 @@ pub mod iter { // region:panic mod panic { pub macro panic_2021 { - () => ( - $crate::panicking::panic("explicit panic") - ), - ($($t:tt)+) => ( - $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)) - ), + () => ({ + const fn panic_cold_explicit() -> ! { + $crate::panicking::panic_explicit() + } + panic_cold_explicit(); + }), + // Special-case the single-argument case for const_panic. + ("{}", $arg:expr $(,)?) => ({ + #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval + #[rustc_do_not_const_check] // hooked by const-eval + const fn panic_cold_display<T: $crate::fmt::Display>(arg: &T) -> ! { + $crate::panicking::panic_display(arg) + } + panic_cold_display(&$arg); + }), + ($($t:tt)+) => ({ + // Semicolon to prevent temporaries inside the formatting machinery from + // being considered alive in the caller after the panic_fmt call. + $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)); + }), } } mod panicking { - #[lang = "panic_fmt"] - pub const fn panic_fmt(_fmt: crate::fmt::Arguments<'_>) -> ! { + #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval + pub const fn panic_display<T: crate::fmt::Display>(x: &T) -> ! { + panic_fmt(crate::format_args!("{}", *x)); + } + + // This function is used instead of panic_fmt in const eval. + #[lang = "const_panic_fmt"] + pub const fn const_panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! { + if let Some(msg) = fmt.as_str() { + // The panic_display function is hooked by const eval. + panic_display(&msg); + } else { + loop {} + } + } + + #[lang = "panic_fmt"] // needed for const-evaluated panics + pub const fn panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! { loop {} } @@ -1378,6 +1423,7 @@ mod panicking { } // endregion:panic +#[macro_use] mod macros { // region:panic #[macro_export] @@ -1470,7 +1516,6 @@ mod macros { } // endregion:unimplemented - // region:derive pub(crate) mod builtin { #[rustc_builtin_macro] diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index 45bb777d4d..1dbccab370 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -222,7 +222,7 @@ impl NotifyActor { let depth = entry.depth(); let is_dir = entry.file_type().is_dir(); let is_file = entry.file_type().is_file(); - let abs_path = AbsPathBuf::try_from(entry.into_path()).unwrap(); + let abs_path = AbsPathBuf::try_from(entry.into_path()).ok()?; if depth < 2 && is_dir { self.send(make_message(abs_path.clone())); } diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml index c88f346655..84f2110eba 100644 --- a/crates/vfs/Cargo.toml +++ b/crates/vfs/Cargo.toml @@ -13,6 +13,7 @@ doctest = false [dependencies] rustc-hash.workspace = true +tracing.workspace = true fst = "0.4.7" indexmap.workspace = true nohash-hasher.workspace = true @@ -21,4 +22,4 @@ paths.workspace = true stdx.workspace = true [lints] -workspace = true
\ No newline at end of file +workspace = true diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index ddd6bbe3f7..b07e97cd6c 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -46,7 +46,7 @@ pub mod loader; mod path_interner; mod vfs_path; -use std::{fmt, mem}; +use std::{fmt, hash::BuildHasherDefault, mem}; use crate::path_interner::PathInterner; @@ -54,8 +54,13 @@ pub use crate::{ anchored_path::{AnchoredPath, AnchoredPathBuf}, vfs_path::VfsPath, }; +use indexmap::{map::Entry, IndexMap}; pub use paths::{AbsPath, AbsPathBuf}; +use rustc_hash::FxHasher; +use stdx::hash_once; +use tracing::{span, Level}; + /// Handle to a file in [`Vfs`] /// /// Most functions in rust-analyzer use this when they need to refer to a file. @@ -91,20 +96,13 @@ impl nohash_hasher::IsEnabled for FileId {} pub struct Vfs { interner: PathInterner, data: Vec<FileState>, - // FIXME: This should be a HashMap<FileId, ChangeFile> - // right now we do a nasty deduplication in GlobalState::process_changes that would be a lot - // easier to handle here on insertion. - changes: Vec<ChangedFile>, - // The above FIXME would then also get rid of this probably - created_this_cycle: Vec<FileId>, + changes: IndexMap<FileId, ChangedFile, BuildHasherDefault<FxHasher>>, } #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] pub enum FileState { - /// The file has been created this cycle. - Created, - /// The file exists. - Exists, + /// The file exists with the given content hash. + Exists(u64), /// The file is deleted. Deleted, } @@ -127,23 +125,23 @@ impl ChangedFile { /// Returns `true` if the change is [`Create`](ChangeKind::Create) or /// [`Delete`](Change::Delete). pub fn is_created_or_deleted(&self) -> bool { - matches!(self.change, Change::Create(_) | Change::Delete) + matches!(self.change, Change::Create(_, _) | Change::Delete) } /// Returns `true` if the change is [`Create`](ChangeKind::Create). pub fn is_created(&self) -> bool { - matches!(self.change, Change::Create(_)) + matches!(self.change, Change::Create(_, _)) } /// Returns `true` if the change is [`Modify`](ChangeKind::Modify). pub fn is_modified(&self) -> bool { - matches!(self.change, Change::Modify(_)) + matches!(self.change, Change::Modify(_, _)) } pub fn kind(&self) -> ChangeKind { match self.change { - Change::Create(_) => ChangeKind::Create, - Change::Modify(_) => ChangeKind::Modify, + Change::Create(_, _) => ChangeKind::Create, + Change::Modify(_, _) => ChangeKind::Modify, Change::Delete => ChangeKind::Delete, } } @@ -153,9 +151,9 @@ impl ChangedFile { #[derive(Eq, PartialEq, Debug)] pub enum Change { /// The file was (re-)created - Create(Vec<u8>), + Create(Vec<u8>, u64), /// The file was modified - Modify(Vec<u8>), + Modify(Vec<u8>, u64), /// The file was deleted Delete, } @@ -174,9 +172,7 @@ pub enum ChangeKind { impl Vfs { /// Id of the given path if it exists in the `Vfs` and is not deleted. pub fn file_id(&self, path: &VfsPath) -> Option<FileId> { - self.interner - .get(path) - .filter(|&it| matches!(self.get(it), FileState::Exists | FileState::Created)) + self.interner.get(path).filter(|&it| matches!(self.get(it), FileState::Exists(_))) } /// File path corresponding to the given `file_id`. @@ -194,9 +190,7 @@ impl Vfs { pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ { (0..self.data.len()) .map(|it| FileId(it as u32)) - .filter(move |&file_id| { - matches!(self.get(file_id), FileState::Exists | FileState::Created) - }) + .filter(move |&file_id| matches!(self.get(file_id), FileState::Exists(_))) .map(move |file_id| { let path = self.interner.lookup(file_id); (file_id, path) @@ -210,44 +204,79 @@ impl Vfs { /// If the path does not currently exists in the `Vfs`, allocates a new /// [`FileId`] for it. pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool { + let _p = span!(Level::INFO, "Vfs::set_file_contents").entered(); let file_id = self.alloc_file_id(path); let state = self.get(file_id); let change_kind = match (state, contents) { (FileState::Deleted, None) => return false, - (FileState::Deleted, Some(v)) => Change::Create(v), - (FileState::Exists | FileState::Created, None) => Change::Delete, - (FileState::Exists | FileState::Created, Some(v)) => Change::Modify(v), - }; - self.data[file_id.0 as usize] = match change_kind { - Change::Create(_) => { - self.created_this_cycle.push(file_id); - FileState::Created + (FileState::Deleted, Some(v)) => { + let hash = hash_once::<FxHasher>(&*v); + Change::Create(v, hash) + } + (FileState::Exists(_), None) => Change::Delete, + (FileState::Exists(hash), Some(v)) => { + let new_hash = hash_once::<FxHasher>(&*v); + if new_hash == hash { + return false; + } + Change::Modify(v, new_hash) } - // If the file got created this cycle, make sure we keep it that way even - // if a modify comes in - Change::Modify(_) if matches!(state, FileState::Created) => FileState::Created, - Change::Modify(_) => FileState::Exists, - Change::Delete => FileState::Deleted, }; + + let mut set_data = |change_kind| { + self.data[file_id.0 as usize] = match change_kind { + &Change::Create(_, hash) | &Change::Modify(_, hash) => FileState::Exists(hash), + Change::Delete => FileState::Deleted, + }; + }; + let changed_file = ChangedFile { file_id, change: change_kind }; - self.changes.push(changed_file); + match self.changes.entry(file_id) { + // two changes to the same file in one cycle, merge them appropriately + Entry::Occupied(mut o) => { + use Change::*; + + match (&mut o.get_mut().change, changed_file.change) { + // newer `Delete` wins + (change, Delete) => *change = Delete, + // merge `Create` with `Create` or `Modify` + (Create(prev, old_hash), Create(new, new_hash) | Modify(new, new_hash)) => { + *prev = new; + *old_hash = new_hash; + } + // collapse identical `Modify`es + (Modify(prev, old_hash), Modify(new, new_hash)) => { + *prev = new; + *old_hash = new_hash; + } + // equivalent to `Modify` + (change @ Delete, Create(new, new_hash)) => { + *change = Modify(new, new_hash); + } + // shouldn't occur, but collapse into `Create` + (change @ Delete, Modify(new, new_hash)) => { + stdx::never!(); + *change = Create(new, new_hash); + } + // shouldn't occur, but keep the Create + (prev @ Modify(_, _), new @ Create(_, _)) => *prev = new, + } + set_data(&o.get().change); + } + Entry::Vacant(v) => set_data(&v.insert(changed_file).change), + }; + true } /// Drain and returns all the changes in the `Vfs`. - pub fn take_changes(&mut self) -> Vec<ChangedFile> { - for file_id in self.created_this_cycle.drain(..) { - if self.data[file_id.0 as usize] == FileState::Created { - // downgrade the file from `Created` to `Exists` as the cycle is done - self.data[file_id.0 as usize] = FileState::Exists; - } - } + pub fn take_changes(&mut self) -> IndexMap<FileId, ChangedFile, BuildHasherDefault<FxHasher>> { mem::take(&mut self.changes) } /// Provides a panic-less way to verify file_id validity. pub fn exists(&self, file_id: FileId) -> bool { - matches!(self.get(file_id), FileState::Exists | FileState::Created) + matches!(self.get(file_id), FileState::Exists(_)) } /// Returns the id associated with `path` diff --git a/docs/dev/README.md b/docs/dev/README.md index cdab6b0992..8897f02e27 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -229,12 +229,22 @@ Release steps: * publishes the VS Code extension to the marketplace * call the GitHub API for PR details * create a new changelog in `rust-analyzer.github.io` -3. While the release is in progress, fill in the changelog -4. Commit & push the changelog +3. While the release is in progress, fill in the changelog. +4. Commit & push the changelog. 5. Run `cargo xtask publish-release-notes <CHANGELOG>` -- this will convert the changelog entry in AsciiDoc to Markdown and update the body of GitHub Releases entry. -6. Tweet -7. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's subtree. - Self-approve the PR. +6. Tweet. +7. Make a new branch and run `cargo xtask rustc-pull`, open a PR, and merge it. + This will pull any changes from `rust-lang/rust` into `rust-analyzer`. +8. Switch to `master`, pull, then run `cargo xtask rustc-push --rust-path ../rust-rust-analyzer --rust-fork matklad/rust`. + Replace `matklad/rust` with your own fork of `rust-lang/rust`. + You can use the token to authenticate when you get prompted for a password, since `josh` will push over HTTPS, not SSH. + This will push the `rust-analyzer` changes to your fork. + You can then open a PR against `rust-lang/rust`. + +Note: besides the `rust-rust-analyzer` clone, the Josh cache (stored under `~/.cache/rust-analyzer-josh`) will contain a bare clone of `rust-lang/rust`. +This currently takes about 3.5 GB. + +This [HackMD](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg) has details about how `josh` syncs work. If the GitHub Actions release fails because of a transient problem like a timeout, you can re-run the job from the Actions console. If it fails because of something that needs to be fixed, remove the release tag (if needed), fix the problem, then start over. diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index f1815082e2..46c1ccb79b 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ <!--- -lsp/ext.rs hash: dd51139b0530147e +lsp/ext.rs hash: a39009c351009d16 If you need to change the above hash to make the test pass, please check if you need to adjust this doc as well and ping this issue: diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index a03ab0031d..8993a46d2b 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -9,6 +9,11 @@ for enum variants. -- Placeholder expression to use for missing expressions in assists. -- +[[rust-analyzer.assist.termSearch.fuel]]rust-analyzer.assist.termSearch.fuel (default: `400`):: ++ +-- +Term search fuel in "units of work" for assists (Defaults to 400). +-- [[rust-analyzer.cachePriming.enable]]rust-analyzer.cachePriming.enable (default: `true`):: + -- @@ -256,7 +261,7 @@ by changing `#rust-analyzer.check.invocationStrategy#` and If `$saved_file` is part of the command, rust-analyzer will pass the absolute path of the saved file to the provided command. This is intended to be used with non-Cargo build systems. -Note that `$saved_file` is experimental and may be removed in the futureg. +Note that `$saved_file` is experimental and may be removed in the future. An example command would be: @@ -373,6 +378,11 @@ Custom completion snippets. -- Whether to enable term search based snippets like `Some(foo.bar().baz())`. -- +[[rust-analyzer.completion.termSearch.fuel]]rust-analyzer.completion.termSearch.fuel (default: `200`):: ++ +-- +Term search fuel in "units of work" for autocompletion (Defaults to 200). +-- [[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`):: + -- @@ -529,10 +539,15 @@ How to render the offset information in a memory layout hover. -- How to render the size information in a memory layout hover. -- -[[rust-analyzer.hover.show.structFields]]rust-analyzer.hover.show.structFields (default: `null`):: +[[rust-analyzer.hover.show.enumVariants]]rust-analyzer.hover.show.enumVariants (default: `5`):: ++ +-- +How many variants of an enum to display when hovering on. Show none if empty. +-- +[[rust-analyzer.hover.show.fields]]rust-analyzer.hover.show.fields (default: `5`):: + -- -How many fields of a struct to display when hovering a struct. +How many fields of a struct, variant or union to display when hovering on. Show none if empty. -- [[rust-analyzer.hover.show.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`):: + @@ -778,7 +793,8 @@ Disable project auto-discovery in favor of explicitly specified set of projects. Elements must be paths pointing to `Cargo.toml`, -`rust-project.json`, or JSON objects in `rust-project.json` format. +`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON +objects in `rust-project.json` format. -- [[rust-analyzer.lru.capacity]]rust-analyzer.lru.capacity (default: `null`):: + diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 6521b6d8b3..8e6c53d0c5 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -376,7 +376,7 @@ If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attac ---- lspconfig.rust_analyzer.setup({ on_attach = function(client, bufnr) - vim.lsp.inlay_hint.enable(bufnr) + vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) end }) ---- @@ -787,8 +787,6 @@ There are three ways to feed `rust-project.json` to rust-analyzer: Relative paths are interpreted relative to `rust-project.json` file location or (for inline JSON) relative to `rootUri`. -See https://github.com/rust-analyzer/rust-project.json-example for a small example. - You can set the `RA_LOG` environment variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading. Note that calls to `cargo check` are disabled when using `rust-project.json` by default, so compilation errors and warnings will no longer be sent to your LSP client. diff --git a/editors/code/package.json b/editors/code/package.json index 389e1b8742..6e4fedd992 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -534,6 +534,12 @@ "Fill missing expressions with reasonable defaults, `new` or `default` constructors." ] }, + "rust-analyzer.assist.termSearch.fuel": { + "markdownDescription": "Term search fuel in \"units of work\" for assists (Defaults to 400).", + "default": 400, + "type": "integer", + "minimum": 0 + }, "rust-analyzer.cachePriming.enable": { "markdownDescription": "Warm up caches on project load.", "default": true, @@ -798,7 +804,7 @@ ] }, "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#` and\n`#rust-analyzer.check.invocationLocation#`.\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 futureg.\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#` and\n`#rust-analyzer.check.invocationLocation#`.\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.", "default": null, "type": [ "null", @@ -930,6 +936,12 @@ "default": false, "type": "boolean" }, + "rust-analyzer.completion.termSearch.fuel": { + "markdownDescription": "Term search fuel in \"units of work\" for autocompletion (Defaults to 200).", + "default": 200, + "type": "integer", + "minimum": 0 + }, "rust-analyzer.diagnostics.disabled": { "markdownDescription": "List of rust-analyzer diagnostics to disable.", "default": [], @@ -1145,9 +1157,18 @@ } ] }, - "rust-analyzer.hover.show.structFields": { - "markdownDescription": "How many fields of a struct to display when hovering a struct.", - "default": null, + "rust-analyzer.hover.show.enumVariants": { + "markdownDescription": "How many variants of an enum to display when hovering on. Show none if empty.", + "default": 5, + "type": [ + "null", + "integer" + ], + "minimum": 0 + }, + "rust-analyzer.hover.show.fields": { + "markdownDescription": "How many fields of a struct, variant or union to display when hovering on. Show none if empty.", + "default": 5, "type": [ "null", "integer" @@ -1495,7 +1516,7 @@ "type": "boolean" }, "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`, or JSON objects in `rust-project.json` format.", + "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.", "default": [], "type": "array", "items": { diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index f76dec2629..5b683dccdc 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -433,7 +433,6 @@ export class Ctx implements RustAnalyzerExtensionApi { statusBar.tooltip.isTrusted = true; switch (status.health) { case "ok": - statusBar.tooltip.appendText(status.message ?? "Ready"); statusBar.color = undefined; statusBar.backgroundColor = undefined; if (this.config.statusBarClickAction === "stopServer") { @@ -444,9 +443,6 @@ export class Ctx implements RustAnalyzerExtensionApi { this.dependencies?.refresh(); break; case "warning": - if (status.message) { - statusBar.tooltip.appendText(status.message); - } statusBar.color = new vscode.ThemeColor("statusBarItem.warningForeground"); statusBar.backgroundColor = new vscode.ThemeColor( "statusBarItem.warningBackground", @@ -455,9 +451,6 @@ export class Ctx implements RustAnalyzerExtensionApi { icon = "$(warning) "; break; case "error": - if (status.message) { - statusBar.tooltip.appendText(status.message); - } statusBar.color = new vscode.ThemeColor("statusBarItem.errorForeground"); statusBar.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground"); statusBar.command = "rust-analyzer.openLogs"; @@ -476,6 +469,15 @@ export class Ctx implements RustAnalyzerExtensionApi { statusBar.text = "$(stop-circle) rust-analyzer"; return; } + if (status.message) { + statusBar.tooltip.appendText(status.message); + } + if (status.workspaceInfo) { + if (statusBar.tooltip.value) { + statusBar.tooltip.appendMarkdown("\n\n---\n\n"); + } + statusBar.tooltip.appendMarkdown(status.workspaceInfo); + } if (statusBar.tooltip.value) { statusBar.tooltip.appendMarkdown("\n\n---\n\n"); } diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 9a7a4aae95..7d6b16b019 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -241,6 +241,7 @@ export type ServerStatusParams = { health: "ok" | "warning" | "error"; quiescent: boolean; message?: string; + workspaceInfo?: string; }; export type SsrParams = { query: string; diff --git a/lib/lsp-server/src/msg.rs b/lib/lsp-server/src/msg.rs index 71e5933689..53c64796f2 100644 --- a/lib/lsp-server/src/msg.rs +++ b/lib/lsp-server/src/msg.rs @@ -153,6 +153,14 @@ pub struct Notification { pub params: serde_json::Value, } +fn invalid_data(error: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, error) +} + +macro_rules! invalid_data { + ($($tt:tt)*) => (invalid_data(format!($($tt)*))) +} + impl Message { pub fn read(r: &mut impl BufRead) -> io::Result<Option<Message>> { Message::_read(r) @@ -162,7 +170,14 @@ impl Message { None => return Ok(None), Some(text) => text, }; - let msg = serde_json::from_str(&text)?; + + let msg = match serde_json::from_str(&text) { + Ok(msg) => msg, + Err(e) => { + return Err(invalid_data!("malformed LSP payload: {:?}", e)); + } + }; + Ok(Some(msg)) } pub fn write(self, w: &mut impl Write) -> io::Result<()> { @@ -240,13 +255,6 @@ impl Notification { } fn read_msg_text(inp: &mut dyn BufRead) -> io::Result<Option<String>> { - fn invalid_data(error: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error { - io::Error::new(io::ErrorKind::InvalidData, error) - } - macro_rules! invalid_data { - ($($tt:tt)*) => (invalid_data(format!($($tt)*))) - } - let mut size = None; let mut buf = String::new(); loop { @@ -264,12 +272,12 @@ fn read_msg_text(inp: &mut dyn BufRead) -> io::Result<Option<String>> { let mut parts = buf.splitn(2, ": "); let header_name = parts.next().unwrap(); let header_value = - parts.next().ok_or_else(|| invalid_data(format!("malformed header: {:?}", buf)))?; + parts.next().ok_or_else(|| invalid_data!("malformed header: {:?}", buf))?; if header_name.eq_ignore_ascii_case("Content-Length") { size = Some(header_value.parse::<usize>().map_err(invalid_data)?); } } - let size: usize = size.ok_or_else(|| invalid_data("no Content-Length".to_owned()))?; + let size: usize = size.ok_or_else(|| invalid_data!("no Content-Length"))?; let mut buf = buf.into_bytes(); buf.resize(size, 0); inp.read_exact(&mut buf)?; diff --git a/rust-version b/rust-version new file mode 100644 index 0000000000..207ef6c5de --- /dev/null +++ b/rust-version @@ -0,0 +1 @@ +6579ed89f0fcc26da71afdd11d30d63f6f812a0a diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index a83d32e414..192de86947 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -8,6 +8,7 @@ rust-version.workspace = true [dependencies] anyhow.workspace = true +directories = "5.0" flate2 = "1.0.24" write-json = "0.1.2" xshell.workspace = true diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs index 9066545920..dd7bfd0bda 100644 --- a/xtask/src/flags.rs +++ b/xtask/src/flags.rs @@ -14,16 +14,16 @@ xflags::xflags! { cmd install { /// Install only VS Code plugin. optional --client - /// One of 'code', 'code-exploration', 'code-insiders', 'codium', or 'code-oss'. + /// One of `code`, `code-exploration`, `code-insiders`, `codium`, or `code-oss`. optional --code-bin name: String /// Install only the language server. optional --server - /// Use mimalloc allocator for server + /// Use mimalloc allocator for server. optional --mimalloc - /// Use jemalloc allocator for server + /// Use jemalloc allocator for server. optional --jemalloc - /// build in release with debug info set to 2 + /// build in release with debug info set to 2. optional --dev-rel } @@ -32,9 +32,21 @@ xflags::xflags! { cmd release { optional --dry-run } - cmd promote { - optional --dry-run + + cmd rustc-pull { + /// rustc commit to pull. + optional --commit refspec: String + } + + cmd rustc-push { + /// rust local path, e.g. `../rust-rust-analyzer`. + required --rust-path rust_path: String + /// rust fork name, e.g. `matklad/rust`. + required --rust-fork rust_fork: String + /// branch name. + optional --branch branch: String } + cmd dist { /// Use mimalloc allocator for server optional --mimalloc @@ -77,7 +89,8 @@ pub enum XtaskCmd { Install(Install), FuzzTests(FuzzTests), Release(Release), - Promote(Promote), + RustcPull(RustcPull), + RustcPush(RustcPush), Dist(Dist), PublishReleaseNotes(PublishReleaseNotes), Metrics(Metrics), @@ -104,8 +117,15 @@ pub struct Release { } #[derive(Debug)] -pub struct Promote { - pub dry_run: bool, +pub struct RustcPull { + pub commit: Option<String>, +} + +#[derive(Debug)] +pub struct RustcPush { + pub rust_path: String, + pub rust_fork: String, + pub branch: Option<String>, } #[derive(Debug)] diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 9418675a34..e070576303 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -34,7 +34,8 @@ fn main() -> anyhow::Result<()> { flags::XtaskCmd::Install(cmd) => cmd.run(sh), flags::XtaskCmd::FuzzTests(_) => run_fuzzer(sh), flags::XtaskCmd::Release(cmd) => cmd.run(sh), - flags::XtaskCmd::Promote(cmd) => cmd.run(sh), + flags::XtaskCmd::RustcPull(cmd) => cmd.run(sh), + flags::XtaskCmd::RustcPush(cmd) => cmd.run(sh), flags::XtaskCmd::Dist(cmd) => cmd.run(sh), flags::XtaskCmd::PublishReleaseNotes(cmd) => cmd.run(sh), flags::XtaskCmd::Metrics(cmd) => cmd.run(sh), diff --git a/xtask/src/release.rs b/xtask/src/release.rs index 1a5e6dfb4c..5699053a23 100644 --- a/xtask/src/release.rs +++ b/xtask/src/release.rs @@ -1,5 +1,12 @@ mod changelog; +use std::process::{Command, Stdio}; +use std::thread; +use std::time::Duration; + +use anyhow::{bail, Context as _}; +use directories::ProjectDirs; +use stdx::JodChild; use xshell::{cmd, Shell}; use crate::{codegen, date_iso, flags, is_release_tag, project_root}; @@ -71,26 +78,167 @@ impl flags::Release { } } -impl flags::Promote { +// git sync implementation adapted from https://github.com/rust-lang/miri/blob/62039ac/miri-script/src/commands.rs +impl flags::RustcPull { pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { - let _dir = sh.push_dir("../rust-rust-analyzer"); - cmd!(sh, "git switch master").run()?; - cmd!(sh, "git fetch upstream").run()?; - cmd!(sh, "git reset --hard upstream/master").run()?; + sh.change_dir(project_root()); + let commit = self.commit.map(Result::Ok).unwrap_or_else(|| { + let rust_repo_head = + cmd!(sh, "git ls-remote https://github.com/rust-lang/rust/ HEAD").read()?; + rust_repo_head + .split_whitespace() + .next() + .map(|front| front.trim().to_owned()) + .ok_or_else(|| anyhow::format_err!("Could not obtain Rust repo HEAD from remote.")) + })?; + // Make sure the repo is clean. + if !cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty() { + bail!("working directory must be clean before running `cargo xtask pull`"); + } + // This should not add any new root commits. So count those before and after merging. + let num_roots = || -> anyhow::Result<u32> { + Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count") + .read() + .context("failed to determine the number of root commits")? + .parse::<u32>()?) + }; + let num_roots_before = num_roots()?; + // Make sure josh is running. + let josh = start_josh()?; - let date = date_iso(sh)?; - let branch = format!("rust-analyzer-{date}"); - cmd!(sh, "git switch -c {branch}").run()?; - cmd!(sh, "git subtree pull -m ':arrow_up: rust-analyzer' -P src/tools/rust-analyzer rust-analyzer release").run()?; + // Update rust-version file. As a separate commit, since making it part of + // the merge has confused the heck out of josh in the past. + // We pass `--no-verify` to avoid running any git hooks that might exist, + // in case they dirty the repository. + sh.write_file("rust-version", format!("{commit}\n"))?; + const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rust-lang/rust"; + cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}") + .run() + .context("FAILED to commit rust-version file, something went wrong")?; - if !self.dry_run { - cmd!(sh, "git push -u origin {branch}").run()?; - cmd!( - sh, - "xdg-open https://github.com/matklad/rust/pull/new/{branch}?body=r%3F%20%40ghost" - ) + // Fetch given rustc commit. + cmd!(sh, "git fetch http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git") + .run() + .map_err(|e| { + // Try to un-do the previous `git commit`, to leave the repo in the state we found it it. + cmd!(sh, "git reset --hard HEAD^") + .run() + .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); + e + }) + .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?; + + // Merge the fetched commit. + const MERGE_COMMIT_MESSAGE: &str = "Merge from rust-lang/rust"; + cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") + .run() + .context("FAILED to merge new commits, something went wrong")?; + + // Check that the number of roots did not increase. + if num_roots()? != num_roots_before { + bail!("Josh created a new root commit. This is probably not the history you want."); + } + + drop(josh); + Ok(()) + } +} + +impl flags::RustcPush { + pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { + let branch = self.branch.as_deref().unwrap_or("sync-from-ra"); + let rust_path = self.rust_path; + let rust_fork = self.rust_fork; + + sh.change_dir(project_root()); + let base = sh.read_file("rust-version")?.trim().to_owned(); + // Make sure the repo is clean. + if !cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty() { + bail!("working directory must be clean before running `cargo xtask push`"); + } + // Make sure josh is running. + let josh = start_josh()?; + + // Find a repo we can do our preparation in. + sh.change_dir(rust_path); + + // Prepare the branch. Pushing works much better if we use as base exactly + // the commit that we pulled from last time, so we use the `rust-version` + // file to find out which commit that would be. + println!("Preparing {rust_fork} (base: {base})..."); + if cmd!(sh, "git fetch https://github.com/{rust_fork} {branch}") + .ignore_stderr() + .read() + .is_ok() + { + bail!( + "The branch `{branch}` seems to already exist in `https://github.com/{rust_fork}`. Please delete it and try again." + ); + } + cmd!(sh, "git fetch https://github.com/rust-lang/rust {base}").run()?; + cmd!(sh, "git push https://github.com/{rust_fork} {base}:refs/heads/{branch}") + .ignore_stdout() + .ignore_stderr() // silence the "create GitHub PR" message .run()?; + println!(); + + // Do the actual push. + sh.change_dir(project_root()); + println!("Pushing rust-analyzer changes..."); + cmd!( + sh, + "git push http://localhost:{JOSH_PORT}/{rust_fork}.git{JOSH_FILTER}.git HEAD:{branch}" + ) + .run()?; + println!(); + + // Do a round-trip check to make sure the push worked as expected. + cmd!( + sh, + "git fetch http://localhost:{JOSH_PORT}/{rust_fork}.git{JOSH_FILTER}.git {branch}" + ) + .ignore_stderr() + .read()?; + let head = cmd!(sh, "git rev-parse HEAD").read()?; + let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?; + if head != fetch_head { + bail!("Josh created a non-roundtrip push! Do NOT merge this into rustc!"); } + println!("Confirmed that the push round-trips back to rust-analyzer properly. Please create a rustc PR:"); + // https://github.com/github-linguist/linguist/compare/master...octocat:linguist:master + let fork_path = rust_fork.replace('/', ":"); + println!( + " https://github.com/rust-lang/rust/compare/{fork_path}:{branch}?quick_pull=1&title=Subtree+update+of+rust-analyzer&body=r?+@ghost" + ); + + drop(josh); Ok(()) } } + +/// Used for rustc syncs. +const JOSH_FILTER: &str = + ":rev(55d9a533b309119c8acd13061581b43ae8840823:prefix=src/tools/rust-analyzer):/src/tools/rust-analyzer"; +const JOSH_PORT: &str = "42042"; + +fn start_josh() -> anyhow::Result<impl Drop> { + // Determine cache directory. + let local_dir = { + let user_dirs = ProjectDirs::from("org", "rust-lang", "rust-analyzer-josh").unwrap(); + user_dirs.cache_dir().to_owned() + }; + + // Start josh, silencing its output. + let mut cmd = Command::new("josh-proxy"); + cmd.arg("--local").arg(local_dir); + cmd.arg("--remote").arg("https://github.com"); + cmd.arg("--port").arg(JOSH_PORT); + cmd.arg("--no-background"); + cmd.stdout(Stdio::null()); + cmd.stderr(Stdio::null()); + let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?; + // Give it some time so hopefully the port is open. (100ms was not enough.) + thread::sleep(Duration::from_millis(200)); + + Ok(JodChild(josh)) +} |