diff options
| author | bors <bors@rust-lang.org> | 2023-09-18 16:16:30 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-09-18 16:16:30 +0000 |
| commit | b1575cb72ef40459666f802af8636faf8428e3eb (patch) | |
| tree | 72bff2177e2ec68f2f0527e4a825d8164c07b0ca | |
| parent | cbcf9a5368c0d8b6d0b5784201471475cb3496a3 (diff) | |
| parent | b18db7a13e52f71e94bdf221a7a013fd9ace4c7f (diff) | |
| download | rust-b1575cb72ef40459666f802af8636faf8428e3eb.tar.gz rust-b1575cb72ef40459666f802af8636faf8428e3eb.zip | |
Auto merge of #115927 - lnicola:sync-from-ra, r=lnicola
:arrow_up: `rust-analyzer` r? `@ghost`
195 files changed, 5763 insertions, 2740 deletions
diff --git a/src/tools/rust-analyzer/.cargo/config.toml b/src/tools/rust-analyzer/.cargo/config.toml index 24745d1c806..c9ad7803951 100644 --- a/src/tools/rust-analyzer/.cargo/config.toml +++ b/src/tools/rust-analyzer/.cargo/config.toml @@ -8,4 +8,4 @@ lint = "clippy --all-targets -- -Aclippy::collapsible_if -Aclippy::needless_pass linker = "rust-lld" [env] -CARGO_WORKSPACE_DIR = { value = "", relative = true } \ No newline at end of file +CARGO_WORKSPACE_DIR = { value = "", relative = true } diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 9f246098e76..fb7b4b07f98 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -86,12 +86,20 @@ jobs: - name: Test run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet + - name: Switch to stable toolchain + run: | + rustup update --no-self-update stable + rustup component add --toolchain stable rust-src + rustup default stable + - name: Run analysis-stats on rust-analyzer if: matrix.os == 'ubuntu-latest' run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats . - name: Run analysis-stats on rust std library if: matrix.os == 'ubuntu-latest' + env: + RUSTC_BOOTSTRAP: 1 run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std # Weird targets to catch non-portable code diff --git a/src/tools/rust-analyzer/.github/workflows/metrics.yaml b/src/tools/rust-analyzer/.github/workflows/metrics.yaml index 260e45ff517..bbeccd1621d 100644 --- a/src/tools/rust-analyzer/.github/workflows/metrics.yaml +++ b/src/tools/rust-analyzer/.github/workflows/metrics.yaml @@ -67,7 +67,7 @@ jobs: other_metrics: strategy: matrix: - names: [self, ripgrep, webrender, diesel] + 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] @@ -92,7 +92,7 @@ jobs: key: ${{ runner.os }}-target-${{ github.sha }} - name: Collect metrics - run: cargo xtask metrics ${{ matrix.names }} + run: cargo xtask metrics "${{ matrix.names }}" - name: Upload metrics uses: actions/upload-artifact@v3 @@ -118,25 +118,30 @@ jobs: with: name: self-${{ github.sha }} - - name: Download ripgrep metrics + - name: Download ripgrep-13.0.0 metrics uses: actions/download-artifact@v3 with: - name: ripgrep-${{ github.sha }} + name: ripgrep-13.0.0-${{ github.sha }} - - name: Download webrender metrics + - name: Download webrender-2022 metrics uses: actions/download-artifact@v3 with: - name: webrender-${{ github.sha }} + name: webrender-2022-${{ github.sha }} - - name: Download diesel metrics + - name: Download diesel-1.4.8 metrics uses: actions/download-artifact@v3 with: - name: diesel-${{ github.sha }} + name: diesel-1.4.8-${{ github.sha }} + + - name: Download hyper-0.14.18 metrics + uses: actions/download-artifact@v3 + with: + name: hyper-0.14.18-${{ github.sha }} - name: Combine json run: | git clone --depth 1 https://$METRICS_TOKEN@github.com/rust-analyzer/metrics.git - jq -s ".[0] * .[1] * .[2] * .[3] * .[4]" build.json self.json ripgrep.json webrender.json diesel.json -c >> metrics/metrics.json + jq -s ".[0] * .[1] * .[2] * .[3] * .[4] * .[5]" build.json self.json ripgrep-13.0.0.json webrender-2022.json diesel-1.4.8.json hyper-0.14.18.json -c >> metrics/metrics.json cd metrics git add . git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈 diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index 43681c785fd..b5dbe30c322 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -128,6 +128,8 @@ jobs: - name: Run analysis-stats on rust std library if: matrix.target == 'x86_64-unknown-linux-gnu' + env: + RUSTC_BOOTSTRAP: 1 run: target/${{ matrix.target }}/release/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std - name: Upload artifacts diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 49e96236c51..bd6554bf889 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -381,14 +381,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.19" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys 0.42.0", + "redox_syscall 0.3.5", + "windows-sys 0.48.0", ] [[package]] @@ -541,6 +541,7 @@ dependencies = [ "mbe", "once_cell", "profile", + "ra-ap-rustc_parse_format", "rustc-hash", "smallvec", "stdx", @@ -854,7 +855,6 @@ version = "0.0.0" dependencies = [ "dashmap", "hashbrown 0.12.3", - "once_cell", "rustc-hash", "triomphe", ] @@ -999,7 +999,7 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "lsp-server" -version = "0.7.3" +version = "0.7.4" dependencies = [ "crossbeam-channel", "log", @@ -1010,9 +1010,9 @@ dependencies = [ [[package]] name = "lsp-server" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72417faa455bfb4e5bf14b157d8e2ca2ed74b4e89b8cf42ea2d864825ae5c8a2" +checksum = "b52dccdf3302eefab8c8a1273047f0a3c3dca4b527c8458d00c09484c8371928" dependencies = [ "crossbeam-channel", "log", @@ -1149,20 +1149,21 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "notify" -version = "5.1.0" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.3.2", "crossbeam-channel", "filetime", "fsevent-sys", "inotify", "kqueue", "libc", + "log", "mio", "walkdir", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -1251,7 +1252,7 @@ dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] @@ -1264,7 +1265,7 @@ checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.42.0", ] @@ -1482,16 +1483,36 @@ dependencies = [ ] [[package]] +name = "ra-ap-rustc_index" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b5fa61d34da18e148dc3a81f654488ea07f40938d8aefb17f8b64bb78c6120" +dependencies = [ + "arrayvec", + "smallvec", +] + +[[package]] name = "ra-ap-rustc_lexer" -version = "0.1.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1c145702ed3f237918e512685185dc8a4d0edc3a5326c63d20361d8ba9b45b3" +checksum = "f2e2f6b48422e4eed5218277ab7cc9733e60dd8f3167f4f36a49a0cafe4dc195" dependencies = [ - "unic-emoji-char", + "unicode-properties", "unicode-xid", ] [[package]] +name = "ra-ap-rustc_parse_format" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7369ad01cc79f9e3513c9f6a6326f6b980100e4862a7ac71b9991c88108bb" +dependencies = [ + "ra-ap-rustc_index", + "ra-ap-rustc_lexer", +] + +[[package]] name = "rayon" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1523,6 +1544,15 @@ dependencies = [ ] [[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] name = "rowan" version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1545,7 +1575,6 @@ dependencies = [ "crossbeam-channel", "dissimilar", "expect-test", - "filetime", "flycheck", "hir", "hir-def", @@ -1555,7 +1584,7 @@ dependencies = [ "ide-ssr", "itertools", "load-cargo", - "lsp-server 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-server 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "mbe", "mimalloc", @@ -2057,47 +2086,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e5df347f0bf3ec1d670aad6ca5c6a1859cd9ea61d2113125794654ccced68f" [[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-emoji-char" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - -[[package]] name = "unicase" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2128,6 +2116,12 @@ dependencies = [ ] [[package]] +name = "unicode-properties" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0" + +[[package]] name = "unicode-segmentation" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 5eb59d6db11..cab88fc18ce 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -86,7 +86,7 @@ proc-macro-test = { path = "./crates/proc-macro-test" } # In-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.0-pre.1" } la-arena = { version = "0.3.1" } -lsp-server = { version = "0.7.3" } +lsp-server = { version = "0.7.4" } # non-local crates smallvec = { version = "1.10.0", features = [ @@ -97,11 +97,15 @@ smallvec = { version = "1.10.0", features = [ smol_str = "0.2.0" nohash-hasher = "0.2.0" text-size = "1.1.0" -# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde -serde = { version = "1.0.156, < 1.0.172", features = ["derive"] } +serde = { version = "1.0.156", features = ["derive"] } serde_json = "1.0.96" triomphe = { version = "0.1.8", default-features = false, features = ["std"] } # can't upgrade due to dashmap depending on 0.12.3 currently hashbrown = { version = "0.12.3", features = ["inline-more"], default-features = false } -rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" } +rustc_lexer = { version = "0.10.0", package = "ra-ap-rustc_lexer" } +rustc_parse_format = { version = "0.10.0", package = "ra-ap-rustc_parse_format", default-features = false } + +# Upstream broke this for us so we can't update it +rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } +rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } diff --git a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs index aaac0fc3790..3f5ccb621c7 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs @@ -179,8 +179,8 @@ impl ChangeFixture { meta.edition, Some(crate_name.clone().into()), version, - meta.cfg, - Default::default(), + meta.cfg.clone(), + Some(meta.cfg), meta.env, false, origin, @@ -200,7 +200,7 @@ impl ChangeFixture { } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { assert!(default_crate_root.is_none()); default_crate_root = Some(file_id); - default_cfg = meta.cfg; + default_cfg.extend(meta.cfg.into_iter()); default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned()))); default_target_data_layout = meta.target_data_layout; } @@ -220,8 +220,8 @@ impl ChangeFixture { Edition::CURRENT, Some(CrateName::new("test").unwrap().into()), None, - default_cfg, - Default::default(), + default_cfg.clone(), + Some(default_cfg), default_env, false, CrateOrigin::Local { repo: None, name: None }, diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 183b9b7d278..0aeb0b05052 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -86,6 +86,32 @@ impl CfgOptions { } } +impl Extend<CfgAtom> for CfgOptions { + fn extend<T: IntoIterator<Item = CfgAtom>>(&mut self, iter: T) { + iter.into_iter().for_each(|cfg_flag| _ = self.enabled.insert(cfg_flag)); + } +} + +impl IntoIterator for CfgOptions { + type Item = <FxHashSet<CfgAtom> as IntoIterator>::Item; + + type IntoIter = <FxHashSet<CfgAtom> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + <FxHashSet<CfgAtom> as IntoIterator>::into_iter(self.enabled) + } +} + +impl<'a> IntoIterator for &'a CfgOptions { + type Item = <&'a FxHashSet<CfgAtom> as IntoIterator>::Item; + + type IntoIter = <&'a FxHashSet<CfgAtom> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + <&FxHashSet<CfgAtom> as IntoIterator>::into_iter(&self.enabled) + } +} + #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CfgDiff { // Invariants: No duplicates, no atom that's both in `enable` and `disable`. diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs index fbb943ccb99..2de719af92c 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs @@ -5,7 +5,9 @@ #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] use std::{ + ffi::OsString, fmt, io, + path::PathBuf, process::{ChildStderr, ChildStdout, Command, Stdio}, time::Duration, }; @@ -168,7 +170,7 @@ struct FlycheckActor { /// doesn't provide a way to read sub-process output without blocking, so we /// have to wrap sub-processes output handling in a thread and pass messages /// back over a channel. - cargo_handle: Option<CargoHandle>, + command_handle: Option<CommandHandle>, } enum Event { @@ -184,7 +186,7 @@ impl FlycheckActor { workspace_root: AbsPathBuf, ) -> FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); - FlycheckActor { id, sender, config, root: workspace_root, cargo_handle: None } + FlycheckActor { id, sender, config, root: workspace_root, command_handle: None } } fn report_progress(&self, progress: Progress) { @@ -192,7 +194,7 @@ impl FlycheckActor { } fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> { - let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver); + let check_chan = self.command_handle.as_ref().map(|cargo| &cargo.receiver); if let Ok(msg) = inbox.try_recv() { // give restarts a preference so check outputs don't block a restart or stop return Some(Event::RequestStateChange(msg)); @@ -221,21 +223,19 @@ impl FlycheckActor { } let command = self.check_command(); + let formatted_command = format!("{:?}", command); + tracing::debug!(?command, "will restart flycheck"); - match CargoHandle::spawn(command) { - Ok(cargo_handle) => { - tracing::debug!( - command = ?self.check_command(), - "did restart flycheck" - ); - self.cargo_handle = Some(cargo_handle); + match CommandHandle::spawn(command) { + Ok(command_handle) => { + tracing::debug!(command = formatted_command, "did restart flycheck"); + self.command_handle = Some(command_handle); self.report_progress(Progress::DidStart); } Err(error) => { self.report_progress(Progress::DidFailToRestart(format!( - "Failed to run the following command: {:?} error={}", - self.check_command(), - error + "Failed to run the following command: {} error={}", + formatted_command, error ))); } } @@ -244,12 +244,14 @@ impl FlycheckActor { tracing::debug!(flycheck_id = self.id, "flycheck finished"); // Watcher finished - let cargo_handle = self.cargo_handle.take().unwrap(); - let res = cargo_handle.join(); + let command_handle = self.command_handle.take().unwrap(); + let formatted_handle = format!("{:?}", command_handle); + + let res = command_handle.join(); if res.is_err() { tracing::error!( - "Flycheck failed to run the following command: {:?}", - self.check_command() + "Flycheck failed to run the following command: {}", + formatted_handle ); } self.report_progress(Progress::DidFinish(res)); @@ -284,12 +286,12 @@ impl FlycheckActor { } fn cancel_check_process(&mut self) { - if let Some(cargo_handle) = self.cargo_handle.take() { + if let Some(command_handle) = self.command_handle.take() { tracing::debug!( - command = ?self.check_command(), + command = ?command_handle, "did cancel flycheck" ); - cargo_handle.cancel(); + command_handle.cancel(); self.report_progress(Progress::DidCancel); } } @@ -391,19 +393,36 @@ impl Drop for JodGroupChild { } /// A handle to a cargo process used for fly-checking. -struct CargoHandle { +struct CommandHandle { /// The handle to the actual cargo process. As we cannot cancel directly from with /// a read syscall dropping and therefore terminating the process is our best option. child: JodGroupChild, thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>, receiver: Receiver<CargoMessage>, + program: OsString, + arguments: Vec<OsString>, + current_dir: Option<PathBuf>, } -impl CargoHandle { - fn spawn(mut command: Command) -> std::io::Result<CargoHandle> { +impl fmt::Debug for CommandHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CommandHandle") + .field("program", &self.program) + .field("arguments", &self.arguments) + .field("current_dir", &self.current_dir) + .finish() + } +} + +impl CommandHandle { + fn spawn(mut command: Command) -> std::io::Result<CommandHandle> { command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); let mut child = command.group_spawn().map(JodGroupChild)?; + let program = command.get_program().into(); + let arguments = command.get_args().map(|arg| arg.into()).collect::<Vec<OsString>>(); + let current_dir = command.get_current_dir().map(|arg| arg.to_path_buf()); + let stdout = child.0.inner().stdout.take().unwrap(); let stderr = child.0.inner().stderr.take().unwrap(); @@ -413,7 +432,7 @@ impl CargoHandle { .name("CargoHandle".to_owned()) .spawn(move || actor.run()) .expect("failed to spawn thread"); - Ok(CargoHandle { child, thread, receiver }) + Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver }) } fn cancel(mut self) { diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 30307deb79b..8cf61ee04d4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -31,8 +31,10 @@ smallvec.workspace = true hashbrown.workspace = true triomphe.workspace = true -rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } -rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } +rustc_abi.workspace = true +rustc_index.workspace = true +rustc_parse_format.workspace = true + # local deps stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index a5db75a91eb..c6454eb9ea0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -5,7 +5,7 @@ pub mod builtin; #[cfg(test)] mod tests; -use std::{hash::Hash, ops}; +use std::{hash::Hash, ops, slice::Iter as SliceIter}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; @@ -14,12 +14,11 @@ use hir_expand::{ attrs::{collect_attrs, Attr, AttrId, RawAttrs}, HirFileId, InFile, }; -use itertools::Itertools; use la_arena::{ArenaMap, Idx, RawIdx}; use mbe::DelimiterKind; use syntax::{ - ast::{self, HasAttrs, IsString}, - AstPtr, AstToken, SmolStr, TextRange, TextSize, + ast::{self, HasAttrs}, + AstPtr, SmolStr, }; use triomphe::Arc; @@ -33,26 +32,6 @@ use crate::{ LocalFieldId, Lookup, MacroId, VariantId, }; -/// Holds documentation -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Documentation(String); - -impl Documentation { - pub fn new(s: String) -> Self { - Documentation(s) - } - - pub fn as_str(&self) -> &str { - &self.0 - } -} - -impl From<Documentation> for String { - fn from(Documentation(string): Documentation) -> Self { - string - } -} - #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct Attrs(RawAttrs); @@ -221,33 +200,6 @@ impl Attrs { self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it)) } - pub fn docs(&self) -> Option<Documentation> { - let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value()); - let indent = doc_indent(self); - let mut buf = String::new(); - for doc in docs { - // str::lines doesn't yield anything for the empty string - if !doc.is_empty() { - buf.extend(Itertools::intersperse( - doc.lines().map(|line| { - line.char_indices() - .nth(indent) - .map_or(line, |(offset, _)| &line[offset..]) - .trim_end() - }), - "\n", - )); - } - buf.push('\n'); - } - buf.pop(); - if buf.is_empty() { - None - } else { - Some(Documentation(buf)) - } - } - pub fn has_doc_hidden(&self) -> bool { self.by_key("doc").tt_values().any(|tt| { tt.delimiter.kind == DelimiterKind::Parenthesis && @@ -299,7 +251,6 @@ impl Attrs { } } -use std::slice::Iter as SliceIter; #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum DocAtom { /// eg. `#[doc(hidden)]` @@ -313,7 +264,6 @@ pub enum DocAtom { // Adapted from `CfgExpr` parsing code #[derive(Debug, Clone, PartialEq, Eq, Hash)] -// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))] pub enum DocExpr { Invalid, /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]` @@ -574,62 +524,6 @@ impl AttrsWithOwner { AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs)) } - - pub fn docs_with_rangemap( - &self, - db: &dyn DefDatabase, - ) -> Option<(Documentation, DocsRangeMap)> { - let docs = - self.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id))); - let indent = doc_indent(self); - let mut buf = String::new(); - let mut mapping = Vec::new(); - for (doc, idx) in docs { - if !doc.is_empty() { - let mut base_offset = 0; - for raw_line in doc.split('\n') { - let line = raw_line.trim_end(); - let line_len = line.len(); - let (offset, line) = match line.char_indices().nth(indent) { - Some((offset, _)) => (offset, &line[offset..]), - None => (0, line), - }; - let buf_offset = buf.len(); - buf.push_str(line); - mapping.push(( - TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?), - idx, - TextRange::at( - (base_offset + offset).try_into().ok()?, - line_len.try_into().ok()?, - ), - )); - buf.push('\n'); - base_offset += raw_line.len() + 1; - } - } else { - buf.push('\n'); - } - } - buf.pop(); - if buf.is_empty() { - None - } else { - Some((Documentation(buf), DocsRangeMap { mapping, source_map: self.source_map(db) })) - } - } -} - -fn doc_indent(attrs: &Attrs) -> usize { - attrs - .by_key("doc") - .attrs() - .filter_map(|attr| attr.string_value()) - .flat_map(|s| s.lines()) - .filter(|line| !line.chars().all(|c| c.is_whitespace())) - .map(|line| line.chars().take_while(|c| c.is_whitespace()).count()) - .min() - .unwrap_or(0) } #[derive(Debug)] @@ -673,7 +567,7 @@ impl AttrSourceMap { self.source_of_id(attr.id) } - fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> { + pub fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> { let ast_idx = id.ast_index(); let file_id = match self.mod_def_site_file_id { Some((file_id, def_site_cut)) if def_site_cut <= ast_idx => file_id, @@ -687,69 +581,6 @@ impl AttrSourceMap { } } -/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. -#[derive(Debug)] -pub struct DocsRangeMap { - source_map: AttrSourceMap, - // (docstring-line-range, attr_index, attr-string-range) - // a mapping from the text range of a line of the [`Documentation`] to the attribute index and - // the original (untrimmed) syntax doc line - mapping: Vec<(TextRange, AttrId, TextRange)>, -} - -impl DocsRangeMap { - /// Maps a [`TextRange`] relative to the documentation string back to its AST range - pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> { - let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?; - let (line_docs_range, idx, original_line_src_range) = self.mapping[found]; - if !line_docs_range.contains_range(range) { - return None; - } - - let relative_range = range - line_docs_range.start(); - - let InFile { file_id, value: source } = self.source_map.source_of_id(idx); - match source { - Either::Left(attr) => { - let string = get_doc_string_in_attr(attr)?; - let text_range = string.open_quote_text_range()?; - let range = TextRange::at( - text_range.end() + original_line_src_range.start() + relative_range.start(), - string.syntax().text_range().len().min(range.len()), - ); - Some(InFile { file_id, value: range }) - } - Either::Right(comment) => { - let text_range = comment.syntax().text_range(); - let range = TextRange::at( - text_range.start() - + TextSize::try_from(comment.prefix().len()).ok()? - + original_line_src_range.start() - + relative_range.start(), - text_range.len().min(range.len()), - ); - Some(InFile { file_id, value: range }) - } - } - } -} - -fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> { - match it.expr() { - // #[doc = lit] - Some(ast::Expr::Literal(lit)) => match lit.kind() { - ast::LiteralKind::String(it) => Some(it), - _ => None, - }, - // #[cfg_attr(..., doc = "", ...)] - None => { - // FIXME: See highlight injection for what to do here - None - } - _ => None, - } -} - #[derive(Debug, Clone, Copy)] pub struct AttrQuery<'attr> { attrs: &'attr Attrs, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs index 9bd0c30b105..152f05b2c3b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs @@ -8,7 +8,8 @@ //! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to //! ease updating. -use once_cell::sync::OnceCell; +use std::sync::OnceLock; + use rustc_hash::FxHashMap; /// Ignored attribute namespaces used by tools. @@ -29,7 +30,7 @@ pub struct AttributeTemplate { } pub fn find_builtin_attr_idx(name: &str) -> Option<usize> { - static BUILTIN_LOOKUP_TABLE: OnceCell<FxHashMap<&'static str, usize>> = OnceCell::new(); + static BUILTIN_LOOKUP_TABLE: OnceLock<FxHashMap<&'static str, usize>> = OnceLock::new(); BUILTIN_LOOKUP_TABLE .get_or_init(|| { INERT_ATTRIBUTES.iter().map(|attr| attr.name).enumerate().map(|(a, b)| (b, a)).collect() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index f8d492d0e52..c0baf6011f7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -65,6 +65,8 @@ pub type LabelSource = InFile<LabelPtr>; pub type FieldPtr = AstPtr<ast::RecordExprField>; pub type FieldSource = InFile<FieldPtr>; +pub type PatFieldPtr = AstPtr<ast::RecordPatField>; +pub type PatFieldSource = InFile<PatFieldPtr>; /// An item body together with the mapping from syntax nodes to HIR expression /// IDs. This is needed to go from e.g. a position in a file to the HIR @@ -90,8 +92,8 @@ pub struct BodySourceMap { /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). /// Instead, we use id of expression (`92`) to identify the field. - field_map: FxHashMap<FieldSource, ExprId>, field_map_back: FxHashMap<ExprId, FieldSource>, + pat_field_map_back: FxHashMap<PatId, PatFieldSource>, expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, @@ -164,9 +166,10 @@ impl Body { }; let module = def.module(db); let expander = Expander::new(db, file_id, module); - let (mut body, source_map) = + let (mut body, mut source_map) = Body::new(db, def, expander, params, body, module.krate, is_async_fn); body.shrink_to_fit(); + source_map.shrink_to_fit(); (Arc::new(body), Arc::new(source_map)) } @@ -375,9 +378,8 @@ impl BodySourceMap { self.field_map_back[&expr].clone() } - pub fn node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId> { - let src = node.map(AstPtr::new); - self.field_map.get(&src).cloned() + pub fn pat_field_syntax(&self, pat: PatId) -> PatFieldSource { + self.pat_field_map_back[&pat].clone() } pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprId> { @@ -389,4 +391,29 @@ impl BodySourceMap { pub fn diagnostics(&self) -> &[BodyDiagnostic] { &self.diagnostics } + + fn shrink_to_fit(&mut self) { + let Self { + expr_map, + expr_map_back, + pat_map, + pat_map_back, + label_map, + label_map_back, + field_map_back, + pat_field_map_back, + expansions, + diagnostics, + } = self; + expr_map.shrink_to_fit(); + expr_map_back.shrink_to_fit(); + pat_map.shrink_to_fit(); + pat_map_back.shrink_to_fit(); + label_map.shrink_to_fit(); + label_map_back.shrink_to_fit(); + field_map_back.shrink_to_fit(); + pat_field_map_back.shrink_to_fit(); + expansions.shrink_to_fit(); + diagnostics.shrink_to_fit(); + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 7071fcb9394..cc02df80a8f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -25,13 +25,20 @@ use triomphe::Arc; use crate::{ body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr}, + builtin_type::BuiltinUint, data::adt::StructKind, db::DefDatabase, expander::Expander, hir::{ - dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, - ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, - Pat, PatId, RecordFieldPat, RecordLitField, Statement, + dummy_expr_id, + format_args::{ + self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, + FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions, + FormatPlaceholder, FormatSign, FormatTrait, + }, + Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, + Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, + OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -42,6 +49,8 @@ use crate::{ AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro, }; +type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>; + pub(super) fn lower( db: &dyn DefDatabase, owner: DefWithBodyId, @@ -437,7 +446,6 @@ impl ExprCollector<'_> { None => self.missing_expr(), }; let src = self.expander.to_source(AstPtr::new(&field)); - self.source_map.field_map.insert(src.clone(), expr); self.source_map.field_map_back.insert(expr, src); Some(RecordLitField { name, expr }) }) @@ -579,11 +587,6 @@ impl ExprCollector<'_> { syntax_ptr, ) } - ast::Expr::BoxExpr(e) => { - let expr = self.collect_expr_opt(e.expr()); - self.alloc_expr(Expr::Box { expr }, syntax_ptr) - } - ast::Expr::ArrayExpr(e) => { let kind = e.kind(); @@ -653,6 +656,16 @@ impl ExprCollector<'_> { } } ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), + ast::Expr::AsmExpr(e) => { + let e = self.collect_expr_opt(e.expr()); + self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr) + } + ast::Expr::OffsetOfExpr(e) => { + let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); + let fields = e.fields().map(|it| it.as_name()).collect(); + self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr) + } + ast::Expr::FormatArgsExpr(f) => self.collect_format_args(f, syntax_ptr), }) } @@ -663,6 +676,7 @@ impl ExprCollector<'_> { let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr); let prev_binding_owner = self.current_binding_owner.take(); self.current_binding_owner = Some(result_expr_id); + (result_expr_id, prev_binding_owner) } @@ -744,7 +758,27 @@ impl ExprCollector<'_> { fn collect_while_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::WhileExpr) -> ExprId { let label = e.label().map(|label| self.collect_label(label)); let body = self.collect_labelled_block_opt(label, e.loop_body()); - let condition = self.collect_expr_opt(e.condition()); + + // Labels can also be used in the condition expression, like this: + // ``` + // fn main() { + // let mut optional = Some(0); + // 'my_label: while let Some(a) = match optional { + // None => break 'my_label, + // Some(val) => Some(val), + // } { + // println!("{}", a); + // optional = None; + // } + // } + // ``` + let condition = match label { + Some(label) => { + self.with_labeled_rib(label, |this| this.collect_expr_opt(e.condition())) + } + None => self.collect_expr_opt(e.condition()), + }; + let break_expr = self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone()); let if_expr = self.alloc_expr( @@ -1295,23 +1329,21 @@ impl ExprCollector<'_> { ast::Pat::RecordPat(p) => { let path = p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); - let args = p - .record_pat_field_list() - .expect("every struct should have a field list") + let record_pat_field_list = + &p.record_pat_field_list().expect("every struct should have a field list"); + let args = record_pat_field_list .fields() .filter_map(|f| { let ast_pat = f.pat()?; let pat = self.collect_pat(ast_pat, binding_list); let name = f.field_name()?.as_name(); + let src = self.expander.to_source(AstPtr::new(&f)); + self.source_map.pat_field_map_back.insert(pat, src); Some(RecordFieldPat { name, pat }) }) .collect(); - let ellipsis = p - .record_pat_field_list() - .expect("every struct should have a field list") - .rest_pat() - .is_some(); + let ellipsis = record_pat_field_list.rest_pat().is_some(); Pat::Record { path, args, ellipsis } } @@ -1531,6 +1563,401 @@ impl ExprCollector<'_> { } } // endregion: labels + + // region: format + fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, bool)> { + let m = match expr { + ast::Expr::MacroExpr(m) => m, + ast::Expr::Literal(l) => { + return match l.kind() { + ast::LiteralKind::String(s) => Some((s, true)), + _ => None, + } + } + _ => return None, + }; + let e = m.macro_call()?; + let macro_ptr = AstPtr::new(&e); + let (exp, _) = self.collect_macro_call(e, macro_ptr, true, |this, expansion| { + expansion.and_then(|it| this.expand_macros_to_string(it)) + })?; + Some((exp, false)) + } + + fn collect_format_args( + &mut self, + f: ast::FormatArgsExpr, + syntax_ptr: AstPtr<ast::Expr>, + ) -> ExprId { + let mut args = FormatArgumentsCollector::new(); + f.args().for_each(|arg| { + args.add(FormatArgument { + kind: match arg.name() { + Some(name) => FormatArgumentKind::Named(name.as_name()), + None => FormatArgumentKind::Normal, + }, + expr: self.collect_expr_opt(arg.expr()), + }); + }); + let template = f.template(); + let fmt_snippet = template.as_ref().map(ToString::to_string); + let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) { + Some((s, is_direct_literal)) => { + format_args::parse(&s, fmt_snippet, args, is_direct_literal, |name| { + self.alloc_expr_desugared(Expr::Path(Path::from(name))) + }) + } + None => FormatArgs { template: Default::default(), arguments: args.finish() }, + }; + + // Create a list of all _unique_ (argument, format trait) combinations. + // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)] + let mut argmap = FxIndexSet::default(); + for piece in fmt.template.iter() { + let FormatArgsPiece::Placeholder(placeholder) = piece else { continue }; + if let Ok(index) = placeholder.argument.index { + argmap.insert((index, ArgumentType::Format(placeholder.format_trait))); + } + } + + let lit_pieces = + fmt.template + .iter() + .enumerate() + .filter_map(|(i, piece)| { + match piece { + FormatArgsPiece::Literal(s) => Some( + self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))), + ), + &FormatArgsPiece::Placeholder(_) => { + // Inject empty string before placeholders when not already preceded by a literal piece. + if i == 0 + || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) + { + Some(self.alloc_expr_desugared(Expr::Literal(Literal::String( + "".into(), + )))) + } else { + None + } + } + } + }) + .collect(); + let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList { + elements: lit_pieces, + is_assignee_expr: false, + })); + let lit_pieces = self.alloc_expr_desugared(Expr::Ref { + expr: lit_pieces, + rawness: Rawness::Ref, + mutability: Mutability::Shared, + }); + let format_options = { + // Generate: + // &[format_spec_0, format_spec_1, format_spec_2] + let elements = fmt + .template + .iter() + .filter_map(|piece| { + let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; + Some(self.make_format_spec(placeholder, &mut argmap)) + }) + .collect(); + let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { + elements, + is_assignee_expr: false, + })); + self.alloc_expr_desugared(Expr::Ref { + expr: array, + rawness: Rawness::Ref, + mutability: Mutability::Shared, + }) + }; + let arguments = &*fmt.arguments.arguments; + + let args = if arguments.is_empty() { + let expr = self.alloc_expr_desugared(Expr::Array(Array::ElementList { + elements: Box::default(), + is_assignee_expr: false, + })); + self.alloc_expr_desugared(Expr::Ref { + expr, + rawness: Rawness::Ref, + mutability: Mutability::Shared, + }) + } else { + // Generate: + // &match (&arg0, &arg1, &…) { + // args => [ + // <core::fmt::Argument>::new_display(args.0), + // <core::fmt::Argument>::new_lower_hex(args.1), + // <core::fmt::Argument>::new_debug(args.0), + // … + // ] + // } + let args = argmap + .iter() + .map(|&(arg_index, ty)| { + let arg = self.alloc_expr_desugared(Expr::Ref { + expr: arguments[arg_index].expr, + rawness: Rawness::Ref, + mutability: Mutability::Shared, + }); + self.make_argument(arg, ty) + }) + .collect(); + let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { + elements: args, + is_assignee_expr: false, + })); + self.alloc_expr_desugared(Expr::Ref { + expr: array, + rawness: Rawness::Ref, + mutability: Mutability::Shared, + }) + }; + + // Generate: + // <core::fmt::Arguments>::new_v1_formatted( + // lit_pieces, + // args, + // format_options, + // unsafe { ::core::fmt::UnsafeArg::new() } + // ) + + let Some(new_v1_formatted) = + LangItem::FormatArguments.ty_rel_path(self.db, self.krate, name![new_v1_formatted]) + else { + return self.missing_expr(); + }; + let Some(unsafe_arg_new) = + LangItem::FormatUnsafeArg.ty_rel_path(self.db, self.krate, name![new]) + else { + return self.missing_expr(); + }; + let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted)); + + let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new)); + let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call { + callee: unsafe_arg_new, + args: Box::default(), + is_assignee_expr: false, + }); + let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe { + id: None, + statements: Box::default(), + tail: Some(unsafe_arg_new), + }); + + self.alloc_expr( + Expr::Call { + callee: new_v1_formatted, + args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]), + is_assignee_expr: false, + }, + syntax_ptr, + ) + } + + /// Generate a hir expression for a format_args placeholder specification. + /// + /// Generates + /// + /// ```text + /// <core::fmt::rt::Placeholder::new( + /// …usize, // position + /// '…', // fill + /// <core::fmt::rt::Alignment>::…, // alignment + /// …u32, // flags + /// <core::fmt::rt::Count::…>, // width + /// <core::fmt::rt::Count::…>, // precision + /// ) + /// ``` + fn make_format_spec( + &mut self, + placeholder: &FormatPlaceholder, + argmap: &mut FxIndexSet<(usize, ArgumentType)>, + ) -> ExprId { + let position = match placeholder.argument.index { + Ok(arg_index) => { + let (i, _) = + argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait))); + self.alloc_expr_desugared(Expr::Literal(Literal::Uint( + i as u128, + Some(BuiltinUint::Usize), + ))) + } + Err(_) => self.missing_expr(), + }; + let &FormatOptions { + ref width, + ref precision, + alignment, + fill, + sign, + alternate, + zero_pad, + debug_hex, + } = &placeholder.format_options; + let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' ')))); + + let align = { + let align = LangItem::FormatAlignment.ty_rel_path( + self.db, + self.krate, + match alignment { + Some(FormatAlignment::Left) => name![Left], + Some(FormatAlignment::Right) => name![Right], + Some(FormatAlignment::Center) => name![Center], + None => name![Unknown], + }, + ); + match align { + Some(path) => self.alloc_expr_desugared(Expr::Path(path)), + None => self.missing_expr(), + } + }; + // This needs to match `Flag` in library/core/src/fmt/rt.rs. + let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32) + | ((sign == Some(FormatSign::Minus)) as u32) << 1 + | (alternate as u32) << 2 + | (zero_pad as u32) << 3 + | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4 + | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5; + let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( + flags as u128, + Some(BuiltinUint::U32), + ))); + let precision = self.make_count(&precision, argmap); + let width = self.make_count(&width, argmap); + + let format_placeholder_new = { + let format_placeholder_new = + LangItem::FormatPlaceholder.ty_rel_path(self.db, self.krate, name![new]); + match format_placeholder_new { + Some(path) => self.alloc_expr_desugared(Expr::Path(path)), + None => self.missing_expr(), + } + }; + + self.alloc_expr_desugared(Expr::Call { + callee: format_placeholder_new, + args: Box::new([position, fill, align, flags, precision, width]), + is_assignee_expr: false, + }) + } + + /// Generate a hir expression for a format_args Count. + /// + /// Generates: + /// + /// ```text + /// <core::fmt::rt::Count>::Is(…) + /// ``` + /// + /// or + /// + /// ```text + /// <core::fmt::rt::Count>::Param(…) + /// ``` + /// + /// or + /// + /// ```text + /// <core::fmt::rt::Count>::Implied + /// ``` + fn make_count( + &mut self, + count: &Option<FormatCount>, + argmap: &mut FxIndexSet<(usize, ArgumentType)>, + ) -> 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(), + } + } + 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, + }) + } + None => self.missing_expr(), + } + } else { + self.missing_expr() + } + } + None => match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Implied]) { + Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), + None => self.missing_expr(), + }, + } + } + + /// Generate a hir expression representing an argument to a format_args invocation. + /// + /// Generates: + /// + /// ```text + /// <core::fmt::Argument>::new_…(arg) + /// ``` + fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId { + use ArgumentType::*; + use FormatTrait::*; + match LangItem::FormatArgument.ty_rel_path( + self.db, + self.krate, + match ty { + Format(Display) => name![new_display], + Format(Debug) => name![new_debug], + Format(LowerExp) => name![new_lower_exp], + Format(UpperExp) => name![new_upper_exp], + Format(Octal) => name![new_octal], + Format(Pointer) => name![new_pointer], + Format(Binary) => name![new_binary], + Format(LowerHex) => name![new_lower_hex], + Format(UpperHex) => name![new_upper_hex], + 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, + }) + } + None => self.missing_expr(), + } + } + // endregion: format } fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> { @@ -1606,3 +2033,9 @@ fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool { (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))() .map_or(false, |it| it.kind() == syntax::T![,]) } + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +enum ArgumentType { + Format(FormatTrait), + Usize, +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs index 5d71abe37cc..fad4d7a4da6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Write}; -use hir_expand::db::ExpandDatabase; +use itertools::Itertools; use syntax::ast::HasName; use crate::{ @@ -51,8 +51,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo } }; - let mut p = - Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false }; + let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false }; if let DefWithBodyId::FunctionId(it) = owner { p.buf.push('('); body.params.iter().zip(&db.function_data(it).params).for_each(|(¶m, ty)| { @@ -76,8 +75,7 @@ pub(super) fn print_expr_hir( _owner: DefWithBodyId, expr: ExprId, ) -> String { - let mut p = - Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false }; + let mut p = Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false }; p.print_expr(expr); p.buf } @@ -98,7 +96,7 @@ macro_rules! wln { } struct Printer<'a> { - db: &'a dyn ExpandDatabase, + db: &'a dyn DefDatabase, body: &'a Body, buf: String, indent_level: usize, @@ -142,9 +140,14 @@ impl Printer<'_> { } fn newline(&mut self) { - match self.buf.chars().rev().find(|ch| *ch != ' ') { - Some('\n') | None => {} - _ => writeln!(self).unwrap(), + match self.buf.chars().rev().find_position(|ch| *ch != ' ') { + Some((_, '\n')) | None => {} + Some((idx, _)) => { + if idx != 0 { + self.buf.drain(self.buf.len() - idx..); + } + writeln!(self).unwrap() + } } } @@ -154,6 +157,19 @@ impl Printer<'_> { match expr { Expr::Missing => w!(self, "�"), Expr::Underscore => w!(self, "_"), + Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"), + Expr::OffsetOf(offset_of) => { + w!(self, "builtin#offset_of("); + self.print_type_ref(&offset_of.container); + w!( + self, + ", {})", + offset_of + .fields + .iter() + .format_with(".", |field, f| f(&field.display(self.db.upcast()))) + ); + } Expr::Path(path) => self.print_path(path), Expr::If { condition, then_branch, else_branch } => { w!(self, "if "); @@ -173,7 +189,7 @@ impl Printer<'_> { } Expr::Loop { body, label } => { if let Some(lbl) = label { - w!(self, "{}: ", self.body[*lbl].name.display(self.db)); + w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast())); } w!(self, "loop "); self.print_expr(*body); @@ -193,7 +209,7 @@ impl Printer<'_> { } Expr::MethodCall { receiver, method_name, args, generic_args } => { self.print_expr(*receiver); - w!(self, ".{}", method_name.display(self.db)); + w!(self, ".{}", method_name.display(self.db.upcast())); if let Some(args) = generic_args { w!(self, "::<"); print_generic_args(self.db, args, self).unwrap(); @@ -231,13 +247,13 @@ impl Printer<'_> { Expr::Continue { label } => { w!(self, "continue"); if let Some(lbl) = label { - w!(self, " {}", self.body[*lbl].name.display(self.db)); + w!(self, " {}", self.body[*lbl].name.display(self.db.upcast())); } } Expr::Break { expr, label } => { w!(self, "break"); if let Some(lbl) = label { - w!(self, " {}", self.body[*lbl].name.display(self.db)); + w!(self, " {}", self.body[*lbl].name.display(self.db.upcast())); } if let Some(expr) = expr { self.whitespace(); @@ -276,7 +292,7 @@ impl Printer<'_> { w!(self, "{{"); self.indented(|p| { for field in &**fields { - w!(p, "{}: ", field.name.display(self.db)); + w!(p, "{}: ", field.name.display(self.db.upcast())); p.print_expr(field.expr); wln!(p, ","); } @@ -293,7 +309,7 @@ impl Printer<'_> { } Expr::Field { expr, name } => { self.print_expr(*expr); - w!(self, ".{}", name.display(self.db)); + w!(self, ".{}", name.display(self.db.upcast())); } Expr::Await { expr } => { self.print_expr(*expr); @@ -431,7 +447,8 @@ impl Printer<'_> { } Expr::Literal(lit) => self.print_literal(lit), Expr::Block { id: _, statements, tail, label } => { - let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db))); + let label = + label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db.upcast()))); self.print_block(label.as_deref(), statements, tail); } Expr::Unsafe { id: _, statements, tail } => { @@ -507,7 +524,7 @@ impl Printer<'_> { w!(self, " {{"); self.indented(|p| { for arg in args.iter() { - w!(p, "{}: ", arg.name.display(self.db)); + w!(p, "{}: ", arg.name.display(self.db.upcast())); p.print_pat(arg.pat); wln!(p, ","); } @@ -666,6 +683,6 @@ impl Printer<'_> { BindingAnnotation::Ref => "ref ", BindingAnnotation::RefMut => "ref mut ", }; - w!(self, "{}{}", mode, name.display(self.db)); + w!(self, "{}{}", mode, name.display(self.db.upcast())); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs index d5582011645..1658757d2b6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs @@ -1,13 +1,13 @@ mod block; use base_db::{fixture::WithFixture, SourceDatabase}; -use expect_test::Expect; +use expect_test::{expect, Expect}; use crate::{test_db::TestDB, ModuleDefId}; use super::*; -fn lower(ra_fixture: &str) -> Arc<Body> { +fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) { let db = TestDB::with_files(ra_fixture); let krate = db.crate_graph().iter().next().unwrap(); @@ -21,8 +21,10 @@ fn lower(ra_fixture: &str) -> Arc<Body> { } } } + let fn_def = fn_def.unwrap().into(); - db.body(fn_def.unwrap().into()) + let body = db.body(fn_def); + (db, body, fn_def) } fn def_map_at(ra_fixture: &str) -> String { @@ -138,3 +140,84 @@ mod m { "#, ); } + +#[test] +fn desugar_builtin_format_args() { + // Regression test for a path resolution bug introduced with inner item handling. + let (db, body, def) = lower( + r#" +//- minicore: fmt +fn main() { + let are = "are"; + let count = 10; + builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); +} +"#, + ); + + expect![[r#" + fn main() { + let are = "are"; + let count = 10; + builtin#lang(Arguments::new_v1_formatted)( + &[ + "\"hello ", " ", " friends, we ", " ", "", "\"", + ], + &[ + builtin#lang(Argument::new_display)( + &count, + ), builtin#lang(Argument::new_display)( + &"fancy", + ), builtin#lang(Argument::new_debug)( + &are, + ), builtin#lang(Argument::new_display)( + &"!", + ), + ], + &[ + builtin#lang(Placeholder::new)( + 0usize, + ' ', + builtin#lang(Alignment::Unknown), + 8u32, + builtin#lang(Count::Implied), + builtin#lang(Count::Is)( + 2usize, + ), + ), builtin#lang(Placeholder::new)( + 1usize, + ' ', + builtin#lang(Alignment::Unknown), + 0u32, + builtin#lang(Count::Implied), + builtin#lang(Count::Implied), + ), builtin#lang(Placeholder::new)( + 2usize, + ' ', + builtin#lang(Alignment::Unknown), + 0u32, + builtin#lang(Count::Implied), + builtin#lang(Count::Implied), + ), builtin#lang(Placeholder::new)( + 1usize, + ' ', + builtin#lang(Alignment::Unknown), + 0u32, + builtin#lang(Count::Implied), + builtin#lang(Count::Implied), + ), builtin#lang(Placeholder::new)( + 3usize, + ' ', + builtin#lang(Alignment::Unknown), + 0u32, + builtin#lang(Count::Implied), + builtin#lang(Count::Implied), + ), + ], + unsafe { + builtin#lang(UnsafeArg::new)() + }, + ); + }"#]] + .assert_eq(&body.pretty_print(&db, def)) +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index c8df3f3f96a..224f7328f8c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -447,6 +447,7 @@ impl VariantData { } } + // FIXME: Linear lookup pub fn field(&self, name: &Name) -> Option<LocalFieldId> { self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None }) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index b60e7909105..b9c5ff72792 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -37,6 +37,20 @@ pub fn find_path_prefixed( find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std) } +#[derive(Copy, Clone, Debug)] +enum Stability { + Unstable, + Stable, +} +use Stability::*; + +fn zip_stability(a: Stability, b: Stability) -> Stability { + match (a, b) { + (Stable, Stable) => Stable, + _ => Unstable, + } +} + const MAX_PATH_LEN: usize = 15; #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -95,7 +109,8 @@ fn find_path_inner( MAX_PATH_LEN, prefixed, prefer_no_std || db.crate_supports_no_std(crate_root.krate), - ); + ) + .map(|(item, _)| item); } // - if the item is already in scope, return the name under which it is @@ -143,6 +158,7 @@ fn find_path_inner( prefer_no_std || db.crate_supports_no_std(crate_root.krate), scope_name, ) + .map(|(item, _)| item) } fn find_path_for_module( @@ -155,7 +171,7 @@ fn find_path_for_module( max_len: usize, prefixed: Option<PrefixKind>, prefer_no_std: bool, -) -> Option<ModPath> { +) -> Option<(ModPath, Stability)> { if max_len == 0 { return None; } @@ -165,19 +181,19 @@ fn find_path_for_module( let scope_name = find_in_scope(db, def_map, from, ItemInNs::Types(module_id.into())); if prefixed.is_none() { if let Some(scope_name) = scope_name { - return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name))); + return Some((ModPath::from_segments(PathKind::Plain, Some(scope_name)), Stable)); } } // - if the item is the crate root, return `crate` if module_id == crate_root { - return Some(ModPath::from_segments(PathKind::Crate, None)); + return Some((ModPath::from_segments(PathKind::Crate, None), Stable)); } // - if relative paths are fine, check if we are searching for a parent if prefixed.filter(PrefixKind::is_absolute).is_none() { if let modpath @ Some(_) = find_self_super(def_map, module_id, from) { - return modpath; + return modpath.zip(Some(Stable)); } } @@ -201,14 +217,14 @@ fn find_path_for_module( } else { PathKind::Plain }; - return Some(ModPath::from_segments(kind, Some(name))); + return Some((ModPath::from_segments(kind, Some(name)), Stable)); } } if let value @ Some(_) = find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from) { - return value; + return value.zip(Some(Stable)); } calculate_best_path( db, @@ -301,11 +317,19 @@ fn calculate_best_path( mut prefixed: Option<PrefixKind>, prefer_no_std: bool, scope_name: Option<Name>, -) -> Option<ModPath> { +) -> Option<(ModPath, Stability)> { if max_len <= 1 { return None; } let mut best_path = None; + let update_best_path = + |best_path: &mut Option<_>, new_path: (ModPath, Stability)| match best_path { + Some((old_path, old_stability)) => { + *old_path = new_path.0; + *old_stability = zip_stability(*old_stability, new_path.1); + } + None => *best_path = Some(new_path), + }; // Recursive case: // - otherwise, look for modules containing (reexporting) it and import it from one of those if item.krate(db) == Some(from.krate) { @@ -328,14 +352,14 @@ fn calculate_best_path( prefixed, prefer_no_std, ) { - path.push_segment(name); + path.0.push_segment(name); - let new_path = match best_path { + let new_path = match best_path.take() { Some(best_path) => select_best_path(best_path, path, prefer_no_std), None => path, }; - best_path_len = new_path.len(); - best_path = Some(new_path); + best_path_len = new_path.0.len(); + update_best_path(&mut best_path, new_path); } } } else { @@ -354,7 +378,7 @@ fn calculate_best_path( // Determine best path for containing module and append last segment from `info`. // FIXME: we should guide this to look up the path locally, or from the same crate again? - let mut path = find_path_for_module( + let (mut path, path_stability) = find_path_for_module( db, def_map, visited_modules, @@ -367,16 +391,19 @@ fn calculate_best_path( )?; cov_mark::hit!(partially_imported); path.push_segment(info.name.clone()); - Some(path) + Some(( + path, + zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }), + )) }) }); for path in extern_paths { - let new_path = match best_path { + let new_path = match best_path.take() { Some(best_path) => select_best_path(best_path, path, prefer_no_std), None => path, }; - best_path = Some(new_path); + update_best_path(&mut best_path, new_path); } } if let Some(module) = item.module(db) { @@ -387,15 +414,24 @@ fn calculate_best_path( } match prefixed.map(PrefixKind::prefix) { Some(prefix) => best_path.or_else(|| { - scope_name.map(|scope_name| ModPath::from_segments(prefix, Some(scope_name))) + scope_name.map(|scope_name| (ModPath::from_segments(prefix, Some(scope_name)), Stable)) }), None => best_path, } } -fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { +fn select_best_path( + old_path: (ModPath, Stability), + new_path: (ModPath, Stability), + prefer_no_std: bool, +) -> (ModPath, Stability) { + match (old_path.1, new_path.1) { + (Stable, Unstable) => return old_path, + (Unstable, Stable) => return new_path, + _ => {} + } const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc]; - match (old_path.segments().first(), new_path.segments().first()) { + match (old_path.0.segments().first(), new_path.0.segments().first()) { (Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => { let rank = match prefer_no_std { false => |name: &Name| match name { @@ -416,7 +452,7 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) - match nrank.cmp(&orank) { Ordering::Less => old_path, Ordering::Equal => { - if new_path.len() < old_path.len() { + if new_path.0.len() < old_path.0.len() { new_path } else { old_path @@ -426,7 +462,7 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) - } } _ => { - if new_path.len() < old_path.len() { + if new_path.0.len() < old_path.0.len() { new_path } else { old_path @@ -1360,4 +1396,29 @@ pub mod ops { "std::ops::Deref", ); } + + #[test] + fn respect_unstable_modules() { + check_found_path( + r#" +//- /main.rs crate:main deps:std,core +#![no_std] +extern crate std; +$0 +//- /longer.rs crate:std deps:core +pub mod error { + pub use core::error::Error; +} +//- /core.rs crate:core +pub mod error { + #![unstable(feature = "error_in_core", issue = "103765")] + pub trait Error {} +} +"#, + "std::error::Error", + "std::error::Error", + "std::error::Error", + "std::error::Error", + ); + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 6591c92ac62..591ee77c70a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -13,6 +13,7 @@ //! See also a neighboring `body` module. pub mod type_ref; +pub mod format_args; use std::fmt; @@ -117,7 +118,6 @@ impl From<ast::LiteralKind> for Literal { fn from(ast_lit_kind: ast::LiteralKind) -> Self { use ast::LiteralKind; match ast_lit_kind { - // FIXME: these should have actual values filled in, but unsure on perf impact LiteralKind::IntNumber(lit) => { if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { Literal::Float( @@ -281,6 +281,19 @@ pub enum Expr { Array(Array), Literal(Literal), Underscore, + OffsetOf(OffsetOf), + InlineAsm(InlineAsm), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct OffsetOf { + pub container: Interned<TypeRef>, + pub fields: Box<[Name]>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InlineAsm { + pub e: ExprId, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -341,7 +354,8 @@ impl Expr { pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) { match self { Expr::Missing => {} - Expr::Path(_) => {} + Expr::Path(_) | Expr::OffsetOf(_) => {} + Expr::InlineAsm(it) => f(it.e), Expr::If { condition, then_branch, else_branch } => { f(*condition); f(*then_branch); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs new file mode 100644 index 00000000000..75025a984fc --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -0,0 +1,502 @@ +//! Parses `format_args` input. +use std::mem; + +use hir_expand::name::Name; +use rustc_parse_format as parse; +use syntax::{ + ast::{self, IsString}, + AstToken, SmolStr, TextRange, +}; + +use crate::hir::ExprId; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FormatArgs { + pub template: Box<[FormatArgsPiece]>, + pub arguments: FormatArguments, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FormatArguments { + pub arguments: Box<[FormatArgument]>, + pub num_unnamed_args: usize, + pub num_explicit_args: usize, + pub names: Box<[(Name, usize)]>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum FormatArgsPiece { + Literal(Box<str>), + Placeholder(FormatPlaceholder), +} + +#[derive(Copy, Debug, Clone, PartialEq, Eq)] +pub struct FormatPlaceholder { + /// Index into [`FormatArgs::arguments`]. + pub argument: FormatArgPosition, + /// The span inside the format string for the full `{…}` placeholder. + pub span: Option<TextRange>, + /// `{}`, `{:?}`, or `{:x}`, etc. + pub format_trait: FormatTrait, + /// `{}` or `{:.5}` or `{:-^20}`, etc. + pub format_options: FormatOptions, +} + +#[derive(Copy, Debug, Clone, PartialEq, Eq)] +pub struct FormatArgPosition { + /// Which argument this position refers to (Ok), + /// or would've referred to if it existed (Err). + pub index: Result<usize, usize>, + /// What kind of position this is. See [`FormatArgPositionKind`]. + pub kind: FormatArgPositionKind, + /// The span of the name or number. + pub span: Option<TextRange>, +} + +#[derive(Copy, Debug, Clone, PartialEq, Eq)] +pub enum FormatArgPositionKind { + /// `{}` or `{:.*}` + Implicit, + /// `{1}` or `{:1$}` or `{:.1$}` + Number, + /// `{a}` or `{:a$}` or `{:.a$}` + Named, +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum FormatTrait { + /// `{}` + Display, + /// `{:?}` + Debug, + /// `{:e}` + LowerExp, + /// `{:E}` + UpperExp, + /// `{:o}` + Octal, + /// `{:p}` + Pointer, + /// `{:b}` + Binary, + /// `{:x}` + LowerHex, + /// `{:X}` + UpperHex, +} + +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] +pub struct FormatOptions { + /// The width. E.g. `{:5}` or `{:width$}`. + pub width: Option<FormatCount>, + /// The precision. E.g. `{:.5}` or `{:.precision$}`. + pub precision: Option<FormatCount>, + /// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`. + pub alignment: Option<FormatAlignment>, + /// The fill character. E.g. the `.` in `{:.>10}`. + pub fill: Option<char>, + /// The `+` or `-` flag. + pub sign: Option<FormatSign>, + /// The `#` flag. + pub alternate: bool, + /// The `0` flag. E.g. the `0` in `{:02x}`. + pub zero_pad: bool, + /// The `x` or `X` flag (for `Debug` only). E.g. the `x` in `{:x?}`. + pub debug_hex: Option<FormatDebugHex>, +} +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum FormatSign { + /// The `+` flag. + Plus, + /// The `-` flag. + Minus, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum FormatDebugHex { + /// The `x` flag in `{:x?}`. + Lower, + /// The `X` flag in `{:X?}`. + Upper, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum FormatAlignment { + /// `{:<}` + Left, + /// `{:>}` + Right, + /// `{:^}` + Center, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum FormatCount { + /// `{:5}` or `{:.5}` + Literal(usize), + /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc. + Argument(FormatArgPosition), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FormatArgument { + pub kind: FormatArgumentKind, + pub expr: ExprId, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum FormatArgumentKind { + /// `format_args(…, arg)` + Normal, + /// `format_args(…, arg = 1)` + Named(Name), + /// `format_args("… {arg} …")` + Captured(Name), +} + +// Only used in parse_args and report_invalid_references, +// to indicate how a referred argument was used. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum PositionUsedAs { + Placeholder(Option<TextRange>), + Precision, + Width, +} +use PositionUsedAs::*; + +pub(crate) fn parse( + s: &ast::String, + fmt_snippet: Option<String>, + mut args: FormatArgumentsCollector, + is_direct_literal: bool, + mut synth: impl FnMut(Name) -> ExprId, +) -> FormatArgs { + let text = s.text(); + let str_style = match s.quote_offsets() { + Some(offsets) => { + let raw = u32::from(offsets.quotes.0.len()) - 1; + (raw != 0).then_some(raw as usize) + } + None => None, + }; + let mut parser = + parse::Parser::new(text, str_style, fmt_snippet, false, parse::ParseMode::Format); + + let mut pieces = Vec::new(); + while let Some(piece) = parser.next() { + if !parser.errors.is_empty() { + break; + } else { + pieces.push(piece); + } + } + let is_source_literal = parser.is_source_literal; + if !parser.errors.is_empty() { + // FIXME: Diagnose + return FormatArgs { template: Default::default(), arguments: args.finish() }; + } + + let to_span = |inner_span: parse::InnerSpan| { + is_source_literal.then(|| { + TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap()) + }) + }; + + let mut used = vec![false; args.explicit_args().len()]; + let mut invalid_refs = Vec::new(); + let mut numeric_refences_to_named_arg = Vec::new(); + + enum ArgRef<'a> { + Index(usize), + Name(&'a str, Option<TextRange>), + } + let mut lookup_arg = |arg: ArgRef<'_>, + span: Option<TextRange>, + used_as: PositionUsedAs, + kind: FormatArgPositionKind| + -> FormatArgPosition { + let index = match arg { + ArgRef::Index(index) => { + if let Some(arg) = args.by_index(index) { + used[index] = true; + if arg.kind.ident().is_some() { + // This was a named argument, but it was used as a positional argument. + numeric_refences_to_named_arg.push((index, span, used_as)); + } + Ok(index) + } else { + // Doesn't exist as an explicit argument. + invalid_refs.push((index, span, used_as, kind)); + Err(index) + } + } + ArgRef::Name(name, _span) => { + let name = Name::new_text_dont_use(SmolStr::new(name)); + if let Some((index, _)) = args.by_name(&name) { + // Name found in `args`, so we resolve it to its index. + if index < args.explicit_args().len() { + // Mark it as used, if it was an explicit argument. + used[index] = true; + } + Ok(index) + } else { + // Name not found in `args`, so we add it as an implicitly captured argument. + if !is_direct_literal { + // For the moment capturing variables from format strings expanded from macros is + // disabled (see RFC #2795) + // FIXME: Diagnose + } + Ok(args.add(FormatArgument { + kind: FormatArgumentKind::Captured(name.clone()), + // FIXME: This is problematic, we might want to synthesize a dummy + // expression proper and/or desugar these. + expr: synth(name), + })) + } + } + }; + FormatArgPosition { index, kind, span } + }; + + let mut template = Vec::new(); + let mut unfinished_literal = String::new(); + let mut placeholder_index = 0; + + for piece in pieces { + match piece { + parse::Piece::String(s) => { + unfinished_literal.push_str(s); + } + parse::Piece::NextArgument(arg) => { + let parse::Argument { position, position_span, format } = *arg; + if !unfinished_literal.is_empty() { + template.push(FormatArgsPiece::Literal( + mem::take(&mut unfinished_literal).into_boxed_str(), + )); + } + + let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s)); + placeholder_index += 1; + + let position_span = to_span(position_span); + let argument = match position { + parse::ArgumentImplicitlyIs(i) => lookup_arg( + ArgRef::Index(i), + position_span, + Placeholder(span), + FormatArgPositionKind::Implicit, + ), + parse::ArgumentIs(i) => lookup_arg( + ArgRef::Index(i), + position_span, + Placeholder(span), + FormatArgPositionKind::Number, + ), + parse::ArgumentNamed(name) => lookup_arg( + ArgRef::Name(name, position_span), + position_span, + Placeholder(span), + FormatArgPositionKind::Named, + ), + }; + + let alignment = match format.align { + parse::AlignUnknown => None, + parse::AlignLeft => Some(FormatAlignment::Left), + parse::AlignRight => Some(FormatAlignment::Right), + parse::AlignCenter => Some(FormatAlignment::Center), + }; + + let format_trait = match format.ty { + "" => FormatTrait::Display, + "?" => FormatTrait::Debug, + "e" => FormatTrait::LowerExp, + "E" => FormatTrait::UpperExp, + "o" => FormatTrait::Octal, + "p" => FormatTrait::Pointer, + "b" => FormatTrait::Binary, + "x" => FormatTrait::LowerHex, + "X" => FormatTrait::UpperHex, + _ => { + // FIXME: Diagnose + FormatTrait::Display + } + }; + + let precision_span = format.precision_span.and_then(to_span); + let precision = match format.precision { + parse::CountIs(n) => Some(FormatCount::Literal(n)), + parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg( + ArgRef::Name(name, to_span(name_span)), + precision_span, + Precision, + FormatArgPositionKind::Named, + ))), + parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg( + ArgRef::Index(i), + precision_span, + Precision, + FormatArgPositionKind::Number, + ))), + parse::CountIsStar(i) => Some(FormatCount::Argument(lookup_arg( + ArgRef::Index(i), + precision_span, + Precision, + FormatArgPositionKind::Implicit, + ))), + parse::CountImplied => None, + }; + + let width_span = format.width_span.and_then(to_span); + let width = match format.width { + parse::CountIs(n) => Some(FormatCount::Literal(n)), + parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg( + ArgRef::Name(name, to_span(name_span)), + width_span, + Width, + FormatArgPositionKind::Named, + ))), + parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg( + ArgRef::Index(i), + width_span, + Width, + FormatArgPositionKind::Number, + ))), + parse::CountIsStar(_) => unreachable!(), + parse::CountImplied => None, + }; + + template.push(FormatArgsPiece::Placeholder(FormatPlaceholder { + argument, + span, + format_trait, + format_options: FormatOptions { + fill: format.fill, + alignment, + sign: format.sign.map(|s| match s { + parse::Sign::Plus => FormatSign::Plus, + parse::Sign::Minus => FormatSign::Minus, + }), + alternate: format.alternate, + zero_pad: format.zero_pad, + debug_hex: format.debug_hex.map(|s| match s { + parse::DebugHex::Lower => FormatDebugHex::Lower, + parse::DebugHex::Upper => FormatDebugHex::Upper, + }), + precision, + width, + }, + })); + } + } + } + + if !unfinished_literal.is_empty() { + template.push(FormatArgsPiece::Literal(unfinished_literal.into_boxed_str())); + } + + if !invalid_refs.is_empty() { + // FIXME: Diagnose + } + + let unused = used + .iter() + .enumerate() + .filter(|&(_, used)| !used) + .map(|(i, _)| { + let named = matches!(args.explicit_args()[i].kind, FormatArgumentKind::Named(_)); + (args.explicit_args()[i].expr, named) + }) + .collect::<Vec<_>>(); + + if !unused.is_empty() { + // FIXME: Diagnose + } + + FormatArgs { template: template.into_boxed_slice(), arguments: args.finish() } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FormatArgumentsCollector { + arguments: Vec<FormatArgument>, + num_unnamed_args: usize, + num_explicit_args: usize, + names: Vec<(Name, usize)>, +} + +impl FormatArgumentsCollector { + pub(crate) fn finish(self) -> FormatArguments { + FormatArguments { + arguments: self.arguments.into_boxed_slice(), + num_unnamed_args: self.num_unnamed_args, + num_explicit_args: self.num_explicit_args, + names: self.names.into_boxed_slice(), + } + } + + pub fn new() -> Self { + Self { arguments: vec![], names: vec![], num_unnamed_args: 0, num_explicit_args: 0 } + } + + pub fn add(&mut self, arg: FormatArgument) -> usize { + let index = self.arguments.len(); + if let Some(name) = arg.kind.ident() { + self.names.push((name.clone(), index)); + } else if self.names.is_empty() { + // Only count the unnamed args before the first named arg. + // (Any later ones are errors.) + self.num_unnamed_args += 1; + } + if !matches!(arg.kind, FormatArgumentKind::Captured(..)) { + // This is an explicit argument. + // Make sure that all arguments so far are explicit. + assert_eq!( + self.num_explicit_args, + self.arguments.len(), + "captured arguments must be added last" + ); + self.num_explicit_args += 1; + } + self.arguments.push(arg); + index + } + + pub fn by_name(&self, name: &Name) -> Option<(usize, &FormatArgument)> { + let &(_, i) = self.names.iter().find(|(n, _)| n == name)?; + Some((i, &self.arguments[i])) + } + + pub fn by_index(&self, i: usize) -> Option<&FormatArgument> { + (i < self.num_explicit_args).then(|| &self.arguments[i]) + } + + pub fn unnamed_args(&self) -> &[FormatArgument] { + &self.arguments[..self.num_unnamed_args] + } + + pub fn named_args(&self) -> &[FormatArgument] { + &self.arguments[self.num_unnamed_args..self.num_explicit_args] + } + + pub fn explicit_args(&self) -> &[FormatArgument] { + &self.arguments[..self.num_explicit_args] + } + + pub fn all_args(&self) -> &[FormatArgument] { + &self.arguments[..] + } + + pub fn all_args_mut(&mut self) -> &mut Vec<FormatArgument> { + &mut self.arguments + } +} + +impl FormatArgumentKind { + pub fn ident(&self) -> Option<&Name> { + match self { + Self::Normal => None, + Self::Named(id) => Some(id), + Self::Captured(id) => Some(id), + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index 6038caaf8fc..44b7f1b4f63 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -32,6 +32,8 @@ pub struct ImportInfo { pub is_trait_assoc_item: bool, /// Whether this item is annotated with `#[doc(hidden)]`. pub is_doc_hidden: bool, + /// Whether this item is annotated with `#[unstable(..)]`. + pub is_unstable: bool, } /// A map from publicly exported items to its name. @@ -113,7 +115,6 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn for (name, per_ns) in visible_items { for (item, import) in per_ns.iter_items() { - // FIXME: Not yet used, but will be once we handle doc(hidden) import sources let attr_id = if let Some(import) = import { match import { ImportOrExternCrate::ExternCrate(id) => Some(id.into()), @@ -125,28 +126,59 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn ItemInNs::Macros(id) => Some(id.into()), } }; - let is_doc_hidden = - attr_id.map_or(false, |attr_id| db.attrs(attr_id).has_doc_hidden()); + let status @ (is_doc_hidden, is_unstable) = + attr_id.map_or((false, false), |attr_id| { + let attrs = db.attrs(attr_id); + (attrs.has_doc_hidden(), attrs.is_unstable()) + }); let import_info = ImportInfo { name: name.clone(), container: module, is_trait_assoc_item: false, is_doc_hidden, + is_unstable, }; match depth_map.entry(item) { - Entry::Vacant(entry) => _ = entry.insert((depth, is_doc_hidden)), + Entry::Vacant(entry) => _ = entry.insert((depth, status)), Entry::Occupied(mut entry) => { - let &(occ_depth, occ_is_doc_hidden) = entry.get(); - // Prefer the one that is not doc(hidden), - // Otherwise, if both have the same doc(hidden)-ness and the new path is shorter, prefer that one. - let overwrite_entry = occ_is_doc_hidden && !is_doc_hidden - || occ_is_doc_hidden == is_doc_hidden && depth < occ_depth; - if !overwrite_entry { + let &(occ_depth, (occ_is_doc_hidden, occ_is_unstable)) = entry.get(); + (depth, occ_depth); + let overwrite = match ( + is_doc_hidden, + occ_is_doc_hidden, + is_unstable, + occ_is_unstable, + ) { + // no change of hiddeness or unstableness + (true, true, true, true) + | (true, true, false, false) + | (false, false, true, true) + | (false, false, false, false) => depth < occ_depth, + + // either less hidden or less unstable, accept + (true, true, false, true) + | (false, true, true, true) + | (false, true, false, true) + | (false, true, false, false) + | (false, false, false, true) => true, + // more hidden or unstable, discard + (true, true, true, false) + | (true, false, true, true) + | (true, false, true, false) + | (true, false, false, false) + | (false, false, true, false) => false, + + // exchanges doc(hidden) for unstable (and vice-versa), + (true, false, false, true) | (false, true, true, false) => { + depth < occ_depth + } + }; + if !overwrite { continue; } - entry.insert((depth, is_doc_hidden)); + entry.insert((depth, status)); } } @@ -171,7 +203,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn } } } - + map.shrink_to_fit(); map } @@ -200,11 +232,13 @@ fn collect_trait_assoc_items( ItemInNs::Values(module_def_id) }; + let attrs = &db.attrs(item.into()); let assoc_item_info = ImportInfo { container: trait_import_info.container, name: assoc_item_name.clone(), is_trait_assoc_item: true, - is_doc_hidden: db.attrs(item.into()).has_doc_hidden(), + is_doc_hidden: attrs.has_doc_hidden(), + is_unstable: attrs.is_unstable(), }; map.insert(assoc_item, assoc_item_info); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 3e1922750b9..4c812b62a46 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -177,7 +177,7 @@ impl ItemTree { } pub fn pretty_print(&self, db: &dyn DefDatabase) -> String { - pretty::print_item_tree(db.upcast(), self) + pretty::print_item_tree(db, self) } fn data(&self) -> &ItemTreeData { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 4b852dd613e..417bd37c8a9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -2,8 +2,6 @@ use std::fmt::{self, Write}; -use hir_expand::db::ExpandDatabase; - use crate::{ generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, pretty::{print_path, print_type_bounds, print_type_ref}, @@ -12,7 +10,7 @@ use crate::{ use super::*; -pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String { +pub(super) fn print_item_tree(db: &dyn DefDatabase, tree: &ItemTree) -> String { let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true }; if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { @@ -45,7 +43,7 @@ macro_rules! wln { } struct Printer<'a> { - db: &'a dyn ExpandDatabase, + db: &'a dyn DefDatabase, tree: &'a ItemTree, buf: String, indent_level: usize, @@ -91,7 +89,7 @@ impl Printer<'_> { self, "#{}[{}{}]{}", inner, - attr.path.display(self.db), + attr.path.display(self.db.upcast()), attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), separated_by, ); @@ -106,7 +104,7 @@ impl Printer<'_> { fn print_visibility(&mut self, vis: RawVisibilityId) { match &self.tree[vis] { - RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)), + RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db.upcast())), RawVisibility::Public => w!(self, "pub "), }; } @@ -121,7 +119,7 @@ impl Printer<'_> { let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; this.print_attrs_of(field, "\n"); this.print_visibility(*visibility); - w!(this, "{}: ", name.display(self.db)); + w!(this, "{}: ", name.display(self.db.upcast())); this.print_type_ref(type_ref); wln!(this, ","); } @@ -135,7 +133,7 @@ impl Printer<'_> { let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; this.print_attrs_of(field, "\n"); this.print_visibility(*visibility); - w!(this, "{}: ", name.display(self.db)); + w!(this, "{}: ", name.display(self.db.upcast())); this.print_type_ref(type_ref); wln!(this, ","); } @@ -168,20 +166,20 @@ impl Printer<'_> { fn print_use_tree(&mut self, use_tree: &UseTree) { match &use_tree.kind { UseTreeKind::Single { path, alias } => { - w!(self, "{}", path.display(self.db)); + w!(self, "{}", path.display(self.db.upcast())); if let Some(alias) = alias { w!(self, " as {}", alias); } } UseTreeKind::Glob { path } => { if let Some(path) = path { - w!(self, "{}::", path.display(self.db)); + w!(self, "{}::", path.display(self.db.upcast())); } w!(self, "*"); } UseTreeKind::Prefixed { prefix, list } => { if let Some(prefix) = prefix { - w!(self, "{}::", prefix.display(self.db)); + w!(self, "{}::", prefix.display(self.db.upcast())); } w!(self, "{{"); for (i, tree) in list.iter().enumerate() { @@ -209,7 +207,7 @@ impl Printer<'_> { ModItem::ExternCrate(it) => { let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "extern crate {}", name.display(self.db)); + w!(self, "extern crate {}", name.display(self.db.upcast())); if let Some(alias) = alias { w!(self, " as {}", alias); } @@ -256,7 +254,7 @@ impl Printer<'_> { if let Some(abi) = abi { w!(self, "extern \"{}\" ", abi); } - w!(self, "fn {}", name.display(self.db)); + w!(self, "fn {}", name.display(self.db.upcast())); self.print_generic_params(explicit_generic_params); w!(self, "("); if !params.is_empty() { @@ -290,7 +288,7 @@ impl Printer<'_> { ModItem::Struct(it) => { let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "struct {}", name.display(self.db)); + w!(self, "struct {}", name.display(self.db.upcast())); self.print_generic_params(generic_params); self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { @@ -302,7 +300,7 @@ impl Printer<'_> { ModItem::Union(it) => { let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "union {}", name.display(self.db)); + w!(self, "union {}", name.display(self.db.upcast())); self.print_generic_params(generic_params); self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { @@ -314,14 +312,14 @@ impl Printer<'_> { ModItem::Enum(it) => { let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "enum {}", name.display(self.db)); + w!(self, "enum {}", name.display(self.db.upcast())); self.print_generic_params(generic_params); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { for variant in variants.clone() { let Variant { name, fields, ast_id: _ } = &this.tree[variant]; this.print_attrs_of(variant, "\n"); - w!(this, "{}", name.display(self.db)); + w!(this, "{}", name.display(self.db.upcast())); this.print_fields(fields); wln!(this, ","); } @@ -333,7 +331,7 @@ impl Printer<'_> { self.print_visibility(*visibility); w!(self, "const "); match name { - Some(name) => w!(self, "{}", name.display(self.db)), + Some(name) => w!(self, "{}", name.display(self.db.upcast())), None => w!(self, "_"), } w!(self, ": "); @@ -347,7 +345,7 @@ impl Printer<'_> { if *mutable { w!(self, "mut "); } - w!(self, "{}: ", name.display(self.db)); + w!(self, "{}: ", name.display(self.db.upcast())); self.print_type_ref(type_ref); w!(self, " = _;"); wln!(self); @@ -369,7 +367,7 @@ impl Printer<'_> { if *is_auto { w!(self, "auto "); } - w!(self, "trait {}", name.display(self.db)); + w!(self, "trait {}", name.display(self.db.upcast())); self.print_generic_params(generic_params); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { @@ -382,7 +380,7 @@ impl Printer<'_> { ModItem::TraitAlias(it) => { let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "trait {}", name.display(self.db)); + w!(self, "trait {}", name.display(self.db.upcast())); self.print_generic_params(generic_params); w!(self, " = "); self.print_where_clause(generic_params); @@ -415,7 +413,7 @@ impl Printer<'_> { let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "type {}", name.display(self.db)); + w!(self, "type {}", name.display(self.db.upcast())); self.print_generic_params(generic_params); if !bounds.is_empty() { w!(self, ": "); @@ -432,7 +430,7 @@ impl Printer<'_> { ModItem::Mod(it) => { let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "mod {}", name.display(self.db)); + w!(self, "mod {}", name.display(self.db.upcast())); match kind { ModKind::Inline { items } => { w!(self, " {{"); @@ -450,16 +448,16 @@ impl Printer<'_> { } ModItem::MacroCall(it) => { let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it]; - wln!(self, "{}!(...);", path.display(self.db)); + wln!(self, "{}!(...);", path.display(self.db.upcast())); } ModItem::MacroRules(it) => { let MacroRules { name, ast_id: _ } = &self.tree[it]; - wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db)); + wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db.upcast())); } ModItem::MacroDef(it) => { let MacroDef { name, visibility, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - wln!(self, "macro {} {{ ... }}", name.display(self.db)); + wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast())); } } @@ -491,7 +489,7 @@ impl Printer<'_> { } first = false; self.print_attrs_of(idx, " "); - w!(self, "{}", lt.name.display(self.db)); + w!(self, "{}", lt.name.display(self.db.upcast())); } for (idx, x) in params.type_or_consts.iter() { if !first { @@ -501,11 +499,11 @@ impl Printer<'_> { self.print_attrs_of(idx, " "); match x { TypeOrConstParamData::TypeParamData(ty) => match &ty.name { - Some(name) => w!(self, "{}", name.display(self.db)), + Some(name) => w!(self, "{}", name.display(self.db.upcast())), None => w!(self, "_anon_{}", idx.into_raw()), }, TypeOrConstParamData::ConstParamData(konst) => { - w!(self, "const {}: ", konst.name.display(self.db)); + w!(self, "const {}: ", konst.name.display(self.db.upcast())); self.print_type_ref(&konst.ty); } } @@ -540,8 +538,8 @@ impl Printer<'_> { wln!( this, "{}: {},", - target.name.display(self.db), - bound.name.display(self.db) + target.name.display(self.db.upcast()), + bound.name.display(self.db.upcast()) ); continue; } @@ -551,7 +549,7 @@ impl Printer<'_> { if i != 0 { w!(this, ", "); } - w!(this, "{}", lt.display(self.db)); + w!(this, "{}", lt.display(self.db.upcast())); } w!(this, "> "); (target, bound) @@ -562,7 +560,7 @@ impl Printer<'_> { WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty), WherePredicateTypeTarget::TypeOrConstParam(id) => { match ¶ms.type_or_consts[*id].name() { - Some(name) => w!(this, "{}", name.display(self.db)), + Some(name) => w!(this, "{}", name.display(self.db.upcast())), None => w!(this, "_anon_{}", id.into_raw()), } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 627479bb7c1..1ae6bd4c919 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -2,6 +2,7 @@ //! //! This attribute to tell the compiler about semi built-in std library //! features, such as Fn family of traits. +use hir_expand::name::Name; use rustc_hash::FxHashMap; use syntax::SmolStr; use triomphe::Arc; @@ -238,7 +239,17 @@ impl LangItem { pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> { let t = db.lang_item(start_crate, *self)?; - Some(Path::LangItem(t)) + Some(Path::LangItem(t, None)) + } + + pub fn ty_rel_path( + &self, + db: &dyn DefDatabase, + start_crate: CrateId, + seg: Name, + ) -> Option<Path> { + let t = db.lang_item(start_crate, *self)?; + Some(Path::LangItem(t, Some(seg))) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs index e523c229179..52781d98892 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs @@ -1,10 +1,11 @@ //! Context for lowering paths. +use std::cell::OnceCell; + use hir_expand::{ ast_id_map::{AstIdMap, AstIdNode}, hygiene::Hygiene, AstId, HirFileId, InFile, }; -use once_cell::unsync::OnceCell; use syntax::ast; use triomphe::Arc; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index b232651db96..4aedb22c6bc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -23,6 +23,45 @@ fn main() { 0 as u32; } } #[test] +fn test_asm_expand() { + check( + r#" +#[rustc_builtin_macro] +macro_rules! asm {() => {}} + +fn main() { + let i: u64 = 3; + let o: u64; + unsafe { + asm!( + "mov {0}, {1}", + "add {0}, 5", + out(reg) o, + in(reg) i, + ); + } +} +"#, + expect![[r##" +#[rustc_builtin_macro] +macro_rules! asm {() => {}} + +fn main() { + let i: u64 = 3; + let o: u64; + unsafe { + builtin #asm ( { + $crate::format_args!("mov {0}, {1}"); + $crate::format_args!("add {0}, 5"); + } + ); + } +} +"##]], + ); +} + +#[test] fn test_line_expand() { check( r#" @@ -201,7 +240,7 @@ macro_rules! format_args { } fn main() { - ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(arg2), ::core::fmt::Debug::fmt), ]); + builtin #format_args ("{} {:?}", arg1(a, b, c), arg2); } "##]], ); @@ -219,10 +258,10 @@ macro_rules! format_args { fn main() { format_args!(x = 2); - format_args!(x =); - format_args!(x =, x = 2); - format_args!("{}", x =); - format_args!(=, "{}", x =); + format_args!/*+errors*/(x =); + format_args!/*+errors*/(x =, x = 2); + format_args!/*+errors*/("{}", x =); + format_args!/*+errors*/(=, "{}", x =); format_args!(x = 2, "{}", 5); } "#, @@ -234,12 +273,19 @@ macro_rules! format_args { } fn main() { - /* error: no rule matches input tokens */; - /* error: expected expression */; - /* error: expected expression, expected COMMA */; - /* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]); - /* error: expected expression, expected R_PAREN */; - ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]); + builtin #format_args (x = 2); + /* parse error: expected expression */ +builtin #format_args (x = ); + /* parse error: expected expression */ +/* parse error: expected R_PAREN */ +/* parse error: expected expression, item or let statement */ +builtin #format_args (x = , x = 2); + /* parse error: expected expression */ +builtin #format_args ("{}", x = ); + /* parse error: expected expression */ +/* parse error: expected expression */ +builtin #format_args ( = , "{}", x = ); + builtin #format_args (x = 2, "{}", 5); } "##]], ); @@ -267,7 +313,7 @@ macro_rules! format_args { } fn main() { - ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a::<A, B>()), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]); + builtin #format_args ("{} {:?}", a::<A, B>(), b); } "##]], ); @@ -300,7 +346,7 @@ macro_rules! format_args { } fn main() { - ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]); + builtin #format_args (r#"{},mismatch,"{}","{}""#, location_csv_pat(db, &analysis, vfs, &sm, pat_id), mismatch.expected.display(db), mismatch.actual.display(db)); } "##]], ); @@ -334,7 +380,7 @@ macro_rules! format_args { } fn main() { - ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::ArgumentV1::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]); + builtin #format_args (concat!("xxx{}y", "{:?}zzz"), 2, b); } "##]], ); @@ -364,8 +410,8 @@ macro_rules! format_args { fn main() { let _ = - /* error: expected field name or number *//* parse error: expected field name or number */ -::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(), ::core::fmt::Debug::fmt), ]); + /* parse error: expected field name or number */ +builtin #format_args ("{} {:?}", a.); } "##]], ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index 2170cadcf83..d0906213243 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -117,7 +117,7 @@ fn main(foo: ()) { macro_rules! format_args {} fn main(foo: ()) { - /* error: unresolved macro identity */::core::fmt::Arguments::new_v1(&["", " ", " ", ], &[::core::fmt::ArgumentV1::new(&(::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(0), ::core::fmt::Display::fmt), ])), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(foo), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(identity!(10)), ::core::fmt::Display::fmt), ]) + builtin #format_args ("{} {} {}", format_args!("{}", 0), foo, identity!(10), "bar") } "##]], ); @@ -150,8 +150,8 @@ macro_rules! identity { } fn main(foo: ()) { - // format_args/*+tokenids*/!("{} {} {}"#1,#3 format_args!("{}", 0#10),#12 foo#13,#14 identity!(10#18),#21 "bar"#22) -::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(�[#4294967295""#4294967295,#4294967295 " "#4294967295,#4294967295 " "#4294967295,#4294967295 ]#4294967295,#4294967295 �[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(�[#4294967295""#4294967295,#4294967295 ]#4294967295,#4294967295 �[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#42949672950#10)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#4294967295foo#13)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(�(#429496729510#18)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295 + // format_args/*+tokenids*/!("{} {} {}"#1,#2 format_args#3!#4("{}"#6,#7 0#8),#9 foo#10,#11 identity#12!#13(10#15),#16 "bar"#17) +builtin#4294967295 ##4294967295format_args#4294967295 (#0"{} {} {}"#1,#2 format_args#3!#4(#5"{}"#6,#7 0#8)#5,#9 foo#10,#11 identity#12!#13(#1410#15)#14,#16 "bar"#17)#0 } "##]], diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index 97554f93f1c..b416f45ff20 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -929,8 +929,8 @@ fn main() { macro_rules! format_args {} fn main() { - /* error: expected field name or number *//* parse error: expected field name or number */ -::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(line.1.), ::core::fmt::Display::fmt), ]); + /* parse error: expected field name or number */ +builtin #format_args ("{}", line.1.); } "##]], @@ -956,19 +956,15 @@ fn main() { macro_rules! format_args {} fn main() { - /* error: expected COMMA, expected R_BRACK, expected COMMA, expected COMMA, expected expression, expected R_PAREN *//* parse error: expected COMMA */ + /* parse error: expected COMMA */ /* parse error: expected R_BRACK */ /* parse error: expected COMMA */ /* parse error: expected COMMA */ /* parse error: expected expression */ /* parse error: expected R_PAREN */ -/* parse error: expected R_PAREN */ -/* parse error: expected expression, item or let statement */ -/* parse error: expected expression, item or let statement */ -/* parse error: expected expression, item or let statement */ /* parse error: expected expression, item or let statement */ /* parse error: expected expression, item or let statement */ -::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(&[0 2]), ::core::fmt::Display::fmt), ]); +builtin #format_args ("{}", &[0 2]); } "##]], diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index f2110410980..9a9fa0e02b0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -93,7 +93,7 @@ use crate::{ #[derive(Debug, PartialEq, Eq)] pub struct DefMap { _c: Count<Self>, - /// When this is a block def map, this will hold the block id of the the block and module that + /// When this is a block def map, this will hold the block id of the block and module that /// contains this block. block: Option<BlockInfo>, /// The modules and their data declared in this crate. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index e9e71a8747f..2d4586146db 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -1828,7 +1828,11 @@ impl ModCollector<'_, '_> { let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else { // `#[macro_use]` (without any paths) found, forget collected names and just import // all visible macros. - self.def_collector.import_macros_from_extern_crate(target_crate, None, Some(extern_crate_id)); + self.def_collector.import_macros_from_extern_crate( + target_crate, + None, + Some(extern_crate_id), + ); return; }; for path in paths { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs index 06530cc7ebd..3894172a5ad 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs @@ -47,7 +47,7 @@ pub enum Path { }, /// A link to a lang item. It is used in desugaring of things like `it?`. We can show these /// links via a normal path since they might be private and not accessible in the usage place. - LangItem(LangItemTarget), + LangItem(LangItemTarget, Option<Name>), } /// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This @@ -122,33 +122,40 @@ impl Path { pub fn kind(&self) -> &PathKind { match self { Path::Normal { mod_path, .. } => &mod_path.kind, - Path::LangItem(_) => &PathKind::Abs, + Path::LangItem(..) => &PathKind::Abs, } } pub fn type_anchor(&self) -> Option<&TypeRef> { match self { Path::Normal { type_anchor, .. } => type_anchor.as_deref(), - Path::LangItem(_) => None, + Path::LangItem(..) => None, } } pub fn segments(&self) -> PathSegments<'_> { - let Path::Normal { mod_path, generic_args, .. } = self else { - return PathSegments { segments: &[], generic_args: None }; - }; - let s = - PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() }; - if let Some(generic_args) = s.generic_args { - assert_eq!(s.segments.len(), generic_args.len()); + match self { + Path::Normal { mod_path, generic_args, .. } => { + let s = PathSegments { + segments: mod_path.segments(), + generic_args: generic_args.as_deref(), + }; + if let Some(generic_args) = s.generic_args { + assert_eq!(s.segments.len(), generic_args.len()); + } + s + } + Path::LangItem(_, seg) => PathSegments { + segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)), + generic_args: None, + }, } - s } pub fn mod_path(&self) -> Option<&ModPath> { match self { Path::Normal { mod_path, .. } => Some(&mod_path), - Path::LangItem(_) => None, + Path::LangItem(..) => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs index 11d58a6ba09..f4f5541e373 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs @@ -2,18 +2,54 @@ use std::fmt::{self, Write}; -use hir_expand::{db::ExpandDatabase, mod_path::PathKind}; +use hir_expand::mod_path::PathKind; use intern::Interned; use itertools::Itertools; use crate::{ + db::DefDatabase, + lang_item::LangItemTarget, path::{GenericArg, GenericArgs, Path}, type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef}, }; -pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result { - if let Path::LangItem(it) = path { - return write!(buf, "$lang_item::{it:?}"); +pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result { + if let Path::LangItem(it, s) = path { + write!(buf, "builtin#lang(")?; + match *it { + LangItemTarget::ImplDef(it) => write!(buf, "{it:?}")?, + LangItemTarget::EnumId(it) => { + write!(buf, "{}", db.enum_data(it).name.display(db.upcast()))? + } + LangItemTarget::Function(it) => { + write!(buf, "{}", db.function_data(it).name.display(db.upcast()))? + } + LangItemTarget::Static(it) => { + write!(buf, "{}", db.static_data(it).name.display(db.upcast()))? + } + LangItemTarget::Struct(it) => { + write!(buf, "{}", db.struct_data(it).name.display(db.upcast()))? + } + LangItemTarget::Union(it) => { + write!(buf, "{}", db.union_data(it).name.display(db.upcast()))? + } + LangItemTarget::TypeAlias(it) => { + write!(buf, "{}", db.type_alias_data(it).name.display(db.upcast()))? + } + LangItemTarget::Trait(it) => { + write!(buf, "{}", db.trait_data(it).name.display(db.upcast()))? + } + LangItemTarget::EnumVariant(it) => write!( + buf, + "{}", + db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()) + )?, + } + + if let Some(s) = s { + write!(buf, "::{}", s.display(db.upcast()))?; + } + return write!(buf, ")"); } match path.type_anchor() { Some(anchor) => { @@ -44,7 +80,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri write!(buf, "::")?; } - write!(buf, "{}", segment.name.display(db))?; + write!(buf, "{}", segment.name.display(db.upcast()))?; if let Some(generics) = segment.args_and_bindings { write!(buf, "::<")?; print_generic_args(db, generics, buf)?; @@ -57,7 +93,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri } pub(crate) fn print_generic_args( - db: &dyn ExpandDatabase, + db: &dyn DefDatabase, generics: &GenericArgs, buf: &mut dyn Write, ) -> fmt::Result { @@ -83,7 +119,7 @@ pub(crate) fn print_generic_args( write!(buf, ", ")?; } first = false; - write!(buf, "{}", binding.name.display(db))?; + write!(buf, "{}", binding.name.display(db.upcast()))?; if !binding.bounds.is_empty() { write!(buf, ": ")?; print_type_bounds(db, &binding.bounds, buf)?; @@ -97,19 +133,19 @@ pub(crate) fn print_generic_args( } pub(crate) fn print_generic_arg( - db: &dyn ExpandDatabase, + db: &dyn DefDatabase, arg: &GenericArg, buf: &mut dyn Write, ) -> fmt::Result { match arg { GenericArg::Type(ty) => print_type_ref(db, ty, buf), - GenericArg::Const(c) => write!(buf, "{}", c.display(db)), - GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)), + GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast())), + GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast())), } } pub(crate) fn print_type_ref( - db: &dyn ExpandDatabase, + db: &dyn DefDatabase, type_ref: &TypeRef, buf: &mut dyn Write, ) -> fmt::Result { @@ -143,7 +179,7 @@ pub(crate) fn print_type_ref( }; write!(buf, "&")?; if let Some(lt) = lt { - write!(buf, "{} ", lt.name.display(db))?; + write!(buf, "{} ", lt.name.display(db.upcast()))?; } write!(buf, "{mtbl}")?; print_type_ref(db, pointee, buf)?; @@ -151,7 +187,7 @@ pub(crate) fn print_type_ref( TypeRef::Array(elem, len) => { write!(buf, "[")?; print_type_ref(db, elem, buf)?; - write!(buf, "; {}]", len.display(db))?; + write!(buf, "; {}]", len.display(db.upcast()))?; } TypeRef::Slice(elem) => { write!(buf, "[")?; @@ -198,7 +234,7 @@ pub(crate) fn print_type_ref( } pub(crate) fn print_type_bounds( - db: &dyn ExpandDatabase, + db: &dyn DefDatabase, bounds: &[Interned<TypeBound>], buf: &mut dyn Write, ) -> fmt::Result { @@ -216,10 +252,14 @@ pub(crate) fn print_type_bounds( print_path(db, path, buf)?; } TypeBound::ForLifetime(lifetimes, path) => { - write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?; + write!( + buf, + "for<{}> ", + lifetimes.iter().map(|it| it.display(db.upcast())).format(", ") + )?; print_path(db, path, buf)?; } - TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?, + TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast()))?, TypeBound::Error => write!(buf, "{{unknown}}")?, } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 2f9187009e2..50da9ed06a0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -156,22 +156,19 @@ impl Resolver { ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> { let path = match path { Path::Normal { mod_path, .. } => mod_path, - Path::LangItem(l) => { - return Some(( - match *l { - LangItemTarget::Union(it) => TypeNs::AdtId(it.into()), - LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it), - LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()), - LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it), - LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()), - LangItemTarget::Trait(it) => TypeNs::TraitId(it), - LangItemTarget::Function(_) - | LangItemTarget::ImplDef(_) - | LangItemTarget::Static(_) => return None, - }, - None, - None, - )) + Path::LangItem(l, seg) => { + let type_ns = match *l { + LangItemTarget::Union(it) => TypeNs::AdtId(it.into()), + LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it), + LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()), + LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it), + LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()), + LangItemTarget::Trait(it) => TypeNs::TraitId(it), + LangItemTarget::Function(_) + | LangItemTarget::ImplDef(_) + | LangItemTarget::Static(_) => return None, + }; + return Some((type_ns, seg.as_ref().map(|_| 1), None)); } }; let first_name = path.segments().first()?; @@ -256,7 +253,7 @@ impl Resolver { ) -> Option<ResolveValueResult> { let path = match path { Path::Normal { mod_path, .. } => mod_path, - Path::LangItem(l) => { + Path::LangItem(l, None) => { return Some(ResolveValueResult::ValueNs( match *l { LangItemTarget::Function(it) => ValueNs::FunctionId(it), @@ -272,6 +269,20 @@ impl Resolver { None, )) } + Path::LangItem(l, Some(_)) => { + let type_ns = match *l { + LangItemTarget::Union(it) => TypeNs::AdtId(it.into()), + LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it), + LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()), + LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it), + LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()), + LangItemTarget::Trait(it) => TypeNs::TraitId(it), + LangItemTarget::Function(_) + | LangItemTarget::ImplDef(_) + | LangItemTarget::Static(_) => return None, + }; + return Some(ResolveValueResult::Partial(type_ns, 1, None)); + } }; let n_segments = path.segments().len(); let tmp = name![self]; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index 95c6baf42da..30b19b6e51b 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -1,13 +1,9 @@ //! Builtin macro -use std::mem; - -use ::tt::Ident; use base_db::{AnchoredPath, Edition, FileId}; use cfg::CfgExpr; use either::Either; use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap}; -use rustc_hash::FxHashMap; use syntax::{ ast::{self, AstToken}, SmolStr, @@ -97,11 +93,11 @@ register_builtin! { (unreachable, Unreachable) => unreachable_expand, (log_syntax, LogSyntax) => log_syntax_expand, (trace_macros, TraceMacros) => trace_macros_expand, - - EAGER: (format_args, FormatArgs) => format_args_expand, (const_format_args, ConstFormatArgs) => format_args_expand, (format_args_nl, FormatArgsNl) => format_args_nl_expand, + + EAGER: (compile_error, CompileError) => compile_error_expand, (concat, Concat) => concat_expand, (concat_idents, ConcatIdents) => concat_idents_expand, @@ -247,151 +243,15 @@ fn format_args_expand_general( _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, - end_string: &str, + // FIXME: Make use of this so that mir interpretation works properly + _end_string: &str, ) -> ExpandResult<tt::Subtree> { - let args = parse_exprs_with_sep(tt, ','); - - let expand_error = - ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into()); - - let mut key_args = FxHashMap::default(); - let mut args = args.into_iter().filter_map(|mut arg| { - // Remove `key =`. - if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') - { - // but not with `==` - if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') - { - let key = arg.token_trees.drain(..2).next().unwrap(); - key_args.insert(key.to_string(), arg); - return None; - } - } - Some(arg) - }).collect::<Vec<_>>().into_iter(); - // ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`) - let Some(format_subtree) = args.next() else { - return expand_error; - }; - let format_string = (|| { - let token_tree = format_subtree.token_trees.get(0)?; - match token_tree { - tt::TokenTree::Leaf(l) => match l { - tt::Leaf::Literal(l) => { - if let Some(mut text) = l.text.strip_prefix('r') { - let mut raw_sharps = String::new(); - while let Some(t) = text.strip_prefix('#') { - text = t; - raw_sharps.push('#'); - } - text = - text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?; - Some((text, l.span, Some(raw_sharps))) - } else { - let text = l.text.strip_prefix('"')?.strip_suffix('"')?; - let span = l.span; - Some((text, span, None)) - } - } - _ => None, - }, - tt::TokenTree::Subtree(_) => None, - } - })(); - let Some((format_string, _format_string_span, raw_sharps)) = format_string else { - return expand_error; - }; - let mut format_iter = format_string.chars().peekable(); - let mut parts = vec![]; - let mut last_part = String::new(); - let mut arg_tts = vec![]; - let mut err = None; - while let Some(c) = format_iter.next() { - // Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info - match c { - '{' => { - if format_iter.peek() == Some(&'{') { - format_iter.next(); - last_part.push('{'); - continue; - } - let mut argument = String::new(); - while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) { - argument.push(match format_iter.next() { - Some(c) => c, - None => return expand_error, - }); - } - let format_spec = match format_iter.next().unwrap() { - '}' => "".to_owned(), - ':' => { - let mut s = String::new(); - while let Some(c) = format_iter.next() { - if c == '}' { - break; - } - s.push(c); - } - s - } - _ => unreachable!(), - }; - parts.push(mem::take(&mut last_part)); - let arg_tree = if argument.is_empty() { - match args.next() { - Some(it) => it, - None => { - err = Some(mbe::ExpandError::NoMatchingRule.into()); - tt::Subtree::empty() - } - } - } else if let Some(tree) = key_args.get(&argument) { - tree.clone() - } else { - // FIXME: we should pick the related substring of the `_format_string_span` as the span. You - // can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval. - let ident = Ident::new(argument, tt::TokenId::unspecified()); - quote!(#ident) - }; - let formatter = match &*format_spec { - "?" => quote!(::core::fmt::Debug::fmt), - "" => quote!(::core::fmt::Display::fmt), - _ => { - // FIXME: implement the rest and return expand error here - quote!(::core::fmt::Display::fmt) - } - }; - arg_tts.push(quote! { ::core::fmt::ArgumentV1::new(&(#arg_tree), #formatter), }); - } - '}' => { - if format_iter.peek() == Some(&'}') { - format_iter.next(); - last_part.push('}'); - } else { - return expand_error; - } - } - _ => last_part.push(c), - } - } - last_part += end_string; - if !last_part.is_empty() { - parts.push(last_part); - } - let part_tts = parts.into_iter().map(|it| { - let text = if let Some(raw) = &raw_sharps { - format!("r{raw}\"{}\"{raw}", it).into() - } else { - format!("\"{}\"", it).into() - }; - let l = tt::Literal { span: tt::TokenId::unspecified(), text }; - quote!(#l ,) + let pound = quote! {@PUNCT '#'}; + let mut tt = tt.clone(); + tt.delimiter.kind = tt::DelimiterKind::Parenthesis; + return ExpandResult::ok(quote! { + builtin #pound format_args #tt }); - let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees); - let expanded = quote! { - ::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts]) - }; - ExpandResult { value: expanded, err } } fn asm_expand( @@ -415,10 +275,12 @@ fn asm_expand( } } - let expanded = quote! {{ - ##literals - loop {} - }}; + let pound = quote! {@PUNCT '#'}; + let expanded = quote! { + builtin #pound asm ( + {##literals} + ) + }; ExpandResult::ok(expanded) } @@ -692,7 +554,7 @@ pub(crate) fn include_arg_to_tt( arg_id: MacroCallId, ) -> Result<(triomphe::Arc<(::tt::Subtree<::tt::TokenId>, TokenMap)>, FileId), ExpandError> { let loc = db.lookup_intern_macro_call(arg_id); - let Some(EagerCallInfo { arg,arg_id, .. }) = loc.eager.as_deref() else { + let Some(EagerCallInfo { arg, arg_id, .. }) = loc.eager.as_deref() else { panic!("include_arg_to_tt called on non include macro call: {:?}", &loc.eager); }; let path = parse_string(&arg.0)?; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs index ade4a592893..ca65db1136c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs @@ -242,7 +242,7 @@ impl HygieneFrame { krate, call_site: None, def_site: None, - } + }; }; let def_site = info.attr_input_or_mac_def_start.map(|it| db.hygiene_frame(it.file_id)); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index 7c179c0cf95..a876f48bda4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -54,6 +54,12 @@ impl Name { Name(Repr::Text(text)) } + // FIXME: See above, unfortunately some places really need this right now + #[doc(hidden)] + pub const fn new_text_dont_use(text: SmolStr) -> Name { + Name(Repr::Text(text)) + } + pub fn new_tuple_field(idx: usize) -> Name { Name(Repr::TupleField(idx)) } @@ -302,6 +308,16 @@ pub mod known { rust_2018, rust_2021, v1, + new_display, + new_debug, + new_lower_exp, + new_upper_exp, + new_octal, + new_pointer, + new_binary, + new_lower_hex, + new_upper_hex, + from_usize, // Components of known path (type name) Iterator, IntoIterator, @@ -327,6 +343,13 @@ pub mod known { Not, None, Index, + Left, + Right, + Center, + Unknown, + Is, + Param, + Implied, // Components of known path (function name) filter_map, next, @@ -335,6 +358,8 @@ pub mod known { is_empty, as_str, new, + new_v1_formatted, + none, // Builtin macros asm, assert, diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index abc19d63abf..b95ae05ccd4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -32,7 +32,8 @@ once_cell = "1.17.0" triomphe.workspace = true nohash-hasher.workspace = true typed-arena = "2.0.1" -rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } + +rustc_index.workspace = true # local deps stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 2855f789001..44a4ac27af0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -499,24 +499,26 @@ fn offset() { r#" //- minicore: coerce_unsized, index, slice extern "rust-intrinsic" { - pub fn offset<T>(dst: *const T, offset: isize) -> *const T; + pub fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr; + pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T; } - const GOAL: u8 = unsafe { - let ar: &[(u8, u8, u8)] = &[ + const GOAL: i32 = unsafe { + let ar: &[(i32, i32, i32)] = &[ (10, 11, 12), (20, 21, 22), (30, 31, 32), (40, 41, 42), (50, 51, 52), ]; - let ar: *const [(u8, u8, u8)] = ar; - let ar = ar as *const (u8, u8, u8); - let element = *offset(ar, 2); - element.1 + let ar: *const [(i32, i32, i32)] = ar; + let ar = ar as *const (i32, i32, i32); + let element3 = *offset(ar, 2usize); + let element4 = *arith_offset(ar, 3); + element3.1 * 100 + element4.0 }; "#, - 31, + 3140, ); } @@ -585,6 +587,24 @@ fn write_bytes() { } #[test] +fn write_via_move() { + check_number( + r#" + extern "rust-intrinsic" { + fn write_via_move<T>(ptr: *mut T, value: T); + } + + const GOAL: i32 = unsafe { + let mut x = 2; + write_via_move(&mut x, 100); + x + }; + "#, + 100, + ); +} + +#[test] fn copy() { check_number( r#" diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index a94a962c1f8..36d69edf9d5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -163,25 +163,56 @@ impl<'a> DeclValidator<'a> { || allows.contains(allow::NONSTANDARD_STYLE) }) }; + let db = self.db.upcast(); + let file_id_is_derive = || { + match id { + AttrDefId::ModuleId(m) => { + m.def_map(db)[m.local_id].origin.file_id().map(Into::into) + } + AttrDefId::FunctionId(f) => Some(f.lookup(db).id.file_id()), + AttrDefId::StaticId(sid) => Some(sid.lookup(db).id.file_id()), + AttrDefId::ConstId(cid) => Some(cid.lookup(db).id.file_id()), + AttrDefId::TraitId(tid) => Some(tid.lookup(db).id.file_id()), + AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).id.file_id()), + AttrDefId::ImplId(iid) => Some(iid.lookup(db).id.file_id()), + AttrDefId::ExternBlockId(id) => Some(id.lookup(db).id.file_id()), + AttrDefId::ExternCrateId(id) => Some(id.lookup(db).id.file_id()), + AttrDefId::UseId(id) => Some(id.lookup(db).id.file_id()), + // These warnings should not explore macro definitions at all + AttrDefId::MacroId(_) => None, + AttrDefId::AdtId(aid) => match aid { + AdtId::StructId(sid) => Some(sid.lookup(db).id.file_id()), + AdtId::EnumId(eid) => Some(eid.lookup(db).id.file_id()), + // Unions aren't yet supported + AdtId::UnionId(_) => None, + }, + AttrDefId::FieldId(_) => None, + AttrDefId::EnumVariantId(_) => None, + AttrDefId::TypeAliasId(_) => None, + AttrDefId::GenericParamId(_) => None, + } + .map_or(false, |file_id| { + file_id.is_custom_derive(db.upcast()) || file_id.is_builtin_derive(db.upcast()) + }) + }; - is_allowed(id) - // go upwards one step or give up - || match id { - AttrDefId::ModuleId(m) => m.containing_module(self.db.upcast()).map(|v| v.into()), - AttrDefId::FunctionId(f) => Some(f.lookup(self.db.upcast()).container.into()), - AttrDefId::StaticId(sid) => Some(sid.lookup(self.db.upcast()).container.into()), - AttrDefId::ConstId(cid) => Some(cid.lookup(self.db.upcast()).container.into()), - AttrDefId::TraitId(tid) => Some(tid.lookup(self.db.upcast()).container.into()), - AttrDefId::TraitAliasId(taid) => Some(taid.lookup(self.db.upcast()).container.into()), - AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()), - AttrDefId::ExternBlockId(id) => Some(id.lookup(self.db.upcast()).container.into()), - AttrDefId::ExternCrateId(id) => Some(id.lookup(self.db.upcast()).container.into()), - AttrDefId::UseId(id) => Some(id.lookup(self.db.upcast()).container.into()), + let parent = || { + match id { + AttrDefId::ModuleId(m) => m.containing_module(db).map(|v| v.into()), + AttrDefId::FunctionId(f) => Some(f.lookup(db).container.into()), + AttrDefId::StaticId(sid) => Some(sid.lookup(db).container.into()), + AttrDefId::ConstId(cid) => Some(cid.lookup(db).container.into()), + AttrDefId::TraitId(tid) => Some(tid.lookup(db).container.into()), + AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).container.into()), + AttrDefId::ImplId(iid) => Some(iid.lookup(db).container.into()), + AttrDefId::ExternBlockId(id) => Some(id.lookup(db).container.into()), + AttrDefId::ExternCrateId(id) => Some(id.lookup(db).container.into()), + AttrDefId::UseId(id) => Some(id.lookup(db).container.into()), // These warnings should not explore macro definitions at all AttrDefId::MacroId(_) => None, AttrDefId::AdtId(aid) => match aid { - AdtId::StructId(sid) => Some(sid.lookup(self.db.upcast()).container.into()), - AdtId::EnumId(eid) => Some(eid.lookup(self.db.upcast()).container.into()), + AdtId::StructId(sid) => Some(sid.lookup(db).container.into()), + AdtId::EnumId(eid) => Some(eid.lookup(db).container.into()), // Unions aren't yet supported AdtId::UnionId(_) => None, }, @@ -191,6 +222,12 @@ impl<'a> DeclValidator<'a> { AttrDefId::GenericParamId(_) => None, } .is_some_and(|mid| self.allowed(mid, allow_name, true)) + }; + is_allowed(id) + // FIXME: this is a hack to avoid false positives in derive macros currently + || file_id_is_derive() + // go upwards one step or give up + || parent() } fn validate_func(&mut self, func: FunctionId) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 0fb4934444b..78d3c667a1f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -194,7 +194,8 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>; #[derive(Debug, PartialEq, Eq, Clone)] pub enum InferenceDiagnostic { NoSuchField { - expr: ExprId, + field: ExprOrPatId, + private: bool, }, PrivateField { expr: ExprId, @@ -228,6 +229,11 @@ pub enum InferenceDiagnostic { expected: usize, found: usize, }, + MismatchedTupleStructPatArgCount { + pat: ExprOrPatId, + expected: usize, + found: usize, + }, ExpectedFunction { call_expr: ExprId, found: Ty, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 9e1c74b16fa..a116d444731 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -39,8 +39,14 @@ impl CastCheck { } fn check_ref_to_ptr_cast(expr_ty: Ty, cast_ty: Ty, table: &mut InferenceTable<'_>) -> bool { - let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else { return false; }; - let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else { return false; }; - let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else { return false; }; + let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else { + return false; + }; + let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else { + return false; + }; + let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else { + return false; + }; table.coerce(expr_elt_ty, cast_inner_ty).is_ok() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 23efe616f4f..13d6b5643ac 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -452,6 +452,8 @@ impl InferenceContext<'_> { fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { match &self.body[tgt_expr] { + Expr::OffsetOf(_) => (), + Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e), Expr::If { condition, then_branch, else_branch } => { self.consume_expr(*condition); self.consume_expr(*then_branch); @@ -467,13 +469,13 @@ impl InferenceContext<'_> { Statement::Let { pat, type_ref: _, initializer, else_branch } => { if let Some(else_branch) = else_branch { self.consume_expr(*else_branch); - if let Some(initializer) = initializer { - self.consume_expr(*initializer); - } - return; } if let Some(initializer) = initializer { - self.walk_expr(*initializer); + if else_branch.is_some() { + self.consume_expr(*initializer); + } else { + self.walk_expr(*initializer); + } if let Some(place) = self.place_of_expr(*initializer) { self.consume_with_pat(place, *pat); } @@ -620,6 +622,7 @@ impl InferenceContext<'_> { | Expr::Tuple { exprs, is_assignee_expr: _ } => { self.consume_exprs(exprs.iter().copied()) } + Expr::Missing | Expr::Continue { .. } | Expr::Path(_) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 8b352141084..0c3c725a7c7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -514,9 +514,6 @@ impl InferenceContext<'_> { } Expr::RecordLit { path, fields, spread, .. } => { let (ty, def_id) = self.resolve_variant(path.as_deref(), false); - if let Some(variant) = def_id { - self.write_variant_resolution(tgt_expr.into(), variant); - } if let Some(t) = expected.only_has_type(&mut self.table) { self.unify(&ty, &t); @@ -526,26 +523,56 @@ impl InferenceContext<'_> { .as_adt() .map(|(_, s)| s.clone()) .unwrap_or_else(|| Substitution::empty(Interner)); - let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default(); - let variant_data = def_id.map(|it| it.variant_data(self.db.upcast())); - for field in fields.iter() { - let field_def = - variant_data.as_ref().and_then(|it| match it.field(&field.name) { - Some(local_id) => Some(FieldId { parent: def_id.unwrap(), local_id }), - None => { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - expr: field.expr, - }); - None - } - }); - let field_ty = field_def.map_or(self.err_ty(), |it| { - field_types[it.local_id].clone().substitute(Interner, &substs) - }); - // Field type might have some unknown types - // FIXME: we may want to emit a single type variable for all instance of type fields? - let field_ty = self.insert_type_vars(field_ty); - self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); + if let Some(variant) = def_id { + self.write_variant_resolution(tgt_expr.into(), variant); + } + match def_id { + _ if fields.is_empty() => {} + Some(def) => { + let field_types = self.db.field_types(def); + let variant_data = def.variant_data(self.db.upcast()); + let visibilities = self.db.field_visibilities(def); + for field in fields.iter() { + let field_def = { + match variant_data.field(&field.name) { + Some(local_id) => { + if !visibilities[local_id].is_visible_from( + self.db.upcast(), + self.resolver.module(), + ) { + self.push_diagnostic( + InferenceDiagnostic::NoSuchField { + field: field.expr.into(), + private: true, + }, + ); + } + Some(local_id) + } + None => { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: field.expr.into(), + private: false, + }); + None + } + } + }; + let field_ty = field_def.map_or(self.err_ty(), |it| { + field_types[it].clone().substitute(Interner, &substs) + }); + + // Field type might have some unknown types + // FIXME: we may want to emit a single type variable for all instance of type fields? + let field_ty = self.insert_type_vars(field_ty); + self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); + } + } + None => { + for field in fields.iter() { + self.infer_expr_coerce(field.expr, &Expectation::None); + } + } } if let Some(expr) = spread { self.infer_expr(*expr, &Expectation::has_type(ty.clone())); @@ -843,6 +870,11 @@ impl InferenceContext<'_> { }); expected } + Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), + Expr::InlineAsm(it) => { + self.infer_expr_no_expect(it.e); + self.result.standard_types.unit.clone() + } }; // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); @@ -1122,7 +1154,7 @@ impl InferenceContext<'_> { Expr::Underscore => rhs_ty.clone(), _ => { // `lhs` is a place expression, a unit struct, or an enum variant. - let lhs_ty = self.infer_expr(lhs, &Expectation::none()); + let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none()); // This is the only branch where this function may coerce any type. // We are returning early to avoid the unifiability check below. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 396ca0044ff..b8a1af96fba 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -35,6 +35,8 @@ impl InferenceContext<'_> { fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) { match &self.body[tgt_expr] { Expr::Missing => (), + Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not), + Expr::OffsetOf(_) => (), &Expr::If { condition, then_branch, else_branch } => { self.infer_mut_expr(condition, Mutability::Not); self.infer_mut_expr(then_branch, Mutability::Not); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 5da0ab76b88..4e28ec06023 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -15,7 +15,8 @@ use crate::{ infer::{BindingMode, Expectation, InferenceContext, TypeMismatch}, lower::lower_to_chalk_mutability, primitive::UintTy, - static_lifetime, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind, + static_lifetime, InferenceDiagnostic, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, + TyKind, }; /// Used to generalize patterns and assignee expressions. @@ -74,29 +75,68 @@ impl InferenceContext<'_> { if let Some(variant) = def { self.write_variant_resolution(id.into(), variant); } + if let Some(var) = &var_data { + let cmp = if ellipsis.is_some() { usize::gt } else { usize::ne }; + + if cmp(&subs.len(), &var.fields().len()) { + self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount { + pat: id.into(), + expected: var.fields().len(), + found: subs.len(), + }); + } + } + self.unify(&ty, expected); let substs = ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner)); - let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); - let (pre, post) = match ellipsis { - Some(idx) => subs.split_at(idx), - None => (subs, &[][..]), - }; - let post_idx_offset = field_tys.iter().count().saturating_sub(post.len()); - - let pre_iter = pre.iter().enumerate(); - let post_iter = (post_idx_offset..).zip(post.iter()); - for (i, &subpat) in pre_iter.chain(post_iter) { - let expected_ty = var_data - .as_ref() - .and_then(|d| d.field(&Name::new_tuple_field(i))) - .map_or(self.err_ty(), |field| { - field_tys[field].clone().substitute(Interner, &substs) - }); - let expected_ty = self.normalize_associated_types_in(expected_ty); - T::infer(self, subpat, &expected_ty, default_bm); + match def { + _ if subs.len() == 0 => {} + Some(def) => { + let field_types = self.db.field_types(def); + let variant_data = def.variant_data(self.db.upcast()); + let visibilities = self.db.field_visibilities(def); + + let (pre, post) = match ellipsis { + Some(idx) => subs.split_at(idx), + None => (subs, &[][..]), + }; + let post_idx_offset = field_types.iter().count().saturating_sub(post.len()); + + let pre_iter = pre.iter().enumerate(); + let post_iter = (post_idx_offset..).zip(post.iter()); + + for (i, &subpat) in pre_iter.chain(post_iter) { + let field_def = { + match variant_data.field(&Name::new_tuple_field(i)) { + Some(local_id) => { + if !visibilities[local_id] + .is_visible_from(self.db.upcast(), self.resolver.module()) + { + // FIXME(DIAGNOSE): private tuple field + } + Some(local_id) + } + None => None, + } + }; + + let expected_ty = field_def.map_or(self.err_ty(), |f| { + field_types[f].clone().substitute(Interner, &substs) + }); + let expected_ty = self.normalize_associated_types_in(expected_ty); + + T::infer(self, subpat, &expected_ty, default_bm); + } + } + None => { + let err_ty = self.err_ty(); + for &inner in subs { + T::infer(self, inner, &err_ty, default_bm); + } + } } ty @@ -109,7 +149,7 @@ impl InferenceContext<'_> { expected: &Ty, default_bm: T::BindingMode, id: T, - subs: impl Iterator<Item = (Name, T)>, + subs: impl Iterator<Item = (Name, T)> + ExactSizeIterator, ) -> Ty { let (ty, def) = self.resolve_variant(path, false); if let Some(variant) = def { @@ -121,17 +161,51 @@ impl InferenceContext<'_> { let substs = ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner)); - let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); - let var_data = def.map(|it| it.variant_data(self.db.upcast())); + match def { + _ if subs.len() == 0 => {} + Some(def) => { + let field_types = self.db.field_types(def); + let variant_data = def.variant_data(self.db.upcast()); + let visibilities = self.db.field_visibilities(def); + + for (name, inner) in subs { + let field_def = { + match variant_data.field(&name) { + Some(local_id) => { + if !visibilities[local_id] + .is_visible_from(self.db.upcast(), self.resolver.module()) + { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: inner.into(), + private: true, + }); + } + Some(local_id) + } + None => { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: inner.into(), + private: false, + }); + None + } + } + }; - for (name, inner) in subs { - let expected_ty = var_data - .as_ref() - .and_then(|it| it.field(&name)) - .map_or(self.err_ty(), |f| field_tys[f].clone().substitute(Interner, &substs)); - let expected_ty = self.normalize_associated_types_in(expected_ty); + let expected_ty = field_def.map_or(self.err_ty(), |f| { + field_types[f].clone().substitute(Interner, &substs) + }); + let expected_ty = self.normalize_associated_types_in(expected_ty); - T::infer(self, inner, &expected_ty, default_bm); + T::infer(self, inner, &expected_ty, default_bm); + } + } + None => { + let err_ty = self.err_ty(); + for (_, inner) in subs { + T::infer(self, inner, &err_ty, default_bm); + } + } } ty diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 2a51c84db3a..c6bbf2f6140 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -178,13 +178,30 @@ impl InferenceContext<'_> { remaining_index: usize, id: ExprOrPatId, ) -> Option<(ValueNs, Substitution)> { - assert!(remaining_index < path.segments().len()); // there may be more intermediate segments between the resolved one and // the end. Only the last segment needs to be resolved to a value; from // the segments before that, we need to get either a type or a trait ref. - let resolved_segment = path.segments().get(remaining_index - 1).unwrap(); - let remaining_segments = path.segments().skip(remaining_index); + let _d; + let (resolved_segment, remaining_segments) = match path { + Path::Normal { .. } => { + assert!(remaining_index < path.segments().len()); + ( + path.segments().get(remaining_index - 1).unwrap(), + path.segments().skip(remaining_index), + ) + } + Path::LangItem(..) => ( + PathSegment { + name: { + _d = hir_expand::name::known::Unknown; + &_d + }, + args_and_bindings: None, + }, + path.segments(), + ), + }; let is_before_last = remaining_segments.len() == 1; match (def, is_before_last) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index b15339d4434..1a6106c0244 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -24,7 +24,7 @@ pub use self::{ macro_rules! user_error { ($it: expr) => { - return Err(LayoutError::UserError(format!($it))) + return Err(LayoutError::UserError(format!($it).into())) }; } @@ -50,7 +50,7 @@ pub type Variants = hir_def::layout::Variants<RustcEnumVariantIdx>; #[derive(Debug, PartialEq, Eq, Clone)] pub enum LayoutError { - UserError(String), + UserError(Box<str>), SizeOverflow, TargetLayoutNotAvailable, HasPlaceholder, @@ -109,7 +109,8 @@ fn layout_of_simd_ty( // * the homogeneous field type and the number of fields. let (e_ty, e_len, is_array) = if let TyKind::Array(e_ty, _) = f0_ty.kind(Interner) { // Extract the number of elements from the layout of the array field: - let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields else { + let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields + else { user_error!("Array with non array layout"); }; @@ -233,9 +234,9 @@ pub fn layout_of_ty_query( cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? } TyKind::Array(element, count) => { - let count = try_const_usize(db, &count).ok_or(LayoutError::UserError( - "unevaluated or mistyped const generic parameter".to_string(), - ))? as u64; + let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(Box::from( + "unevaluated or mistyped const generic parameter", + )))? as u64; let element = db.layout_of_ty(element.clone(), trait_env.clone())?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 1c92e80f335..85ef649b895 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -163,7 +163,7 @@ fn repr_discr( return Err(LayoutError::UserError( "Integer::repr_discr: `#[repr]` hint too small for \ discriminant range of enum " - .to_string(), + .into(), )); } return Ok((discr, ity.is_signed())); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 333ad473a8b..ffdbb9de934 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -212,14 +212,14 @@ fn recursive() { } check_fail( r#"struct Goal(Goal);"#, - LayoutError::UserError("infinite sized recursive type".to_string()), + LayoutError::UserError("infinite sized recursive type".into()), ); check_fail( r#" struct Foo<T>(Foo<T>); struct Goal(Foo<i32>); "#, - LayoutError::UserError("infinite sized recursive type".to_string()), + LayoutError::UserError("infinite sized recursive type".into()), ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs index 576e7f3fc61..bbe855a14de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs @@ -255,3 +255,17 @@ fn ellipsis_pattern() { } } } + +#[test] +fn regression_15623() { + size_and_align_expr! { + let a = 2; + let b = 3; + let c = 5; + move || { + let 0 = a else { return b; }; + let y = c; + y + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 9be083d0117..e953058ccca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -1,6 +1,6 @@ //! MIR definitions and implementation -use std::{fmt::Display, iter}; +use std::{collections::hash_map::Entry, fmt::Display, iter}; use crate::{ consteval::usize_const, @@ -37,6 +37,7 @@ pub use monomorphization::{ monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query, monomorphized_mir_body_recover, }; +use rustc_hash::FxHashMap; use smallvec::{smallvec, SmallVec}; use stdx::{impl_from, never}; use triomphe::Arc; @@ -165,8 +166,8 @@ impl<V, T> ProjectionElem<V, T> { TyKind::Adt(_, subst) => { db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst) } - _ => { - never!("Only adt has field"); + ty => { + never!("Only adt has field, found {:?}", ty); return TyKind::Error.intern(Interner); } }, @@ -223,35 +224,93 @@ impl<V, T> ProjectionElem<V, T> { type PlaceElem = ProjectionElem<LocalId, Ty>; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ProjectionId(u32); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProjectionStore { + id_to_proj: FxHashMap<ProjectionId, Box<[PlaceElem]>>, + proj_to_id: FxHashMap<Box<[PlaceElem]>, ProjectionId>, +} + +impl Default for ProjectionStore { + fn default() -> Self { + let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() }; + // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty` + this.intern(Box::new([])); + this + } +} + +impl ProjectionStore { + fn shrink_to_fit(&mut self) { + self.id_to_proj.shrink_to_fit(); + self.proj_to_id.shrink_to_fit(); + } + + fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> { + self.proj_to_id.get(projection).copied() + } + + fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { + let new_id = ProjectionId(self.proj_to_id.len() as u32); + match self.proj_to_id.entry(projection) { + Entry::Occupied(id) => *id.get(), + Entry::Vacant(e) => { + let key_clone = e.key().clone(); + e.insert(new_id); + self.id_to_proj.insert(new_id, key_clone); + new_id + } + } + } +} + +impl ProjectionId { + const EMPTY: ProjectionId = ProjectionId(0); + + fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { + store.id_to_proj.get(&self).unwrap() + } + + fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { + let mut current = self.lookup(store).to_vec(); + current.push(projection); + store.intern(current.into()) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, - pub projection: Box<[PlaceElem]>, + pub projection: ProjectionId, } impl Place { - fn is_parent(&self, child: &Place) -> bool { - self.local == child.local && child.projection.starts_with(&self.projection) + fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool { + self.local == child.local + && child.projection.lookup(store).starts_with(&self.projection.lookup(store)) } /// The place itself is not included - fn iterate_over_parents(&self) -> impl Iterator<Item = Place> + '_ { - (0..self.projection.len()) - .map(|x| &self.projection[0..x]) - .map(|x| Place { local: self.local, projection: x.to_vec().into() }) + fn iterate_over_parents<'a>( + &'a self, + store: &'a ProjectionStore, + ) -> impl Iterator<Item = Place> + 'a { + let projection = self.projection.lookup(store); + (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| { + Some(Place { local: self.local, projection: store.intern_if_exist(x)? }) + }) } - fn project(&self, projection: PlaceElem) -> Place { - Place { - local: self.local, - projection: self.projection.iter().cloned().chain([projection]).collect(), - } + fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place { + Place { local: self.local, projection: self.projection.project(projection, store) } } } impl From<LocalId> for Place { fn from(local: LocalId) -> Self { - Self { local, projection: vec![].into() } + Self { local, projection: ProjectionId::EMPTY } } } @@ -997,6 +1056,7 @@ pub struct BasicBlock { #[derive(Debug, Clone, PartialEq, Eq)] pub struct MirBody { + pub projection_store: ProjectionStore, pub basic_blocks: Arena<BasicBlock>, pub locals: Arena<Local>, pub start_block: BasicBlockId, @@ -1009,11 +1069,15 @@ pub struct MirBody { } impl MirBody { - fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) { - fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) { + fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) { + fn for_operand( + op: &mut Operand, + f: &mut impl FnMut(&mut Place, &mut ProjectionStore), + store: &mut ProjectionStore, + ) { match op { Operand::Copy(p) | Operand::Move(p) => { - f(p); + f(p, store); } Operand::Constant(_) | Operand::Static(_) => (), } @@ -1022,30 +1086,30 @@ impl MirBody { for statement in &mut block.statements { match &mut statement.kind { StatementKind::Assign(p, r) => { - f(p); + f(p, &mut self.projection_store); match r { Rvalue::ShallowInitBoxWithAlloc(_) => (), Rvalue::ShallowInitBox(o, _) | Rvalue::UnaryOp(_, o) | Rvalue::Cast(_, o, _) | Rvalue::Repeat(o, _) - | Rvalue::Use(o) => for_operand(o, &mut f), + | Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store), Rvalue::CopyForDeref(p) | Rvalue::Discriminant(p) | Rvalue::Len(p) - | Rvalue::Ref(_, p) => f(p), + | Rvalue::Ref(_, p) => f(p, &mut self.projection_store), Rvalue::CheckedBinaryOp(_, o1, o2) => { - for_operand(o1, &mut f); - for_operand(o2, &mut f); + for_operand(o1, &mut f, &mut self.projection_store); + for_operand(o2, &mut f, &mut self.projection_store); } Rvalue::Aggregate(_, ops) => { for op in ops.iter_mut() { - for_operand(op, &mut f); + for_operand(op, &mut f, &mut self.projection_store); } } } } - StatementKind::Deinit(p) => f(p), + StatementKind::Deinit(p) => f(p, &mut self.projection_store), StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), @@ -1053,7 +1117,9 @@ impl MirBody { } match &mut block.terminator { Some(x) => match &mut x.kind { - TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f), + TerminatorKind::SwitchInt { discr, .. } => { + for_operand(discr, &mut f, &mut self.projection_store) + } TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } @@ -1063,23 +1129,24 @@ impl MirBody { | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Drop { place, .. } => { - f(place); + f(place, &mut self.projection_store); } TerminatorKind::DropAndReplace { place, value, .. } => { - f(place); - for_operand(value, &mut f); + f(place, &mut self.projection_store); + for_operand(value, &mut f, &mut self.projection_store); } TerminatorKind::Call { func, args, destination, .. } => { - for_operand(func, &mut f); - args.iter_mut().for_each(|x| for_operand(x, &mut f)); - f(destination); + for_operand(func, &mut f, &mut self.projection_store); + args.iter_mut() + .for_each(|x| for_operand(x, &mut f, &mut self.projection_store)); + f(destination, &mut self.projection_store); } TerminatorKind::Assert { cond, .. } => { - for_operand(cond, &mut f); + for_operand(cond, &mut f, &mut self.projection_store); } TerminatorKind::Yield { value, resume_arg, .. } => { - for_operand(value, &mut f); - f(resume_arg); + for_operand(value, &mut f, &mut self.projection_store); + f(resume_arg, &mut self.projection_store); } }, None => (), @@ -1096,7 +1163,9 @@ impl MirBody { binding_locals, param_locals, closures, + projection_store, } = self; + projection_store.shrink_to_fit(); basic_blocks.shrink_to_fit(); locals.shrink_to_fit(); binding_locals.shrink_to_fit(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index c70d7f63fd8..41fb129652a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -42,30 +42,27 @@ pub struct BorrowckResult { fn all_mir_bodies( db: &dyn HirDatabase, def: DefWithBodyId, -) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> { + mut cb: impl FnMut(Arc<MirBody>), +) -> Result<(), MirLowerError> { fn for_closure( db: &dyn HirDatabase, c: ClosureId, - ) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> { + cb: &mut impl FnMut(Arc<MirBody>), + ) -> Result<(), MirLowerError> { match db.mir_body_for_closure(c) { Ok(body) => { - let closures = body.closures.clone(); - Box::new( - iter::once(Ok(body)) - .chain(closures.into_iter().flat_map(|it| for_closure(db, it))), - ) + cb(body.clone()); + body.closures.iter().map(|&it| for_closure(db, it, cb)).collect() } - Err(e) => Box::new(iter::once(Err(e))), + Err(e) => Err(e), } } match db.mir_body(def) { Ok(body) => { - let closures = body.closures.clone(); - Box::new( - iter::once(Ok(body)).chain(closures.into_iter().flat_map(|it| for_closure(db, it))), - ) + cb(body.clone()); + body.closures.iter().map(|&it| for_closure(db, it, &mut cb)).collect() } - Err(e) => Box::new(iter::once(Err(e))), + Err(e) => Err(e), } } @@ -74,17 +71,15 @@ pub fn borrowck_query( def: DefWithBodyId, ) -> Result<Arc<[BorrowckResult]>, MirLowerError> { let _p = profile::span("borrowck_query"); - let r = all_mir_bodies(db, def) - .map(|body| { - let body = body?; - Ok(BorrowckResult { - mutability_of_locals: mutability_of_locals(db, &body), - moved_out_of_ref: moved_out_of_ref(db, &body), - mir_body: body, - }) - }) - .collect::<Result<Vec<_>, MirLowerError>>()?; - Ok(r.into()) + let mut res = vec![]; + all_mir_bodies(db, def, |body| { + res.push(BorrowckResult { + mutability_of_locals: mutability_of_locals(db, &body), + moved_out_of_ref: moved_out_of_ref(db, &body), + mir_body: body, + }); + })?; + Ok(res.into()) } fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> { @@ -93,7 +88,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> Operand::Copy(p) | Operand::Move(p) => { let mut ty: Ty = body.locals[p.local].ty.clone(); let mut is_dereference_of_ref = false; - for proj in &*p.projection { + for proj in p.projection.lookup(&body.projection_store) { if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { is_dereference_of_ref = true; } @@ -125,6 +120,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> Operand::Constant(_) | Operand::Static(_) => (), }; for (_, block) in body.basic_blocks.iter() { + db.unwind_if_cancelled(); for statement in &block.statements { match &statement.kind { StatementKind::Assign(_, r) => match r { @@ -183,6 +179,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> None => (), } } + result.shrink_to_fit(); result } @@ -199,7 +196,7 @@ enum ProjectionCase { fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase { let mut is_part_of = false; let mut ty = body.locals[lvalue.local].ty.clone(); - for proj in lvalue.projection.iter() { + for proj in lvalue.projection.lookup(&body.projection_store).iter() { match proj { ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw ProjectionElem::Deref // It's direct in case of `Box<T>` @@ -258,7 +255,7 @@ fn ever_initialized_map( for statement in &block.statements { match &statement.kind { StatementKind::Assign(p, _) => { - if p.projection.len() == 0 && p.local == l { + if p.projection.lookup(&body.projection_store).len() == 0 && p.local == l { is_ever_initialized = true; } } @@ -277,21 +274,37 @@ fn ever_initialized_map( ); return; }; - let targets = match &terminator.kind { - TerminatorKind::Goto { target } => vec![*target], - TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(), + let mut process = |target, is_ever_initialized| { + if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized { + result[target].insert(l, is_ever_initialized); + dfs(db, body, target, l, result); + } + }; + match &terminator.kind { + TerminatorKind::Goto { target } => process(*target, is_ever_initialized), + TerminatorKind::SwitchInt { targets, .. } => { + targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized)); + } TerminatorKind::UnwindResume | TerminatorKind::Abort | TerminatorKind::Return - | TerminatorKind::Unreachable => vec![], + | TerminatorKind::Unreachable => (), TerminatorKind::Call { target, cleanup, destination, .. } => { - if destination.projection.len() == 0 && destination.local == l { + if destination.projection.lookup(&body.projection_store).len() == 0 + && destination.local == l + { is_ever_initialized = true; } - target.into_iter().chain(cleanup.into_iter()).copied().collect() + target + .into_iter() + .chain(cleanup.into_iter()) + .for_each(|&it| process(it, is_ever_initialized)); } TerminatorKind::Drop { target, unwind, place: _ } => { - Some(target).into_iter().chain(unwind.into_iter()).copied().collect() + iter::once(target) + .into_iter() + .chain(unwind.into_iter()) + .for_each(|&it| process(it, is_ever_initialized)); } TerminatorKind::DropAndReplace { .. } | TerminatorKind::Assert { .. } @@ -300,13 +313,7 @@ fn ever_initialized_map( | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } => { never!("We don't emit these MIR terminators yet"); - vec![] - } - }; - for target in targets { - if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized { - result[target].insert(l, is_ever_initialized); - dfs(db, body, target, l, result); + () } } } @@ -315,6 +322,7 @@ fn ever_initialized_map( dfs(db, body, body.start_block, l, &mut result); } for l in body.locals.iter().map(|it| it.0) { + db.unwind_if_cancelled(); if !result[body.start_block].contains_idx(l) { result[body.start_block].insert(l, false); dfs(db, body, body.start_block, l, &mut result); @@ -384,7 +392,7 @@ fn mutability_of_locals( | TerminatorKind::Assert { .. } | TerminatorKind::Yield { .. } => (), TerminatorKind::Call { destination, .. } => { - if destination.projection.len() == 0 { + if destination.projection.lookup(&body.projection_store).len() == 0 { if ever_init_map.get(destination.local).copied().unwrap_or_default() { push_mut_span(destination.local, MirSpan::Unknown); } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 3944feb128c..4364e0d323b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -46,8 +46,8 @@ use crate::{ use super::{ return_slot, AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, - MirSpan, Operand, Place, PlaceElem, ProjectionElem, Rvalue, StatementKind, TerminatorKind, - UnOp, + MirSpan, Operand, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind, + TerminatorKind, UnOp, }; mod shim; @@ -215,9 +215,7 @@ impl Interval { } fn write_from_interval(&self, memory: &mut Evaluator<'_>, interval: Interval) -> Result<()> { - // FIXME: this could be more efficient - let bytes = &interval.get(memory)?.to_vec(); - memory.write_memory(self.addr, bytes) + memory.copy_from_interval(self.addr, interval) } fn slice(self, range: Range<usize>) -> Interval { @@ -341,7 +339,7 @@ pub enum MirEvalError { InvalidVTableId(usize), CoerceUnsizedError(Ty), LangItemNotFound(LangItem), - BrokenLayout(Layout), + BrokenLayout(Box<Layout>), } impl MirEvalError { @@ -410,7 +408,7 @@ impl MirEvalError { err.pretty_print(f, db, span_formatter)?; } MirEvalError::ConstEvalError(name, err) => { - MirLowerError::ConstEvalError(name.clone(), err.clone()).pretty_print( + MirLowerError::ConstEvalError((**name).into(), err.clone()).pretty_print( f, db, span_formatter, @@ -485,17 +483,18 @@ struct DropFlags { } impl DropFlags { - fn add_place(&mut self, p: Place) { - if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) { + fn add_place(&mut self, p: Place, store: &ProjectionStore) { + if p.iterate_over_parents(store).any(|it| self.need_drop.contains(&it)) { return; } - self.need_drop.retain(|it| !p.is_parent(it)); + self.need_drop.retain(|it| !p.is_parent(it, store)); self.need_drop.insert(p); } - fn remove_place(&mut self, p: &Place) -> bool { + fn remove_place(&mut self, p: &Place, store: &ProjectionStore) -> bool { // FIXME: replace parents with parts - if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(&it)) { + if let Some(parent) = p.iterate_over_parents(store).find(|it| self.need_drop.contains(&it)) + { self.need_drop.remove(&parent); return true; } @@ -656,7 +655,7 @@ impl Evaluator<'_> { let mut addr = locals.ptr[p.local].addr; let mut ty: Ty = locals.body.locals[p.local].ty.clone(); let mut metadata: Option<IntervalOrOwned> = None; // locals are always sized - for proj in &*p.projection { + for proj in p.projection.lookup(&locals.body.projection_store) { let prev_ty = ty.clone(); ty = self.projected_ty(ty, proj.clone()); match proj { @@ -837,7 +836,9 @@ impl Evaluator<'_> { let addr = self.place_addr(l, &locals)?; let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?; self.write_memory(addr, &result)?; - locals.drop_flags.add_place(l.clone()); + locals + .drop_flags + .add_place(l.clone(), &locals.body.projection_store); } StatementKind::Deinit(_) => not_supported!("de-init statement"), StatementKind::StorageLive(_) @@ -889,7 +890,9 @@ impl Evaluator<'_> { )?, it => not_supported!("unknown function type {it:?}"), }; - locals.drop_flags.add_place(destination.clone()); + locals + .drop_flags + .add_place(destination.clone(), &locals.body.projection_store); if let Some(stack_frame) = stack_frame { self.code_stack.push(my_stack_frame); current_block_idx = stack_frame.locals.body.start_block; @@ -970,7 +973,7 @@ impl Evaluator<'_> { ) -> Result<()> { let mut remain_args = body.param_locals.len(); for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) { - locals.drop_flags.add_place(l.into()); + locals.drop_flags.add_place(l.into(), &locals.body.projection_store); match value { IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?, IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?, @@ -1629,7 +1632,7 @@ impl Evaluator<'_> { if let Some((offset, size, value)) = tag { match result.get_mut(offset..offset + size) { Some(it) => it.copy_from_slice(&value.to_le_bytes()[0..size]), - None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())), + None => return Err(MirEvalError::BrokenLayout(Box::new(variant_layout.clone()))), } } for (i, op) in values.enumerate() { @@ -1637,7 +1640,7 @@ impl Evaluator<'_> { let op = op.get(&self)?; match result.get_mut(offset..offset + op.len()) { Some(it) => it.copy_from_slice(op), - None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())), + None => return Err(MirEvalError::BrokenLayout(Box::new(variant_layout.clone()))), } } Ok(result) @@ -1646,7 +1649,7 @@ impl Evaluator<'_> { fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<Interval> { Ok(match it { Operand::Copy(p) | Operand::Move(p) => { - locals.drop_flags.remove_place(p); + locals.drop_flags.remove_place(p, &locals.body.projection_store); self.eval_place(p, locals)? } Operand::Static(st) => { @@ -1760,6 +1763,48 @@ impl Evaluator<'_> { Ok(()) } + fn copy_from_interval(&mut self, addr: Address, r: Interval) -> Result<()> { + if r.size == 0 { + return Ok(()); + } + + let oob = || MirEvalError::UndefinedBehavior("out of bounds memory write".to_string()); + + match (addr, r.addr) { + (Stack(dst), Stack(src)) => { + if self.stack.len() < src + r.size || self.stack.len() < dst + r.size { + return Err(oob()); + } + self.stack.copy_within(src..src + r.size, dst) + } + (Heap(dst), Heap(src)) => { + if self.stack.len() < src + r.size || self.stack.len() < dst + r.size { + return Err(oob()); + } + self.heap.copy_within(src..src + r.size, dst) + } + (Stack(dst), Heap(src)) => { + self.stack + .get_mut(dst..dst + r.size) + .ok_or_else(oob)? + .copy_from_slice(self.heap.get(src..src + r.size).ok_or_else(oob)?); + } + (Heap(dst), Stack(src)) => { + self.heap + .get_mut(dst..dst + r.size) + .ok_or_else(oob)? + .copy_from_slice(self.stack.get(src..src + r.size).ok_or_else(oob)?); + } + _ => { + return Err(MirEvalError::UndefinedBehavior(format!( + "invalid memory write at address {addr:?}" + ))) + } + } + + Ok(()) + } + fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result<Option<(usize, usize)>> { if let Some(layout) = self.layout_cache.borrow().get(ty) { return Ok(layout @@ -2468,7 +2513,7 @@ impl Evaluator<'_> { fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; - if !locals.drop_flags.remove_place(place) { + if !locals.drop_flags.remove_place(place, &locals.body.projection_store) { return Ok(()); } let metadata = match metadata { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 52943e97ac0..803ef631f1e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -4,7 +4,10 @@ use std::cmp; use chalk_ir::TyKind; -use hir_def::resolver::HasResolver; +use hir_def::{ + builtin_type::{BuiltinInt, BuiltinUint}, + resolver::HasResolver, +}; use hir_expand::mod_path::ModPath; use super::*; @@ -300,21 +303,36 @@ impl Evaluator<'_> { BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_string())), PanicFmt => { let message = (|| { - let resolver = self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast()); + let resolver = self + .db + .crate_def_map(self.crate_id) + .crate_root() + .resolver(self.db.upcast()); let Some(format_fn) = resolver.resolve_path_in_value_ns_fully( self.db.upcast(), - &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments( - hir_expand::mod_path::PathKind::Abs, - [name![std], name![fmt], name![format]].into_iter(), - )), + &hir_def::path::Path::from_known_path_with_no_generic( + ModPath::from_segments( + hir_expand::mod_path::PathKind::Abs, + [name![std], name![fmt], name![format]].into_iter(), + ), + ), ) else { not_supported!("std::fmt::format not found"); }; - let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { not_supported!("std::fmt::format is not a function") }; - let message_string = self.interpret_mir(self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.map(|x| IntervalOrOwned::Owned(x.clone())))?; - let addr = Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; + let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { + not_supported!("std::fmt::format is not a function") + }; + let message_string = self.interpret_mir( + self.db + .mir_body(format_fn.into()) + .map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, + args.map(|x| IntervalOrOwned::Owned(x.clone())), + )?; + let addr = + Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]); - Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?).into_owned()) + Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?) + .into_owned()) })() .unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}")); Err(MirEvalError::Panic(message)) @@ -483,9 +501,7 @@ impl Evaluator<'_> { } "syscall" => { let Some((id, rest)) = args.split_first() else { - return Err(MirEvalError::TypeError( - "syscall arg1 is not provided", - )); + return Err(MirEvalError::TypeError("syscall arg1 is not provided")); }; let id = from_bytes!(i64, id.get(self)?); self.exec_syscall(id, rest, destination, locals, span) @@ -710,7 +726,8 @@ impl Evaluator<'_> { } match name { "size_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("size_of generic arg is not provided")); }; @@ -718,14 +735,17 @@ impl Evaluator<'_> { destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size]) } "min_align_of" | "pref_align_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { return Err(MirEvalError::TypeError("align_of generic arg is not provided")); }; let align = self.layout(ty)?.align.abi.bytes(); destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) } "size_of_val" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("size_of_val generic arg is not provided")); }; @@ -741,8 +761,12 @@ impl Evaluator<'_> { } } "min_align_of_val" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { - return Err(MirEvalError::TypeError("min_align_of_val generic arg is not provided")); + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "min_align_of_val generic arg is not provided", + )); }; let [arg] = args else { return Err(MirEvalError::TypeError("min_align_of_val args are not provided")); @@ -756,7 +780,8 @@ impl Evaluator<'_> { } } "type_name" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("type_name generic arg is not provided")); }; @@ -779,7 +804,8 @@ impl Evaluator<'_> { .write_from_bytes(self, &len.to_le_bytes()) } "needs_drop" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError("size_of generic arg is not provided")); }; @@ -831,9 +857,12 @@ impl Evaluator<'_> { let lhs = i128::from_le_bytes(pad16(lhs.get(self)?, false)); let rhs = i128::from_le_bytes(pad16(rhs.get(self)?, false)); let ans = lhs.wrapping_sub(rhs); - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { - return Err(MirEvalError::TypeError("ptr_offset_from generic arg is not provided")); + return Err(MirEvalError::TypeError( + "ptr_offset_from generic arg is not provided", + )); }; let size = self.size_of_sized(ty, locals, "ptr_offset_from arg")? as i128; let ans = ans / size; @@ -940,7 +969,8 @@ impl Evaluator<'_> { "copy_nonoverlapping args are not provided", )); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError( "copy_nonoverlapping generic arg is not provided", @@ -959,9 +989,45 @@ impl Evaluator<'_> { let [ptr, offset] = args else { return Err(MirEvalError::TypeError("offset args are not provided")); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) - else { - return Err(MirEvalError::TypeError("offset generic arg is not provided")); + let ty = if name == "offset" { + let Some(ty0) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError("offset generic arg is not provided")); + }; + let Some(ty1) = + generic_args.as_slice(Interner).get(1).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError("offset generic arg is not provided")); + }; + if !matches!( + ty1.as_builtin(), + Some( + BuiltinType::Int(BuiltinInt::Isize) + | BuiltinType::Uint(BuiltinUint::Usize) + ) + ) { + return Err(MirEvalError::TypeError( + "offset generic arg is not usize or isize", + )); + } + match ty0.as_raw_ptr() { + Some((ty, _)) => ty, + None => { + return Err(MirEvalError::TypeError( + "offset generic arg is not a raw pointer", + )); + } + } + } else { + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "arith_offset generic arg is not provided", + )); + }; + ty }; let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false)); let offset = u128::from_le_bytes(pad16(offset.get(self)?, false)); @@ -1079,7 +1145,8 @@ impl Evaluator<'_> { let [arg] = args else { return Err(MirEvalError::TypeError("discriminant_value arg is not provided")); }; - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { return Err(MirEvalError::TypeError( "discriminant_value generic arg is not provided", @@ -1133,17 +1200,32 @@ impl Evaluator<'_> { let addr = Address::from_bytes(arg.interval.get(self)?)?; destination.write_from_interval(self, Interval { addr, size: destination.size }) } + "write_via_move" => { + let [ptr, val] = args else { + return Err(MirEvalError::TypeError("write_via_move args are not provided")); + }; + let dst = Address::from_bytes(ptr.get(self)?)?; + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::TypeError( + "write_via_copy generic arg is not provided", + )); + }; + let size = self.size_of_sized(ty, locals, "write_via_move ptr type")?; + Interval { addr: dst, size }.write_from_interval(self, val.interval)?; + Ok(()) + } "write_bytes" => { let [dst, val, count] = args else { return Err(MirEvalError::TypeError("write_bytes args are not provided")); }; let count = from_bytes!(usize, count.get(self)?); let val = from_bytes!(u8, val.get(self)?); - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + let Some(ty) = + generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { - return Err(MirEvalError::TypeError( - "write_bytes generic arg is not provided", - )); + return Err(MirEvalError::TypeError("write_bytes generic arg is not provided")); }; let dst = Address::from_bytes(dst.get(self)?)?; let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs index ec746310487..51900662426 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs @@ -45,7 +45,9 @@ impl Evaluator<'_> { }; match try_const_usize(self.db, len) { Some(len) => { - let Some(ty) = subst.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else { + let Some(ty) = + subst.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) + else { return Err(MirEvalError::TypeError("simd type with no ty param")); }; Ok((len as usize, ty.clone())) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 51cf882d053..dd2dba717f9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -71,7 +71,7 @@ struct MirLowerCtx<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub enum MirLowerError { - ConstEvalError(String, Box<ConstEvalError>), + ConstEvalError(Box<str>, Box<ConstEvalError>), LayoutError(LayoutError), IncompleteExpr, IncompletePattern, @@ -84,7 +84,7 @@ pub enum MirLowerError { UnsizedTemporary(Ty), MissingFunctionDefinition(DefWithBodyId, ExprId), TypeMismatch(TypeMismatch), - /// This should be never happen. Type mismatch should catch everything. + /// This should never happen. Type mismatch should catch everything. TypeError(&'static str), NotSupported(String), ContinueWithoutLoop, @@ -244,6 +244,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let locals = Arena::new(); let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new(); let mir = MirBody { + projection_store: ProjectionStore::default(), basic_blocks, locals, start_block, @@ -370,6 +371,12 @@ impl<'ctx> MirLowerCtx<'ctx> { mut current: BasicBlockId, ) -> Result<Option<BasicBlockId>> { match &self.body.exprs[expr_id] { + Expr::OffsetOf(_) => { + not_supported!("builtin#offset_of") + } + Expr::InlineAsm(_) => { + not_supported!("builtin#asm") + } Expr::Missing => { if let DefWithBodyId::FunctionId(f) = self.owner { let assoc = f.lookup(self.db.upcast()); @@ -803,36 +810,34 @@ impl<'ctx> MirLowerCtx<'ctx> { current = c; operands[u32::from(field_id.into_raw()) as usize] = Some(op); } - self.push_assignment( - current, - place, - Rvalue::Aggregate( - AggregateKind::Adt(variant_id, subst), - match spread_place { - Some(sp) => operands - .into_iter() - .enumerate() - .map(|(i, it)| match it { - Some(it) => it, - None => { - let p = - sp.project(ProjectionElem::Field(FieldId { - parent: variant_id, - local_id: LocalFieldId::from_raw( - RawIdx::from(i as u32), - ), - })); - Operand::Copy(p) - } - }) - .collect(), - None => operands.into_iter().collect::<Option<_>>().ok_or( - MirLowerError::TypeError("missing field in record literal"), - )?, - }, - ), - expr_id.into(), + let rvalue = Rvalue::Aggregate( + AggregateKind::Adt(variant_id, subst), + match spread_place { + Some(sp) => operands + .into_iter() + .enumerate() + .map(|(i, it)| match it { + Some(it) => it, + None => { + let p = sp.project( + ProjectionElem::Field(FieldId { + parent: variant_id, + local_id: LocalFieldId::from_raw(RawIdx::from( + i as u32, + )), + }), + &mut self.result.projection_store, + ); + Operand::Copy(p) + } + }) + .collect(), + None => operands.into_iter().collect::<Option<_>>().ok_or( + MirLowerError::TypeError("missing field in record literal"), + )?, + }, ); + self.push_assignment(current, place, rvalue, expr_id.into()); Ok(Some(current)) } VariantId::UnionId(union_id) => { @@ -841,10 +846,10 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let place = place.project(PlaceElem::Field(FieldId { - parent: union_id.into(), - local_id, - })); + let place = place.project( + PlaceElem::Field(FieldId { parent: union_id.into(), local_id }), + &mut self.result.projection_store, + ); self.lower_expr_to_place(*expr, place, current) } } @@ -898,7 +903,7 @@ impl<'ctx> MirLowerCtx<'ctx> { else { return Ok(None); }; - let p = place.project(ProjectionElem::Deref); + let p = place.project(ProjectionElem::Deref, &mut self.result.projection_store); self.push_assignment(current, p, operand.into(), expr_id.into()); Ok(Some(current)) } @@ -1120,27 +1125,31 @@ impl<'ctx> MirLowerCtx<'ctx> { for capture in captures.iter() { let p = Place { local: self.binding_local(capture.place.local)?, - projection: capture - .place - .projections - .clone() - .into_iter() - .map(|it| match it { - ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Field(it) => ProjectionElem::Field(it), - ProjectionElem::TupleOrClosureField(it) => { - ProjectionElem::TupleOrClosureField(it) - } - ProjectionElem::ConstantIndex { offset, from_end } => { - ProjectionElem::ConstantIndex { offset, from_end } - } - ProjectionElem::Subslice { from, to } => { - ProjectionElem::Subslice { from, to } - } - ProjectionElem::OpaqueCast(it) => ProjectionElem::OpaqueCast(it), - ProjectionElem::Index(it) => match it {}, - }) - .collect(), + projection: self.result.projection_store.intern( + capture + .place + .projections + .clone() + .into_iter() + .map(|it| match it { + ProjectionElem::Deref => ProjectionElem::Deref, + ProjectionElem::Field(it) => ProjectionElem::Field(it), + ProjectionElem::TupleOrClosureField(it) => { + ProjectionElem::TupleOrClosureField(it) + } + ProjectionElem::ConstantIndex { offset, from_end } => { + ProjectionElem::ConstantIndex { offset, from_end } + } + ProjectionElem::Subslice { from, to } => { + ProjectionElem::Subslice { from, to } + } + ProjectionElem::OpaqueCast(it) => { + ProjectionElem::OpaqueCast(it) + } + ProjectionElem::Index(it) => match it {}, + }) + .collect(), + ), }; match &capture.kind { CaptureKind::ByRef(bk) => { @@ -1201,7 +1210,8 @@ impl<'ctx> MirLowerCtx<'ctx> { let Some(values) = elements .iter() .map(|it| { - let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else { + let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? + else { return Ok(None); }; current = c; @@ -1254,12 +1264,12 @@ impl<'ctx> MirLowerCtx<'ctx> { match &self.body.exprs[lhs] { Expr::Tuple { exprs, is_assignee_expr: _ } => { for (i, expr) in exprs.iter().enumerate() { - let Some(c) = self.lower_destructing_assignment( - current, - *expr, - rhs.project(ProjectionElem::TupleOrClosureField(i)), - span, - )? else { + let rhs = rhs.project( + ProjectionElem::TupleOrClosureField(i), + &mut self.result.projection_store, + ); + let Some(c) = self.lower_destructing_assignment(current, *expr, rhs, span)? + else { return Ok(None); }; current = c; @@ -1268,8 +1278,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Underscore => Ok(Some(current)), _ => { - let Some((lhs_place, current)) = - self.lower_expr_as_place(current, lhs, false)? + let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else { return Ok(None); }; @@ -1286,9 +1295,7 @@ impl<'ctx> MirLowerCtx<'ctx> { rhs: ExprId, span: MirSpan, ) -> Result<Option<BasicBlockId>> { - let Some((rhs_op, current)) = - self.lower_expr_to_some_operand(rhs, current)? - else { + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(rhs, current)? else { return Ok(None); }; if matches!(&self.body.exprs[lhs], Expr::Underscore) { @@ -1303,9 +1310,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.push_assignment(current, temp.clone(), rhs_op.into(), span); return self.lower_destructing_assignment(current, lhs, temp, span); } - let Some((lhs_place, current)) = - self.lower_expr_as_place(current, lhs, false)? - else { + let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else { return Ok(None); }; self.push_assignment(current, lhs_place, rhs_op.into(), span); @@ -1320,17 +1325,21 @@ impl<'ctx> MirLowerCtx<'ctx> { placeholder_subst } - fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> { + fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<()> { if let Expr::Field { expr, name } = &self.body[expr_id] { if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) { let index = name .as_tuple_index() .ok_or(MirLowerError::TypeError("named field on tuple"))?; - *place = place.project(ProjectionElem::TupleOrClosureField(index)) + *place = place.project( + ProjectionElem::TupleOrClosureField(index), + &mut self.result.projection_store, + ) } else { let field = self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; - *place = place.project(ProjectionElem::Field(field)); + *place = + place.project(ProjectionElem::Field(field), &mut self.result.projection_store); } } else { not_supported!("") @@ -1447,7 +1456,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let name = const_id.name(self.db.upcast()); self.db .const_eval(const_id.into(), subst, None) - .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))? + .map_err(|e| MirLowerError::ConstEvalError(name.into(), Box::new(e)))? }; Ok(Operand::Constant(c)) } @@ -1844,7 +1853,7 @@ impl<'ctx> MirLowerCtx<'ctx> { data.name.display(self.db.upcast()), data.variants[variant.local_id].name.display(self.db.upcast()) ); - Err(MirLowerError::ConstEvalError(name, Box::new(e))) + Err(MirLowerError::ConstEvalError(name.into(), Box::new(e))) } } } @@ -1992,13 +2001,14 @@ pub fn mir_body_for_closure_query( FnTrait::FnOnce => vec![], FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref], }; - ctx.result.walk_places(|p| { + ctx.result.walk_places(|p, store| { if let Some(it) = upvar_map.get(&p.local) { let r = it.iter().find(|it| { - if p.projection.len() < it.0.place.projections.len() { + if p.projection.lookup(&store).len() < it.0.place.projections.len() { return false; } - for (it, y) in p.projection.iter().zip(it.0.place.projections.iter()) { + for (it, y) in p.projection.lookup(&store).iter().zip(it.0.place.projections.iter()) + { match (it, y) { (ProjectionElem::Deref, ProjectionElem::Deref) => (), (ProjectionElem::Field(it), ProjectionElem::Field(y)) if it == y => (), @@ -2016,13 +2026,18 @@ pub fn mir_body_for_closure_query( p.local = closure_local; let mut next_projs = closure_projection.clone(); next_projs.push(PlaceElem::TupleOrClosureField(it.1)); - let prev_projs = mem::take(&mut p.projection); + let prev_projs = p.projection; if it.0.kind != CaptureKind::ByValue { next_projs.push(ProjectionElem::Deref); } - next_projs - .extend(prev_projs.iter().cloned().skip(it.0.place.projections.len())); - p.projection = next_projs.into(); + next_projs.extend( + prev_projs + .lookup(&store) + .iter() + .cloned() + .skip(it.0.place.projections.len()), + ); + p.projection = store.intern(next_projs.into()); } None => err = Some(p.clone()), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index 213f151ab67..8c078eb4ad7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -70,7 +70,7 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - it.0 = it.0.project(ProjectionElem::Deref); + it.0 = it.0.project(ProjectionElem::Deref, &mut self.result.projection_store); Ok(Some(it)) } Adjust::Deref(Some(od)) => { @@ -152,7 +152,10 @@ impl MirLowerCtx<'_> { Operand::Static(s).into(), expr_id.into(), ); - Ok(Some((temp.project(ProjectionElem::Deref), current))) + Ok(Some(( + temp.project(ProjectionElem::Deref, &mut self.result.projection_store), + current, + ))) } _ => try_rvalue(self), } @@ -203,7 +206,7 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - r = r.project(ProjectionElem::Deref); + r = r.project(ProjectionElem::Deref, &mut self.result.projection_store); Ok(Some((r, current))) } _ => try_rvalue(self), @@ -267,7 +270,8 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - p_base = p_base.project(ProjectionElem::Index(l_index)); + p_base = p_base + .project(ProjectionElem::Index(l_index), &mut self.result.projection_store); Ok(Some((p_base, current))) } _ => try_rvalue(self), @@ -308,7 +312,7 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref); + result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); Ok(Some((result, current))) } @@ -363,7 +367,7 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref); + result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); Ok(Some((result, current))) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 1cdfd919742..270f75ad967 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -81,13 +81,16 @@ impl MirLowerCtx<'_> { mode: MatchingMode, ) -> Result<(BasicBlockId, Option<BasicBlockId>)> { let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default(); - cond_place.projection = cond_place - .projection - .iter() - .cloned() - .chain((0..cnt).map(|_| ProjectionElem::Deref)) - .collect::<Vec<_>>() - .into(); + cond_place.projection = self.result.projection_store.intern( + cond_place + .projection + .lookup(&self.result.projection_store) + .iter() + .cloned() + .chain((0..cnt).map(|_| ProjectionElem::Deref)) + .collect::<Vec<_>>() + .into(), + ); Ok(match &self.body.pats[pattern] { Pat::Missing => return Err(MirLowerError::IncompletePattern), Pat::Wild => (current, current_else), @@ -262,20 +265,23 @@ impl MirLowerCtx<'_> { } } for (i, &pat) in prefix.iter().enumerate() { - let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex { - offset: i as u64, - from_end: false, - }); + let next_place = (&mut cond_place).project( + ProjectionElem::ConstantIndex { offset: i as u64, from_end: false }, + &mut self.result.projection_store, + ); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } if let Some(slice) = slice { if mode == MatchingMode::Bind { if let Pat::Bind { id, subpat: _ } = self.body[*slice] { - let next_place = (&mut cond_place).project(ProjectionElem::Subslice { - from: prefix.len() as u64, - to: suffix.len() as u64, - }); + let next_place = (&mut cond_place).project( + ProjectionElem::Subslice { + from: prefix.len() as u64, + to: suffix.len() as u64, + }, + &mut self.result.projection_store, + ); (current, current_else) = self.pattern_match_binding( id, next_place, @@ -287,10 +293,10 @@ impl MirLowerCtx<'_> { } } for (i, &pat) in suffix.iter().enumerate() { - let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex { - offset: i as u64, - from_end: true, - }); + let next_place = (&mut cond_place).project( + ProjectionElem::ConstantIndex { offset: i as u64, from_end: true }, + &mut self.result.projection_store, + ); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } @@ -412,13 +418,11 @@ impl MirLowerCtx<'_> { mode, )? } - Pat::Ref { pat, mutability: _ } => self.pattern_match_inner( - current, - current_else, - cond_place.project(ProjectionElem::Deref), - *pat, - mode, - )?, + Pat::Ref { pat, mutability: _ } => { + let cond_place = + cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store); + self.pattern_match_inner(current, current_else, cond_place, *pat, mode)? + } Pat::Box { .. } => not_supported!("box pattern"), Pat::ConstBlock(_) => not_supported!("const block pattern"), }) @@ -594,7 +598,7 @@ impl MirLowerCtx<'_> { mode: MatchingMode, ) -> Result<(BasicBlockId, Option<BasicBlockId>)> { for (proj, arg) in args { - let cond_place = cond_place.project(proj); + let cond_place = cond_place.project(proj, &mut self.result.projection_store); (current, current_else) = self.pattern_match_inner(current, current_else, cond_place, arg, mode)?; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 781ffaecad5..0108859ff32 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -329,7 +329,7 @@ impl<'a> MirPrettyCtx<'a> { } } } - f(self, p.local, &p.projection); + f(self, p.local, &p.projection.lookup(&self.body.projection_store)); } fn operand(&mut self, r: &Operand) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 2ad7946c8ac..8140c4107b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3,55 +3,6 @@ use expect_test::expect; use super::{check, check_infer, check_no_mismatches, check_types}; #[test] -fn infer_box() { - check_types( - r#" -//- /main.rs crate:main deps:std -fn test() { - let x = box 1; - let t = (x, box x, box &1, box [1]); - t; -} //^ (Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; 1]>) - -//- /std.rs crate:std -#[prelude_import] use prelude::*; -mod prelude {} - -mod boxed { - #[lang = "owned_box"] - pub struct Box<T: ?Sized> { - inner: *mut T, - } -} -"#, - ); -} - -#[test] -fn infer_box_with_allocator() { - check_types( - r#" -//- /main.rs crate:main deps:std -fn test() { - let x = box 1; - let t = (x, box x, box &1, box [1]); - t; -} //^ (Box<i32, {unknown}>, Box<Box<i32, {unknown}>, {unknown}>, Box<&i32, {unknown}>, Box<[i32; 1], {unknown}>) - -//- /std.rs crate:std -#[prelude_import] use prelude::*; -mod boxed { - #[lang = "owned_box"] - pub struct Box<T: ?Sized, A: Allocator> { - inner: *mut T, - allocator: A, - } -} -"#, - ); -} - -#[test] fn infer_adt_self() { check_types( r#" @@ -2763,8 +2714,8 @@ impl<T> [T] { } fn test() { - let vec = <[_]>::into_vec(box [1i32]); - let v: Vec<Box<dyn B>> = <[_]> :: into_vec(box [box Astruct]); + let vec = <[_]>::into_vec(#[rustc_box] Box::new([1i32])); + let v: Vec<Box<dyn B>> = <[_]> :: into_vec(#[rustc_box] Box::new([#[rustc_box] Box::new(Astruct)])); } trait B{} @@ -2774,20 +2725,20 @@ impl B for Astruct {} expect![[r#" 604..608 'self': Box<[T], A> 637..669 '{ ... }': Vec<T, A> - 683..796 '{ ...t]); }': () + 683..853 '{ ...])); }': () 693..696 'vec': Vec<i32, Global> 699..714 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global> - 699..726 '<[_]>:...1i32])': Vec<i32, Global> - 715..725 'box [1i32]': Box<[i32; 1], Global> - 719..725 '[1i32]': [i32; 1] - 720..724 '1i32': i32 - 736..737 'v': Vec<Box<dyn B, Global>, Global> - 757..774 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global> - 757..793 '<[_]> ...ruct])': Vec<Box<dyn B, Global>, Global> - 775..792 'box [b...truct]': Box<[Box<dyn B, Global>; 1], Global> - 779..792 '[box Astruct]': [Box<dyn B, Global>; 1] - 780..791 'box Astruct': Box<Astruct, Global> - 784..791 'Astruct': Astruct + 699..745 '<[_]>:...i32]))': Vec<i32, Global> + 715..744 '#[rust...1i32])': Box<[i32; 1], Global> + 737..743 '[1i32]': [i32; 1] + 738..742 '1i32': i32 + 755..756 'v': Vec<Box<dyn B, Global>, Global> + 776..793 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global> + 776..850 '<[_]> ...ct)]))': Vec<Box<dyn B, Global>, Global> + 794..849 '#[rust...uct)])': Box<[Box<dyn B, Global>; 1], Global> + 816..848 '[#[rus...ruct)]': [Box<dyn B, Global>; 1] + 817..847 '#[rust...truct)': Box<Astruct, Global> + 839..846 'Astruct': Astruct "#]], ) } @@ -3649,3 +3600,30 @@ fn main() { "#, ); } + +#[test] +fn offset_of() { + check_types( + r#" +fn main() { + builtin#offset_of((,), 0); + // ^^^^^^^^^^^^^^^^^^^^^^^^^ usize +} +"#, + ); +} + +#[test] +fn builtin_format_args() { + check( + r#" +//- minicore: fmt +fn main() { + let are = "are"; + let count = 10; + builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'_> +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 542df8b3468..d36b885ec14 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -162,16 +162,16 @@ unsafe impl Allocator for Global {} #[lang = "owned_box"] #[fundamental] -pub struct Box<T: ?Sized, A: Allocator = Global>; +pub struct Box<T: ?Sized, A: Allocator = Global>(T); impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {} fn send() -> Box<dyn Future<Output = ()> + Send + 'static>{ - box async move {} + Box(async move {}) } fn not_send() -> Box<dyn Future<Output = ()> + 'static> { - box async move {} + Box(async move {}) } "#, ); @@ -3057,7 +3057,7 @@ impl<T: ?Sized> core::ops::Deref for Box<T> { fn foo() { let s = None; - let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {}); + let f: Box<dyn FnOnce(&Option<i32>)> = Box { inner: &mut (|ps| {}) }; f(&s); }"#, expect![[r#" @@ -3068,19 +3068,19 @@ fn foo() { 186..197 '*self.inner': T 187..191 'self': &Box<T> 187..197 'self.inner': *mut T - 218..308 '{ ...&s); }': () + 218..324 '{ ...&s); }': () 228..229 's': Option<i32> 232..236 'None': Option<i32> 246..247 'f': Box<dyn FnOnce(&Option<i32>)> - 281..294 'box (|ps| {})': Box<impl Fn(&Option<i32>)> - 286..293 '|ps| {}': impl Fn(&Option<i32>) - 287..289 'ps': &Option<i32> - 291..293 '{}': () - 300..301 'f': Box<dyn FnOnce(&Option<i32>)> - 300..305 'f(&s)': () - 302..304 '&s': &Option<i32> - 303..304 's': Option<i32> - 281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<impl Fn(&Option<i32>)> + 281..310 'Box { ... {}) }': Box<dyn FnOnce(&Option<i32>)> + 294..308 '&mut (|ps| {})': &mut impl Fn(&Option<i32>) + 300..307 '|ps| {}': impl Fn(&Option<i32>) + 301..303 'ps': &Option<i32> + 305..307 '{}': () + 316..317 'f': Box<dyn FnOnce(&Option<i32>)> + 316..321 'f(&s)': () + 318..320 '&s': &Option<i32> + 319..320 's': Option<i32> "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 121e5a0a249..796490abd7f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -1,39 +1,27 @@ //! Attributes & documentation for hir types. use hir_def::{ - attr::{AttrsWithOwner, Documentation}, + attr::AttrsWithOwner, item_scope::ItemInNs, path::{ModPath, Path}, per_ns::Namespace, resolver::{HasResolver, Resolver, TypeNs}, - AssocItemId, AttrDefId, GenericParamId, ModuleDefId, + AssocItemId, AttrDefId, ModuleDefId, }; use hir_expand::{hygiene::Hygiene, name::Name}; use hir_ty::db::HirDatabase; use syntax::{ast, AstNode}; use crate::{ - Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, Enum, ExternCrateDecl, Field, - Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, - TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef, + Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, + Field, Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct, + Trait, TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef, }; pub trait HasAttrs { fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner; - fn docs(self, db: &dyn HirDatabase) -> Option<Documentation>; - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option<Namespace>, - ) -> Option<DocLinkDef>; -} - -/// Subset of `ide_db::Definition` that doc links can resolve to. -pub enum DocLinkDef { - ModuleDef(ModuleDef), - Field(Field), - SelfType(Trait), + #[doc(hidden)] + fn attr_id(self) -> AttrDefId; } macro_rules! impl_has_attrs { @@ -43,18 +31,8 @@ macro_rules! impl_has_attrs { let def = AttrDefId::$def_id(self.into()); db.attrs_with_owner(def) } - fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { - let def = AttrDefId::$def_id(self.into()); - db.attrs(def).docs() - } - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option<Namespace> - ) -> Option<DocLinkDef> { - let def = AttrDefId::$def_id(self.into()); - resolve_doc_path(db, def, link, ns) + fn attr_id(self) -> AttrDefId { + AttrDefId::$def_id(self.into()) } } )*}; @@ -74,6 +52,7 @@ impl_has_attrs![ (Module, ModuleId), (GenericParam, GenericParamId), (Impl, ImplId), + (ExternCrateDecl, ExternCrateId), ]; macro_rules! impl_has_attrs_enum { @@ -82,16 +61,8 @@ macro_rules! impl_has_attrs_enum { fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { $enum::$variant(self).attrs(db) } - fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { - $enum::$variant(self).docs(db) - } - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option<Namespace> - ) -> Option<DocLinkDef> { - $enum::$variant(self).resolve_doc_path(db, link, ns) + fn attr_id(self) -> AttrDefId { + $enum::$variant(self).attr_id() } } )*}; @@ -108,70 +79,35 @@ impl HasAttrs for AssocItem { AssocItem::TypeAlias(it) => it.attrs(db), } } - - fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { + fn attr_id(self) -> AttrDefId { match self { - AssocItem::Function(it) => it.docs(db), - AssocItem::Const(it) => it.docs(db), - AssocItem::TypeAlias(it) => it.docs(db), - } - } - - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option<Namespace>, - ) -> Option<DocLinkDef> { - match self { - AssocItem::Function(it) => it.resolve_doc_path(db, link, ns), - AssocItem::Const(it) => it.resolve_doc_path(db, link, ns), - AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns), + AssocItem::Function(it) => it.attr_id(), + AssocItem::Const(it) => it.attr_id(), + AssocItem::TypeAlias(it) => it.attr_id(), } } } -impl HasAttrs for ExternCrateDecl { - fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { - let def = AttrDefId::ExternCrateId(self.into()); - db.attrs_with_owner(def) - } - fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { - let crate_docs = self.resolved_crate(db)?.root_module().attrs(db).docs().map(String::from); - let def = AttrDefId::ExternCrateId(self.into()); - let decl_docs = db.attrs(def).docs().map(String::from); - match (decl_docs, crate_docs) { - (None, None) => None, - (Some(decl_docs), None) => Some(decl_docs), - (None, Some(crate_docs)) => Some(crate_docs), - (Some(mut decl_docs), Some(crate_docs)) => { - decl_docs.push('\n'); - decl_docs.push('\n'); - decl_docs += &crate_docs; - Some(decl_docs) - } - } - .map(Documentation::new) - } - fn resolve_doc_path( - self, - db: &dyn HirDatabase, - link: &str, - ns: Option<Namespace>, - ) -> Option<DocLinkDef> { - let def = AttrDefId::ExternCrateId(self.into()); - resolve_doc_path(db, def, link, ns) - } +/// Resolves the item `link` points to in the scope of `def`. +pub fn resolve_doc_path_on( + db: &dyn HirDatabase, + def: impl HasAttrs, + link: &str, + ns: Option<Namespace>, +) -> Option<DocLinkDef> { + // AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), + // AttrDefId::EnumVariantId(it) => it.parent.resolver(db.upcast()), + + resolve_doc_path_on_(db, link, def.attr_id(), ns) } -/// Resolves the item `link` points to in the scope of `def`. -fn resolve_doc_path( +fn resolve_doc_path_on_( db: &dyn HirDatabase, - def: AttrDefId, link: &str, + attr_id: AttrDefId, ns: Option<Namespace>, ) -> Option<DocLinkDef> { - let resolver = match def { + let resolver = match attr_id { AttrDefId::ModuleId(it) => it.resolver(db.upcast()), AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()), AttrDefId::AdtId(it) => it.resolver(db.upcast()), @@ -187,12 +123,7 @@ fn resolve_doc_path( AttrDefId::UseId(it) => it.resolver(db.upcast()), AttrDefId::MacroId(it) => it.resolver(db.upcast()), AttrDefId::ExternCrateId(it) => it.resolver(db.upcast()), - AttrDefId::GenericParamId(it) => match it { - GenericParamId::TypeParamId(it) => it.parent(), - GenericParamId::ConstParamId(it) => it.parent(), - GenericParamId::LifetimeParamId(it) => it.parent, - } - .resolver(db.upcast()), + AttrDefId::GenericParamId(_) => return None, }; let mut modpath = modpath_from_str(db, link)?; diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 80c3bcdca8b..479138b67f8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -43,6 +43,7 @@ diagnostics![ MacroExpansionParseError, MalformedDerive, MismatchedArgCount, + MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe, @@ -172,7 +173,8 @@ pub struct MalformedDerive { #[derive(Debug)] pub struct NoSuchField { - pub field: InFile<AstPtr<ast::RecordExprField>>, + pub field: InFile<Either<AstPtr<ast::RecordExprField>, AstPtr<ast::RecordPatField>>>, + pub private: bool, } #[derive(Debug)] @@ -183,6 +185,13 @@ pub struct PrivateAssocItem { } #[derive(Debug)] +pub struct MismatchedTupleStructPatArgCount { + pub expr_or_pat: InFile<Either<AstPtr<ast::Expr>, AstPtr<ast::Pat>>>, + pub expected: usize, + pub found: usize, +} + +#[derive(Debug)] pub struct ExpectedFunction { pub call: InFile<AstPtr<ast::Expr>>, pub found: Type, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 512fe7e0428..b215ed38f28 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -88,13 +88,14 @@ use triomphe::Arc; use crate::db::{DefDatabase, HirDatabase}; pub use crate::{ - attrs::{DocLinkDef, HasAttrs}, + attrs::{resolve_doc_path_on, HasAttrs}, diagnostics::{ AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, - MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields, - MissingMatchArms, MissingUnsafe, MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, - PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, + MacroExpansionParseError, MalformedDerive, MismatchedArgCount, + MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe, + MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, + ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut, @@ -115,7 +116,7 @@ pub use crate::{ pub use { cfg::{CfgAtom, CfgExpr, CfgOptions}, hir_def::{ - attr::{builtin::AttributeTemplate, Attrs, AttrsWithOwner, Documentation}, + attr::{builtin::AttributeTemplate, AttrSourceMap, Attrs, AttrsWithOwner}, data::adt::StructKind, find_path::PrefixKind, import_map, @@ -130,7 +131,7 @@ pub use { {AdtId, ModuleDefId}, }, hir_expand::{ - attrs::Attr, + attrs::{Attr, AttrId}, name::{known, Name}, ExpandResult, HirFileId, InFile, MacroFile, Origin, }, @@ -563,8 +564,8 @@ impl Module { emit_def_diagnostic(db, acc, diag); } - for decl in self.declarations(db) { - match decl { + for def in self.declarations(db) { + match def { ModuleDef::Module(m) => { // Only add diagnostics from inline modules if def_map[m.id.local_id].origin.is_inline() { @@ -575,7 +576,7 @@ impl Module { for diag in db.trait_data_with_diagnostics(t.id).1.iter() { emit_def_diagnostic(db, acc, diag); } - acc.extend(decl.diagnostics(db)) + acc.extend(def.diagnostics(db)) } ModuleDef::Adt(adt) => { match adt { @@ -599,10 +600,10 @@ impl Module { } } } - acc.extend(decl.diagnostics(db)) + acc.extend(def.diagnostics(db)) } ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m), - _ => acc.extend(decl.diagnostics(db)), + _ => acc.extend(def.diagnostics(db)), } } self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m)); @@ -1446,6 +1447,7 @@ impl DefWithBody { } pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) { + db.unwind_if_cancelled(); let krate = self.module(db).id.krate(); let (body, source_map) = db.body_with_source_map(self.into()); @@ -1501,11 +1503,19 @@ impl DefWithBody { let infer = db.infer(self.into()); let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1); let expr_syntax = |expr| source_map.expr_syntax(expr).expect("unexpected synthetic"); + let pat_syntax = |pat| source_map.pat_syntax(pat).expect("unexpected synthetic"); for d in &infer.diagnostics { match d { - &hir_ty::InferenceDiagnostic::NoSuchField { expr } => { - let field = source_map.field_syntax(expr); - acc.push(NoSuchField { field }.into()) + &hir_ty::InferenceDiagnostic::NoSuchField { field: expr, private } => { + let expr_or_pat = match expr { + ExprOrPatId::ExprId(expr) => { + source_map.field_syntax(expr).map(Either::Left) + } + ExprOrPatId::PatId(pat) => { + source_map.pat_field_syntax(pat).map(Either::Right) + } + }; + acc.push(NoSuchField { field: expr_or_pat, private }.into()) } &hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { acc.push( @@ -1521,10 +1531,7 @@ impl DefWithBody { &hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => { let expr_or_pat = match id { ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left), - ExprOrPatId::PatId(pat) => source_map - .pat_syntax(pat) - .expect("unexpected synthetic") - .map(Either::Right), + ExprOrPatId::PatId(pat) => pat_syntax(pat).map(Either::Right), }; let item = item.into(); acc.push(PrivateAssocItem { expr_or_pat, item }.into()) @@ -1596,6 +1603,23 @@ impl DefWithBody { .into(), ) } + &hir_ty::InferenceDiagnostic::MismatchedTupleStructPatArgCount { + pat, + expected, + found, + } => { + let expr_or_pat = match pat { + ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left), + ExprOrPatId::PatId(pat) => source_map + .pat_syntax(pat) + .expect("unexpected synthetic") + .map(|it| it.unwrap_left()) + .map(Either::Right), + }; + acc.push( + MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into(), + ) + } } } for (pat_or_expr, mismatch) in infer.type_mismatches() { @@ -4838,3 +4862,10 @@ pub enum ItemContainer { ExternBlock(), Crate(CrateId), } + +/// Subset of `ide_db::Definition` that doc links can resolve to. +pub enum DocLinkDef { + ModuleDef(ModuleDef), + Field(Field), + SelfType(Trait), +} diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index b8d4ecd4414..a42e0978b25 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -127,165 +127,24 @@ impl<DB> fmt::Debug for Semantics<'_, DB> { } } +impl<'db, DB> ops::Deref for Semantics<'db, DB> { + type Target = SemanticsImpl<'db>; + + fn deref(&self) -> &Self::Target { + &self.imp + } +} + impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn new(db: &DB) -> Semantics<'_, DB> { let impl_ = SemanticsImpl::new(db); Semantics { db, imp: impl_ } } - pub fn parse(&self, file_id: FileId) -> ast::SourceFile { - self.imp.parse(file_id) - } - - pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode { - self.imp.parse_or_expand(file_id) - } - - pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { - self.imp.expand(macro_call) - } - - /// If `item` has an attribute macro attached to it, expands it. - pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> { - self.imp.expand_attr_macro(item) - } - - pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> { - self.imp.expand_derive_as_pseudo_attr_macro(attr) - } - - pub fn resolve_derive_macro(&self, derive: &ast::Attr) -> Option<Vec<Option<Macro>>> { - self.imp.resolve_derive_macro(derive) - } - - pub fn expand_derive_macro(&self, derive: &ast::Attr) -> Option<Vec<SyntaxNode>> { - self.imp.expand_derive_macro(derive) - } - - pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool { - self.imp.is_attr_macro_call(item) - } - - pub fn is_derive_annotated(&self, item: &ast::Adt) -> bool { - self.imp.is_derive_annotated(item) - } - - /// Expand the macro call with a different token tree, mapping the `token_to_map` down into the - /// expansion. `token_to_map` should be a token from the `speculative args` node. - pub fn speculative_expand( - &self, - actual_macro_call: &ast::MacroCall, - speculative_args: &ast::TokenTree, - token_to_map: SyntaxToken, - ) -> Option<(SyntaxNode, SyntaxToken)> { - self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map) - } - - /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the - /// expansion. `token_to_map` should be a token from the `speculative args` node. - pub fn speculative_expand_attr_macro( - &self, - actual_macro_call: &ast::Item, - speculative_args: &ast::Item, - token_to_map: SyntaxToken, - ) -> Option<(SyntaxNode, SyntaxToken)> { - self.imp.speculative_expand_attr(actual_macro_call, speculative_args, token_to_map) - } - - pub fn speculative_expand_derive_as_pseudo_attr_macro( - &self, - actual_macro_call: &ast::Attr, - speculative_args: &ast::Attr, - token_to_map: SyntaxToken, - ) -> Option<(SyntaxNode, SyntaxToken)> { - self.imp.speculative_expand_derive_as_pseudo_attr_macro( - actual_macro_call, - speculative_args, - token_to_map, - ) - } - - /// Descend the token into its macro call if it is part of one, returning the token in the - /// expansion that it is associated with. If `offset` points into the token's range, it will - /// be considered for the mapping in case of inline format args. - pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken { - self.imp.descend_into_macros_single(token, offset) - } - - /// Descend the token into its macro call if it is part of one, returning the tokens in the - /// expansion that it is associated with. If `offset` points into the token's range, it will - /// be considered for the mapping in case of inline format args. - pub fn descend_into_macros( - &self, - token: SyntaxToken, - offset: TextSize, - ) -> SmallVec<[SyntaxToken; 1]> { - self.imp.descend_into_macros(token, offset) - } - - /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token. - /// - /// Returns the original non descended token if none of the mapped counterparts have the same text. - pub fn descend_into_macros_with_same_text( - &self, - token: SyntaxToken, - offset: TextSize, - ) -> SmallVec<[SyntaxToken; 1]> { - self.imp.descend_into_macros_with_same_text(token, offset) - } - - pub fn descend_into_macros_with_kind_preference( - &self, - token: SyntaxToken, - offset: TextSize, - ) -> SyntaxToken { - self.imp.descend_into_macros_with_kind_preference(token, offset) - } - - /// Maps a node down by mapping its first and last token down. - pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> { - self.imp.descend_node_into_attributes(node) - } - - /// Search for a definition's source and cache its syntax tree - pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>> - where - Def::Ast: AstNode, - { - self.imp.source(def) - } - pub fn hir_file_for(&self, syntax_node: &SyntaxNode) -> HirFileId { self.imp.find_file(syntax_node).file_id } - /// Attempts to map the node out of macro expanded files returning the original file range. - /// If upmapping is not possible, this will fall back to the range of the macro call of the - /// macro file the node resides in. - pub fn original_range(&self, node: &SyntaxNode) -> FileRange { - self.imp.original_range(node) - } - - /// Attempts to map the node out of macro expanded files returning the original file range. - pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> { - self.imp.original_range_opt(node) - } - - /// Attempts to map the node out of macro expanded files. - /// This only work for attribute expansions, as other ones do not have nodes as input. - pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> { - self.imp.original_ast_node(node) - } - /// Attempts to map the node out of macro expanded files. - /// This only work for attribute expansions, as other ones do not have nodes as input. - pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> { - self.imp.original_syntax_node(node) - } - - pub fn diagnostics_display_range(&self, diagnostics: InFile<SyntaxNodePtr>) -> FileRange { - self.imp.diagnostics_display_range(diagnostics) - } - pub fn token_ancestors_with_macros( &self, token: SyntaxToken, @@ -293,19 +152,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { token.parent().into_iter().flat_map(move |it| self.ancestors_with_macros(it)) } - /// Iterates the ancestors of the given node, climbing up macro expansions while doing so. - pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { - self.imp.ancestors_with_macros(node) - } - - pub fn ancestors_at_offset_with_macros( - &self, - node: &SyntaxNode, - offset: TextSize, - ) -> impl Iterator<Item = SyntaxNode> + '_ { - self.imp.ancestors_at_offset_with_macros(node, offset) - } - /// Find an AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, /// search up until it is of the target AstNode type pub fn find_node_at_offset_with_macros<N: AstNode>( @@ -336,53 +182,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast)) } - pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> { - self.imp.resolve_lifetime_param(lifetime) - } - - pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> { - self.imp.resolve_label(lifetime) - } - - pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> { - self.imp.resolve_type(ty) - } - - pub fn resolve_trait(&self, trait_: &ast::Path) -> Option<Trait> { - self.imp.resolve_trait(trait_) - } - - pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> { - self.imp.expr_adjustments(expr) - } - - pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> { - self.imp.type_of_expr(expr) - } - - pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> { - self.imp.type_of_pat(pat) - } - - /// It also includes the changes that binding mode makes in the type. For example in - /// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option<T>` but the result - /// of this function is `&mut Option<T>` - pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> { - self.imp.type_of_binding_in_pat(pat) - } - - pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> { - self.imp.type_of_self(param) - } - - pub fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> { - self.imp.pattern_adjustments(pat) - } - - pub fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> { - self.imp.binding_mode_of_pat(pat) - } - pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { self.imp.resolve_method_call(call).map(Function::from) } @@ -417,61 +216,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_try_expr(try_expr).map(Function::from) } - pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { - self.imp.resolve_method_call_as_callable(call) - } - - pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> { - self.imp.resolve_field(field) - } - - pub fn resolve_record_field( - &self, - field: &ast::RecordExprField, - ) -> Option<(Field, Option<Local>, Type)> { - self.imp.resolve_record_field(field) - } - - pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> { - self.imp.resolve_record_pat_field(field) - } - - pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> { - self.imp.resolve_macro_call(macro_call) - } - - pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { - self.imp.is_unsafe_macro_call(macro_call) - } - - pub fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> { - self.imp.resolve_attr_macro_call(item) - } - - pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> { - self.imp.resolve_path(path) - } - pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> { self.imp.resolve_variant(record_lit).map(VariantDef::from) } - pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> { - self.imp.resolve_bind_pat_to_const(pat) - } - - pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> { - self.imp.record_literal_missing_fields(literal) - } - - pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> { - self.imp.record_pattern_missing_fields(pattern) - } - - pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> { - self.imp.to_def(src) - } - pub fn to_module_def(&self, file: FileId) -> Option<Module> { self.imp.to_module_def(file).next() } @@ -479,39 +227,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> { self.imp.to_module_def(file) } - - pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> { - self.imp.scope(node) - } - - pub fn scope_at_offset( - &self, - node: &SyntaxNode, - offset: TextSize, - ) -> Option<SemanticsScope<'db>> { - self.imp.scope_at_offset(node, offset) - } - - pub fn assert_contains_node(&self, node: &SyntaxNode) { - self.imp.assert_contains_node(node) - } - - pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool { - self.imp.is_unsafe_method_call(method_call_expr) - } - - pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { - self.imp.is_unsafe_ref_expr(ref_expr) - } - - pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { - self.imp.is_unsafe_ident_pat(ident_pat) - } - - /// Returns `true` if the `node` is inside an `unsafe` context. - pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool { - self.imp.is_inside_unsafe(expr) - } } impl<'db> SemanticsImpl<'db> { @@ -525,32 +240,33 @@ impl<'db> SemanticsImpl<'db> { } } - fn parse(&self, file_id: FileId) -> ast::SourceFile { + pub fn parse(&self, file_id: FileId) -> ast::SourceFile { let tree = self.db.parse(file_id).tree(); self.cache(tree.syntax().clone(), file_id.into()); tree } - fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode { + pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode { let node = self.db.parse_or_expand(file_id); self.cache(node.clone(), file_id); node } - fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { + pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { let sa = self.analyze_no_infer(macro_call.syntax())?; let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?; let node = self.parse_or_expand(file_id); Some(node) } - fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> { + /// If `item` has an attribute macro attached to it, expands it. + pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> { let src = self.wrap_node_infile(item.clone()); let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?; Some(self.parse_or_expand(macro_call_id.as_file())) } - fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> { + pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> { let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; let src = self.wrap_node_infile(attr.clone()); let call_id = self.with_ctx(|ctx| { @@ -559,7 +275,7 @@ impl<'db> SemanticsImpl<'db> { Some(self.parse_or_expand(call_id.as_file())) } - fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> { + pub fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> { let calls = self.derive_macro_calls(attr)?; self.with_ctx(|ctx| { Some( @@ -573,7 +289,7 @@ impl<'db> SemanticsImpl<'db> { }) } - fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<SyntaxNode>> { + pub fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<SyntaxNode>> { let res: Vec<_> = self .derive_macro_calls(attr)? .into_iter() @@ -598,19 +314,21 @@ impl<'db> SemanticsImpl<'db> { }) } - fn is_derive_annotated(&self, adt: &ast::Adt) -> bool { + pub fn is_derive_annotated(&self, adt: &ast::Adt) -> bool { let file_id = self.find_file(adt.syntax()).file_id; let adt = InFile::new(file_id, adt); self.with_ctx(|ctx| ctx.has_derives(adt)) } - fn is_attr_macro_call(&self, item: &ast::Item) -> bool { + pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool { let file_id = self.find_file(item.syntax()).file_id; let src = InFile::new(file_id, item.clone()); self.with_ctx(|ctx| ctx.item_to_macro_call(src).is_some()) } - fn speculative_expand( + /// Expand the macro call with a different token tree, mapping the `token_to_map` down into the + /// expansion. `token_to_map` should be a token from the `speculative args` node. + pub fn speculative_expand( &self, actual_macro_call: &ast::MacroCall, speculative_args: &ast::TokenTree, @@ -633,7 +351,9 @@ impl<'db> SemanticsImpl<'db> { ) } - fn speculative_expand_attr( + /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the + /// expansion. `token_to_map` should be a token from the `speculative args` node. + pub fn speculative_expand_attr_macro( &self, actual_macro_call: &ast::Item, speculative_args: &ast::Item, @@ -649,7 +369,7 @@ impl<'db> SemanticsImpl<'db> { ) } - fn speculative_expand_derive_as_pseudo_attr_macro( + pub fn speculative_expand_derive_as_pseudo_attr_macro( &self, actual_macro_call: &ast::Attr, speculative_args: &ast::Attr, @@ -668,8 +388,9 @@ impl<'db> SemanticsImpl<'db> { ) } - // This might not be the correct way to do this, but it works for now - fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> { + /// Maps a node down by mapping its first and last token down. + pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> { + // This might not be the correct way to do this, but it works for now let mut res = smallvec![]; let tokens = (|| { let first = skip_trivia_token(node.syntax().first_token()?, Direction::Next)?; @@ -723,7 +444,10 @@ impl<'db> SemanticsImpl<'db> { res } - fn descend_into_macros( + /// Descend the token into its macro call if it is part of one, returning the tokens in the + /// expansion that it is associated with. If `offset` points into the token's range, it will + /// be considered for the mapping in case of inline format args. + pub fn descend_into_macros( &self, token: SyntaxToken, offset: TextSize, @@ -736,7 +460,10 @@ impl<'db> SemanticsImpl<'db> { res } - fn descend_into_macros_with_same_text( + /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token. + /// + /// Returns the original non descended token if none of the mapped counterparts have the same text. + pub fn descend_into_macros_with_same_text( &self, token: SyntaxToken, offset: TextSize, @@ -755,7 +482,7 @@ impl<'db> SemanticsImpl<'db> { res } - fn descend_into_macros_with_kind_preference( + pub fn descend_into_macros_with_kind_preference( &self, token: SyntaxToken, offset: TextSize, @@ -785,7 +512,10 @@ impl<'db> SemanticsImpl<'db> { res.unwrap_or(token) } - fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken { + /// Descend the token into its macro call if it is part of one, returning the token in the + /// expansion that it is associated with. If `offset` points into the token's range, it will + /// be considered for the mapping in case of inline format args. + pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken { let mut res = token.clone(); self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| { res = value; @@ -995,17 +725,23 @@ impl<'db> SemanticsImpl<'db> { }) } - fn original_range(&self, node: &SyntaxNode) -> FileRange { + /// Attempts to map the node out of macro expanded files returning the original file range. + /// If upmapping is not possible, this will fall back to the range of the macro call of the + /// macro file the node resides in. + pub fn original_range(&self, node: &SyntaxNode) -> FileRange { let node = self.find_file(node); node.original_file_range(self.db.upcast()) } - fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> { + /// Attempts to map the node out of macro expanded files returning the original file range. + pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> { let node = self.find_file(node); node.original_file_range_opt(self.db.upcast()) } - fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> { + /// Attempts to map the node out of macro expanded files. + /// This only work for attribute expansions, as other ones do not have nodes as input. + pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> { self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map( |InFile { file_id, value }| { self.cache(find_root(value.syntax()), file_id); @@ -1014,7 +750,9 @@ impl<'db> SemanticsImpl<'db> { ) } - fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> { + /// Attempts to map the node out of macro expanded files. + /// This only work for attribute expansions, as other ones do not have nodes as input. + pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> { let InFile { file_id, .. } = self.find_file(node); InFile::new(file_id, node).original_syntax_node(self.db.upcast()).map( |InFile { file_id, value }| { @@ -1024,7 +762,7 @@ impl<'db> SemanticsImpl<'db> { ) } - fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange { + pub fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange { let root = self.parse_or_expand(src.file_id); let node = src.map(|it| it.to_node(&root)); node.as_ref().original_file_range(self.db.upcast()) @@ -1037,7 +775,8 @@ impl<'db> SemanticsImpl<'db> { token.parent().into_iter().flat_map(move |parent| self.ancestors_with_macros(parent)) } - fn ancestors_with_macros( + /// Iterates the ancestors of the given node, climbing up macro expansions while doing so. + pub fn ancestors_with_macros( &self, node: SyntaxNode, ) -> impl Iterator<Item = SyntaxNode> + Clone + '_ { @@ -1055,7 +794,7 @@ impl<'db> SemanticsImpl<'db> { .map(|it| it.value) } - fn ancestors_at_offset_with_macros( + pub fn ancestors_at_offset_with_macros( &self, node: &SyntaxNode, offset: TextSize, @@ -1065,7 +804,7 @@ impl<'db> SemanticsImpl<'db> { .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) } - fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> { + pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> { let text = lifetime.text(); let lifetime_param = lifetime.syntax().ancestors().find_map(|syn| { let gpl = ast::AnyHasGenericParams::cast(syn)?.generic_param_list()?; @@ -1076,7 +815,7 @@ impl<'db> SemanticsImpl<'db> { ToDef::to_def(self, src) } - fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> { + pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> { let text = lifetime.text(); let label = lifetime.syntax().ancestors().find_map(|syn| { let label = match_ast! { @@ -1098,7 +837,7 @@ impl<'db> SemanticsImpl<'db> { ToDef::to_def(self, src) } - fn resolve_type(&self, ty: &ast::Type) -> Option<Type> { + pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> { let analyze = self.analyze(ty.syntax())?; let ctx = LowerCtx::with_file_id(self.db.upcast(), analyze.file_id); let ty = hir_ty::TyLoweringContext::new( @@ -1110,7 +849,7 @@ impl<'db> SemanticsImpl<'db> { Some(Type::new_with_resolver(self.db, &analyze.resolver, ty)) } - fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> { + pub fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> { let analyze = self.analyze(path.syntax())?; let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id); let ctx = LowerCtx::with_hygiene(self.db.upcast(), &hygiene); @@ -1121,7 +860,7 @@ impl<'db> SemanticsImpl<'db> { } } - fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> { + pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> { let mutability = |m| match m { hir_ty::Mutability::Not => Mutability::Shared, hir_ty::Mutability::Mut => Mutability::Mut, @@ -1165,33 +904,36 @@ impl<'db> SemanticsImpl<'db> { }) } - fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> { + pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> { self.analyze(expr.syntax())? .type_of_expr(self.db, expr) .map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced }) } - fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> { + pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> { self.analyze(pat.syntax())? .type_of_pat(self.db, pat) .map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced }) } - fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> { + /// It also includes the changes that binding mode makes in the type. For example in + /// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option<T>` but the result + /// of this function is `&mut Option<T>` + pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> { self.analyze(pat.syntax())?.type_of_binding_in_pat(self.db, pat) } - fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> { + pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> { self.analyze(param.syntax())?.type_of_self(self.db, param) } - fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> { + pub fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> { self.analyze(pat.syntax()) .and_then(|it| it.pattern_adjustments(self.db, pat)) .unwrap_or_default() } - fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> { + pub fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> { self.analyze(pat.syntax())?.binding_mode_of_pat(self.db, pat) } @@ -1226,32 +968,32 @@ impl<'db> SemanticsImpl<'db> { self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) } - fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { + pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call) } - fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> { + pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> { self.analyze(field.syntax())?.resolve_field(self.db, field) } - fn resolve_record_field( + pub fn resolve_record_field( &self, field: &ast::RecordExprField, ) -> Option<(Field, Option<Local>, Type)> { self.analyze(field.syntax())?.resolve_record_field(self.db, field) } - fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> { + pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> { self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field) } - fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> { + pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> { let sa = self.analyze(macro_call.syntax())?; let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call); sa.resolve_macro_call(self.db, macro_call) } - fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { + pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { let sa = match self.analyze(macro_call.syntax()) { Some(it) => it, None => return false, @@ -1260,7 +1002,7 @@ impl<'db> SemanticsImpl<'db> { sa.is_unsafe_macro_call(self.db, macro_call) } - fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> { + pub fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> { let item_in_file = self.wrap_node_infile(item.clone()); let id = self.with_ctx(|ctx| { let macro_call_id = ctx.item_to_macro_call(item_in_file)?; @@ -1269,7 +1011,7 @@ impl<'db> SemanticsImpl<'db> { Some(Macro { id }) } - fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> { + pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> { self.analyze(path.syntax())?.resolve_path(self.db, path) } @@ -1277,17 +1019,17 @@ impl<'db> SemanticsImpl<'db> { self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit) } - fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> { + pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> { self.analyze(pat.syntax())?.resolve_bind_pat_to_const(self.db, pat) } - fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> { + pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> { self.analyze(literal.syntax()) .and_then(|it| it.record_literal_missing_fields(self.db, literal)) .unwrap_or_default() } - fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> { + pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> { self.analyze(pattern.syntax()) .and_then(|it| it.record_pattern_missing_fields(self.db, pattern)) .unwrap_or_default() @@ -1299,7 +1041,7 @@ impl<'db> SemanticsImpl<'db> { f(&mut ctx) } - fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> { + pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> { let src = self.find_file(src.syntax()).with_value(src).cloned(); T::to_def(self, src) } @@ -1308,7 +1050,7 @@ impl<'db> SemanticsImpl<'db> { self.with_ctx(|ctx| ctx.file_to_def(file)).into_iter().map(Module::from) } - fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> { + pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> { self.analyze_no_infer(node).map(|SourceAnalyzer { file_id, resolver, .. }| SemanticsScope { db: self.db, file_id, @@ -1316,7 +1058,11 @@ impl<'db> SemanticsImpl<'db> { }) } - fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> Option<SemanticsScope<'db>> { + pub fn scope_at_offset( + &self, + node: &SyntaxNode, + offset: TextSize, + ) -> Option<SemanticsScope<'db>> { self.analyze_with_offset_no_infer(node, offset).map( |SourceAnalyzer { file_id, resolver, .. }| SemanticsScope { db: self.db, @@ -1326,7 +1072,8 @@ impl<'db> SemanticsImpl<'db> { ) } - fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>> + /// Search for a definition's source and cache its syntax tree + pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>> where Def::Ast: AstNode, { @@ -1391,7 +1138,7 @@ impl<'db> SemanticsImpl<'db> { assert!(prev == None || prev == Some(file_id)) } - fn assert_contains_node(&self, node: &SyntaxNode) { + pub fn assert_contains_node(&self, node: &SyntaxNode) { self.find_file(node); } @@ -1427,7 +1174,7 @@ impl<'db> SemanticsImpl<'db> { InFile::new(file_id, node) } - fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool { + pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool { method_call_expr .receiver() .and_then(|expr| { @@ -1450,7 +1197,7 @@ impl<'db> SemanticsImpl<'db> { .unwrap_or(false) } - fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { + pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { ref_expr .expr() .and_then(|expr| { @@ -1469,7 +1216,7 @@ impl<'db> SemanticsImpl<'db> { // more than it should with the current implementation. } - fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { + pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { if ident_pat.ref_token().is_none() { return false; } @@ -1512,7 +1259,8 @@ impl<'db> SemanticsImpl<'db> { .unwrap_or(false) } - fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool { + /// Returns `true` if the `node` is inside an `unsafe` context. + pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool { let Some(enclosing_item) = expr.syntax().ancestors().find_map(Either::<ast::Item, ast::Variant>::cast) else { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs new file mode 100644 index 00000000000..45c1f0ccae3 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs @@ -0,0 +1,159 @@ +use crate::assist_context::{AssistContext, Assists}; +use ide_db::{ + assists::{AssistId, AssistKind}, + defs::Definition, + LineIndexDatabase, +}; +use syntax::{ + ast::{self, edit_in_place::Indent}, + AstNode, +}; + +// Assist: bind_unused_param +// +// Binds unused function parameter to an underscore. +// +// ``` +// fn some_function(x: i32$0) {} +// ``` +// -> +// ``` +// fn some_function(x: i32) { +// let _ = x; +// } +// ``` +pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let param: ast::Param = ctx.find_node_at_offset()?; + + let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None }; + + let param_def = { + let local = ctx.sema.to_def(&ident_pat)?; + Definition::Local(local) + }; + if param_def.usages(&ctx.sema).at_least_one() { + cov_mark::hit!(keep_used); + return None; + } + + let func = param.syntax().ancestors().find_map(ast::Fn::cast)?; + let stmt_list = func.body()?.stmt_list()?; + let l_curly_range = stmt_list.l_curly_token()?.text_range(); + let r_curly_range = stmt_list.r_curly_token()?.text_range(); + + acc.add( + AssistId("bind_unused_param", AssistKind::QuickFix), + &format!("Bind as `let _ = {};`", &ident_pat), + param.syntax().text_range(), + |builder| { + let line_index = ctx.db().line_index(ctx.file_id()); + + let indent = func.indent_level(); + let text_indent = indent + 1; + let mut text = format!("\n{text_indent}let _ = {ident_pat};"); + + let left_line = line_index.line_col(l_curly_range.end()).line; + let right_line = line_index.line_col(r_curly_range.start()).line; + + if left_line == right_line { + cov_mark::hit!(single_line); + text.push_str(&format!("\n{indent}")); + } + + builder.insert(l_curly_range.end(), text); + }, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn bind_unused_empty_block() { + cov_mark::check!(single_line); + check_assist( + bind_unused_param, + r#" +fn foo($0y: i32) {} +"#, + r#" +fn foo(y: i32) { + let _ = y; +} +"#, + ); + } + + #[test] + fn bind_unused_empty_block_with_newline() { + check_assist( + bind_unused_param, + r#" +fn foo($0y: i32) { +} +"#, + r#" +fn foo(y: i32) { + let _ = y; +} +"#, + ); + } + + #[test] + fn bind_unused_generic() { + check_assist( + bind_unused_param, + r#" +fn foo<T>($0y: T) +where T : Default { +} +"#, + r#" +fn foo<T>(y: T) +where T : Default { + let _ = y; +} +"#, + ); + } + + #[test] + fn trait_impl() { + check_assist( + bind_unused_param, + r#" +trait Trait { + fn foo(x: i32); +} +impl Trait for () { + fn foo($0x: i32) {} +} +"#, + r#" +trait Trait { + fn foo(x: i32); +} +impl Trait for () { + fn foo(x: i32) { + let _ = x; + } +} +"#, + ); + } + + #[test] + fn keep_used() { + cov_mark::check!(keep_used); + check_assist_not_applicable( + bind_unused_param, + r#" +fn foo(x: i32, $0y: i32) { y; } +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index 1af52c59218..d231708c55b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -103,7 +103,6 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_> cond, ast::Expr::BinExpr(_) | ast::Expr::BlockExpr(_) - | ast::Expr::BoxExpr(_) | ast::Expr::BreakExpr(_) | ast::Expr::CastExpr(_) | ast::Expr::ClosureExpr(_) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index c3d925cb26c..31a1ff496e1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -15,26 +15,13 @@ use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange}; // Move an expression out of a format string. // // ``` -// macro_rules! format_args { -// ($lit:literal $(tt:tt)*) => { 0 }, -// } -// macro_rules! print { -// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); -// } -// +// # //- minicore: fmt // fn main() { // print!("{var} {x + 1}$0"); // } // ``` // -> // ``` -// macro_rules! format_args { -// ($lit:literal $(tt:tt)*) => { 0 }, -// } -// macro_rules! print { -// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); -// } -// // fn main() { // print!("{var} {}"$0, x + 1); // } @@ -158,37 +145,21 @@ mod tests { use super::*; use crate::tests::check_assist; - const MACRO_DECL: &'static str = r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} -macro_rules! print { - ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); -} -"#; - - fn add_macro_decl(s: &'static str) -> String { - MACRO_DECL.to_string() + s - } - #[test] fn multiple_middle_arg() { check_assist( extract_expressions_from_format_string, - &add_macro_decl( - r#" + r#" +//- minicore: fmt fn main() { print!("{} {x + 1:b} {}$0", y + 2, 2); } "#, - ), - &add_macro_decl( - r#" + r#" fn main() { print!("{} {:b} {}"$0, y + 2, x + 1, 2); } "#, - ), ); } @@ -196,20 +167,17 @@ fn main() { fn single_arg() { check_assist( extract_expressions_from_format_string, - &add_macro_decl( - r#" + r#" +//- minicore: fmt fn main() { print!("{obj.value:b}$0",); } "#, - ), - &add_macro_decl( - r#" + r#" fn main() { print!("{:b}"$0, obj.value); } "#, - ), ); } @@ -217,20 +185,17 @@ fn main() { fn multiple_middle_placeholders_arg() { check_assist( extract_expressions_from_format_string, - &add_macro_decl( - r#" + r#" +//- minicore: fmt fn main() { print!("{} {x + 1:b} {} {}$0", y + 2, 2); } "#, - ), - &add_macro_decl( - r#" + r#" fn main() { print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1); } "#, - ), ); } @@ -238,20 +203,17 @@ fn main() { fn multiple_trailing_args() { check_assist( extract_expressions_from_format_string, - &add_macro_decl( - r#" + r#" +//- minicore: fmt fn main() { print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1); } "#, - ), - &add_macro_decl( - r#" + r#" fn main() { print!("{:b} {:b} {}"$0, 1, x + 1, Struct(1, 2)); } "#, - ), ); } @@ -259,20 +221,17 @@ fn main() { fn improper_commas() { check_assist( extract_expressions_from_format_string, - &add_macro_decl( - r#" + r#" +//- minicore: fmt fn main() { print!("{} {x + 1:b} {Struct(1, 2)}$0", 1,); } "#, - ), - &add_macro_decl( - r#" + r#" fn main() { print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2)); } "#, - ), ); } @@ -280,20 +239,17 @@ fn main() { fn nested_tt() { check_assist( extract_expressions_from_format_string, - &add_macro_decl( - r#" + r#" +//- minicore: fmt fn main() { print!("My name is {} {x$0 + x}", stringify!(Paperino)) } "#, - ), - &add_macro_decl( - r#" + r#" fn main() { print!("My name is {} {}"$0, stringify!(Paperino), x + x) } "#, - ), ); } @@ -301,22 +257,19 @@ fn main() { fn extract_only_expressions() { check_assist( extract_expressions_from_format_string, - &add_macro_decl( - r#" + r#" +//- minicore: fmt fn main() { let var = 1 + 1; print!("foobar {var} {var:?} {x$0 + x}") } "#, - ), - &add_macro_decl( - r#" + r#" fn main() { let var = 1 + 1; print!("foobar {var} {var:?} {}"$0, x + x) } "#, - ), ); } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index ea7a21e77a4..de591cfde94 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -531,7 +531,7 @@ impl FunctionBody { fn extracted_from_trait_impl(&self) -> bool { match self.node().ancestors().find_map(ast::Impl::cast) { - Some(c) => return c.trait_().is_some(), + Some(c) => c.trait_().is_some(), None => false, } } @@ -1048,23 +1048,17 @@ impl GenericParent { fn generic_parents(parent: &SyntaxNode) -> Vec<GenericParent> { let mut list = Vec::new(); if let Some(parent_item) = parent.ancestors().find_map(ast::Item::cast) { - match parent_item { - ast::Item::Fn(ref fn_) => { - if let Some(parent_parent) = parent_item - .syntax() - .parent() - .and_then(|it| it.parent()) - .and_then(ast::Item::cast) - { - match parent_parent { - ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)), - ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)), - _ => (), - } + if let ast::Item::Fn(ref fn_) = parent_item { + if let Some(parent_parent) = + parent_item.syntax().parent().and_then(|it| it.parent()).and_then(ast::Item::cast) + { + match parent_parent { + ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)), + ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)), + _ => (), } - list.push(GenericParent::Fn(fn_.clone())); } - _ => (), + list.push(GenericParent::Fn(fn_.clone())); } } list @@ -1728,7 +1722,7 @@ fn make_body( let block = match &fun.body { FunctionBody::Expr(expr) => { let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()); - let expr = ast::Expr::cast(expr).unwrap(); + let expr = ast::Expr::cast(expr).expect("Body segment should be an expr"); match expr { ast::Expr::BlockExpr(block) => { // If the extracted expression is itself a block, there is no need to wrap it inside another block. @@ -1868,9 +1862,8 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr if let Some(stmt_list) = block.stmt_list() { stmt_list.syntax().children_with_tokens().for_each(|node_or_token| { - match &node_or_token { - syntax::NodeOrToken::Token(_) => elements.push(node_or_token), - _ => (), + if let syntax::NodeOrToken::Token(_) = &node_or_token { + elements.push(node_or_token) }; }); } @@ -1934,12 +1927,18 @@ fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNo Some(ast::Expr::RefExpr(node)) if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => { - ted::replace(node.syntax(), node.expr().unwrap().syntax()); + ted::replace( + node.syntax(), + node.expr().expect("RefExpr::expr() cannot be None").syntax(), + ); } Some(ast::Expr::RefExpr(node)) if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => { - ted::replace(node.syntax(), node.expr().unwrap().syntax()); + ted::replace( + node.syntax(), + node.expr().expect("RefExpr::expr() cannot be None").syntax(), + ); } Some(_) | None => { let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs new file mode 100644 index 00000000000..663df266b6f --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs @@ -0,0 +1,205 @@ +use hir::{AsAssocItem, HirDisplay}; +use ide_db::{ + assists::{AssistId, AssistKind}, + famous_defs::FamousDefs, +}; +use syntax::{ast, AstNode}; + +use crate::assist_context::{AssistContext, Assists}; + +// Assist: into_to_qualified_from +// +// Convert an `into` method call to a fully qualified `from` call. +// +// ``` +// //- minicore: from +// struct B; +// impl From<i32> for B { +// fn from(a: i32) -> Self { +// B +// } +// } +// +// fn main() -> () { +// let a = 3; +// let b: B = a.in$0to(); +// } +// ``` +// -> +// ``` +// struct B; +// impl From<i32> for B { +// fn from(a: i32) -> Self { +// B +// } +// } +// +// fn main() -> () { +// let a = 3; +// let b: B = B::from(a); +// } +// ``` +pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; + let nameref = method_call.name_ref()?; + let receiver = method_call.receiver()?; + let db = ctx.db(); + let sema = &ctx.sema; + let fnc = sema.resolve_method_call(&method_call)?; + let scope = sema.scope(method_call.syntax())?; + // Check if the method call refers to Into trait. + if fnc.as_assoc_item(db)?.containing_trait_impl(db)? + == FamousDefs(sema, scope.krate()).core_convert_Into()? + { + let type_call = sema.type_of_expr(&method_call.clone().into())?; + let type_call_disp = + type_call.adjusted().display_source_code(db, scope.module().into(), true).ok()?; + + acc.add( + AssistId("into_to_qualified_from", AssistKind::Generate), + "Convert `into` to fully qualified `from`", + nameref.syntax().text_range(), + |edit| { + edit.replace( + method_call.syntax().text_range(), + format!("{}::from({})", type_call_disp, receiver), + ); + }, + ); + } + + Some(()) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_assist; + + use super::into_to_qualified_from; + + #[test] + fn two_types_in_same_mod() { + check_assist( + into_to_qualified_from, + r#" +//- minicore: from +struct A; +struct B; +impl From<A> for B { + fn from(a: A) -> Self { + B + } +} + +fn main() -> () { + let a: A = A; + let b: B = a.in$0to(); +}"#, + r#" +struct A; +struct B; +impl From<A> for B { + fn from(a: A) -> Self { + B + } +} + +fn main() -> () { + let a: A = A; + let b: B = B::from(a); +}"#, + ) + } + + #[test] + fn fromed_in_child_mod_imported() { + check_assist( + into_to_qualified_from, + r#" +//- minicore: from +use C::B; + +struct A; + +mod C { + use crate::A; + + pub(super) struct B; + impl From<A> for B { + fn from(a: A) -> Self { + B + } + } +} + +fn main() -> () { + let a: A = A; + let b: B = a.in$0to(); +}"#, + r#" +use C::B; + +struct A; + +mod C { + use crate::A; + + pub(super) struct B; + impl From<A> for B { + fn from(a: A) -> Self { + B + } + } +} + +fn main() -> () { + let a: A = A; + let b: B = B::from(a); +}"#, + ) + } + + #[test] + fn fromed_in_child_mod_not_imported() { + check_assist( + into_to_qualified_from, + r#" +//- minicore: from +struct A; + +mod C { + use crate::A; + + pub(super) struct B; + impl From<A> for B { + fn from(a: A) -> Self { + B + } + } +} + +fn main() -> () { + let a: A = A; + let b: C::B = a.in$0to(); +}"#, + r#" +struct A; + +mod C { + use crate::A; + + pub(super) struct B; + impl From<A> for B { + fn from(a: A) -> Self { + B + } + } +} + +fn main() -> () { + let a: A = A; + let b: C::B = C::B::from(a); +}"#, + ) + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs index 5cc110cf12b..6ed9bd85fcc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -76,12 +76,19 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) let name = to_upper_snake_case(&name.to_string()); let usages = Definition::Local(local).usages(&ctx.sema).all(); if let Some(usages) = usages.references.get(&ctx.file_id()) { - let name = make::name_ref(&name); + let name_ref = make::name_ref(&name); for usage in usages { let Some(usage) = usage.name.as_name_ref().cloned() else { continue }; - let usage = edit.make_mut(usage); - ted::replace(usage.syntax(), name.clone_for_update().syntax()); + if let Some(record_field) = ast::RecordExprField::for_name_ref(&usage) { + let record_field = edit.make_mut(record_field); + let name_expr = + make::expr_path(make::path_from_text(&name)).clone_for_update(); + record_field.replace_expr(name_expr); + } else { + let usage = edit.make_mut(usage); + ted::replace(usage.syntax(), name_ref.clone_for_update().syntax()); + } } } @@ -120,8 +127,7 @@ fn is_body_const(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> bool { is_const &= sema.resolve_method_call(&call).map(|it| it.is_const(sema.db)).unwrap_or(true) } - ast::Expr::BoxExpr(_) - | ast::Expr::ForExpr(_) + ast::Expr::ForExpr(_) | ast::Expr::ReturnExpr(_) | ast::Expr::TryExpr(_) | ast::Expr::YieldExpr(_) @@ -180,6 +186,33 @@ fn foo() { } #[test] + fn usage_in_field_shorthand() { + check_assist( + promote_local_to_const, + r" +struct Foo { + bar: usize, +} + +fn main() { + let $0bar = 0; + let foo = Foo { bar }; +} +", + r" +struct Foo { + bar: usize, +} + +fn main() { + const $0BAR: usize = 0; + let foo = Foo { bar: BAR }; +} +", + ) + } + + #[test] fn not_applicable_non_const_meth_call() { cov_mark::check!(promote_local_non_const); check_assist_not_applicable( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index e2b82223289..cffa3f55c90 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs @@ -113,10 +113,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt Some(parent) => match (expr, parent) { (ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false, ( - ast::Expr::BoxExpr(_) - | ast::Expr::PrefixExpr(_) - | ast::Expr::RefExpr(_) - | ast::Expr::MacroExpr(_), + ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_) | ast::Expr::MacroExpr(_), ast::Expr::AwaitExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::CastExpr(_) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index 24c3387457a..61e9bcdcc51 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs @@ -48,6 +48,11 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< return None; } + let new_result_ty = + make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update(); + let generic_args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast)?; + let last_genarg = generic_args.generic_args().last()?; + acc.add( AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite), "Wrap return type in Result", @@ -75,19 +80,12 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< ted::replace(ret_expr_arg.syntax(), ok_wrapped.syntax()); } - let new_result_ty = - make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update(); let old_result_ty = edit.make_mut(type_ref.clone()); ted::replace(old_result_ty.syntax(), new_result_ty.syntax()); if let Some(cap) = ctx.config.snippet_cap { - let generic_args = new_result_ty - .syntax() - .descendants() - .find_map(ast::GenericArgList::cast) - .unwrap(); - edit.add_placeholder_snippet(cap, generic_args.generic_args().last().unwrap()); + edit.add_placeholder_snippet(cap, last_genarg); } }, ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 2ebb5ef9b19..6f973ab53ee 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -114,6 +114,7 @@ mod handlers { mod add_turbo_fish; mod apply_demorgan; mod auto_import; + mod bind_unused_param; mod change_visibility; mod convert_bool_then; mod convert_comment_block; @@ -211,6 +212,7 @@ mod handlers { mod unwrap_result_return_type; mod unqualify_method_call; mod wrap_return_type_in_result; + mod into_to_qualified_from; pub(crate) fn all() -> &'static [Handler] { &[ @@ -224,6 +226,7 @@ mod handlers { add_turbo_fish::add_turbo_fish, apply_demorgan::apply_demorgan, auto_import::auto_import, + bind_unused_param::bind_unused_param, change_visibility::change_visibility, convert_bool_then::convert_bool_then_to_if, convert_bool_then::convert_if_to_bool_then, @@ -274,6 +277,7 @@ mod handlers { inline_local_variable::inline_local_variable, inline_type_alias::inline_type_alias, inline_type_alias::inline_type_alias_uses, + into_to_qualified_from::into_to_qualified_from, introduce_named_generic::introduce_named_generic, introduce_named_lifetime::introduce_named_lifetime, invert_if::invert_if, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 6eadc3dbcbc..dfaa53449f4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -266,6 +266,21 @@ pub mod std { pub mod collections { pub struct HashMap { } } } } #[test] +fn doctest_bind_unused_param() { + check_doc_test( + "bind_unused_param", + r#####" +fn some_function(x: i32$0) {} +"#####, + r#####" +fn some_function(x: i32) { + let _ = x; +} +"#####, + ) +} + +#[test] fn doctest_change_visibility() { check_doc_test( "change_visibility", @@ -694,25 +709,12 @@ fn doctest_extract_expressions_from_format_string() { check_doc_test( "extract_expressions_from_format_string", r#####" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} -macro_rules! print { - ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); -} - +//- minicore: fmt fn main() { print!("{var} {x + 1}$0"); } "#####, r#####" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} -macro_rules! print { - ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); -} - fn main() { print!("{var} {}"$0, x + 1); } @@ -1755,6 +1757,40 @@ fn foo() { } #[test] +fn doctest_into_to_qualified_from() { + check_doc_test( + "into_to_qualified_from", + r#####" +//- minicore: from +struct B; +impl From<i32> for B { + fn from(a: i32) -> Self { + B + } +} + +fn main() -> () { + let a = 3; + let b: B = a.in$0to(); +} +"#####, + r#####" +struct B; +impl From<i32> for B { + fn from(a: i32) -> Self { + B + } +} + +fn main() -> () { + let a = 3; + let b: B = B::from(a); +} +"#####, + ) +} + +#[test] fn doctest_introduce_named_generic() { check_doc_test( "introduce_named_generic", diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs index f74ebfae02c..16704d598ef 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs @@ -103,7 +103,6 @@ pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) match expr { ast::Expr::RefExpr(inner) => next_expr = inner.expr(), - ast::Expr::BoxExpr(inner) => next_expr = inner.expr(), ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(), // ast::Expr::BlockExpr(block) => expr = block.tail_expr(), ast::Expr::CastExpr(inner) => next_expr = inner.expr(), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 62bdb6ee688..87a286778e6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -1,10 +1,8 @@ //! Completion for cfg -use std::iter; - use ide_db::SymbolKind; use itertools::Itertools; -use syntax::SyntaxKind; +use syntax::{algo, ast::Ident, AstToken, Direction, NodeOrToken, SyntaxKind}; use crate::{completions::Completions, context::CompletionContext, CompletionItem}; @@ -15,31 +13,44 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { acc.add(completion.build(ctx.db)); }; - let previous = iter::successors(ctx.original_token.prev_token(), |t| { - (matches!(t.kind(), SyntaxKind::EQ) || t.kind().is_trivia()) - .then(|| t.prev_token()) - .flatten() - }) - .find(|t| matches!(t.kind(), SyntaxKind::IDENT)); - - match previous.as_ref().map(|p| p.text()) { - Some("target_arch") => KNOWN_ARCH.iter().copied().for_each(add_completion), - Some("target_env") => KNOWN_ENV.iter().copied().for_each(add_completion), - Some("target_os") => KNOWN_OS.iter().copied().for_each(add_completion), - Some("target_vendor") => KNOWN_VENDOR.iter().copied().for_each(add_completion), - Some("target_endian") => ["little", "big"].into_iter().for_each(add_completion), - Some(name) => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| { - let insert_text = format!(r#""{s}""#); - let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); - item.insert_text(insert_text); + // FIXME: Move this into context/analysis.rs + let previous = ctx + .original_token + .prev_token() + .and_then(|it| { + if matches!(it.kind(), SyntaxKind::EQ) { + Some(it.into()) + } else { + algo::non_trivia_sibling(it.into(), Direction::Prev) + } + }) + .filter(|t| matches!(t.kind(), SyntaxKind::EQ)) + .and_then(|it| algo::non_trivia_sibling(it.prev_sibling_or_token()?, Direction::Prev)) + .map(|it| match it { + NodeOrToken::Node(_) => None, + NodeOrToken::Token(t) => Ident::cast(t), + }); + match previous { + Some(None) => (), + Some(Some(p)) => match p.text() { + "target_arch" => KNOWN_ARCH.iter().copied().for_each(add_completion), + "target_env" => KNOWN_ENV.iter().copied().for_each(add_completion), + "target_os" => KNOWN_OS.iter().copied().for_each(add_completion), + "target_vendor" => KNOWN_VENDOR.iter().copied().for_each(add_completion), + "target_endian" => ["little", "big"].into_iter().for_each(add_completion), + name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| { + let insert_text = format!(r#""{s}""#); + let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); + item.insert_text(insert_text); - acc.add(item.build(ctx.db)); - }), + acc.add(item.build(ctx.db)); + }), + }, None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| { let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); acc.add(item.build(ctx.db)); }), - }; + } } const KNOWN_ARCH: [&str; 20] = [ diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs index 9447bc7db0a..90dac1902a4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs @@ -1,6 +1,6 @@ //! Completion for derives -use hir::{HasAttrs, ScopeDef}; -use ide_db::SymbolKind; +use hir::ScopeDef; +use ide_db::{documentation::HasDocs, SymbolKind}; use itertools::Itertools; use syntax::SmolStr; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs index 6bc6f34ed41..f9dec538064 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs @@ -1,5 +1,5 @@ //! Completion for lints -use ide_db::{generated::lints::Lint, SymbolKind}; +use ide_db::{documentation::Documentation, generated::lints::Lint, SymbolKind}; use syntax::ast; use crate::{context::CompletionContext, item::CompletionItem, Completions}; @@ -55,7 +55,7 @@ pub(super) fn complete_lint( _ => name.to_owned(), }; let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label); - item.documentation(hir::Documentation::new(description.to_owned())); + item.documentation(Documentation::new(description.to_owned())); item.add_to(acc, ctx.db) } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs index 0d0e143f5f6..f9cde44667b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs @@ -1,7 +1,7 @@ //! Completion for extern crates -use hir::{HasAttrs, Name}; -use ide_db::SymbolKind; +use hir::Name; +use ide_db::{documentation::HasDocs, SymbolKind}; use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs index 8e904fd605a..cecbe75391d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs @@ -51,9 +51,7 @@ mod tests { fn works_when_wrapped() { check( r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} +//- minicore: fmt macro_rules! print { ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); } @@ -70,9 +68,7 @@ fn main() { fn no_completion_without_brace() { check( r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} +//- minicore: fmt fn main() { let foobar = 1; format_args!("f$0"); @@ -87,18 +83,13 @@ fn main() { check_edit( "foobar", r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} +//- minicore: fmt fn main() { let foobar = 1; format_args!("{f$0"); } "#, r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} fn main() { let foobar = 1; format_args!("{foobar"); @@ -108,18 +99,13 @@ fn main() { check_edit( "foobar", r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} +//- minicore: fmt fn main() { let foobar = 1; format_args!("{$0"); } "#, r#" -macro_rules! format_args { - ($lit:literal $(tt:tt)*) => { 0 }, -} fn main() { let foobar = 1; format_args!("{foobar"); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 269e40e6ef5..42dfbfc7d9a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -33,8 +33,8 @@ use hir::{self, HasAttrs}; use ide_db::{ - path_transform::PathTransform, syntax_helpers::insert_whitespace_into_node, - traits::get_missing_assoc_items, SymbolKind, + documentation::HasDocs, path_transform::PathTransform, + syntax_helpers::insert_whitespace_into_node, traits::get_missing_assoc_items, SymbolKind, }; use syntax::{ ast::{self, edit_in_place::AttrsOwnerEdit, HasTypeBounds}, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 2ffe1233744..fc21bba456b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -2,8 +2,12 @@ mod format_like; -use hir::{Documentation, HasAttrs}; -use ide_db::{imports::insert_use::ImportScope, ty_filter::TryEnum, SnippetCap}; +use ide_db::{ + documentation::{Documentation, HasDocs}, + imports::insert_use::ImportScope, + ty_filter::TryEnum, + SnippetCap, +}; use syntax::{ ast::{self, make, AstNode, AstToken}, SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR}, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs index e9831a5b2a1..3ff68b97882 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs @@ -1,7 +1,6 @@ //! This file provides snippet completions, like `pd` => `eprintln!(...)`. -use hir::Documentation; -use ide_db::{imports::insert_use::ImportScope, SnippetCap}; +use ide_db::{documentation::Documentation, imports::insert_use::ImportScope, SnippetCap}; use crate::{ context::{ExprCtx, ItemListKind, PathCompletionCtx, Qualified}, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 0309952c29a..c45cc8d7b37 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -2,8 +2,11 @@ use std::fmt; -use hir::{Documentation, Mutability}; -use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind}; +use hir::Mutability; +use ide_db::{ + documentation::Documentation, imports::import_assets::LocatedImport, RootDatabase, SnippetCap, + SymbolKind, +}; use itertools::Itertools; use smallvec::SmallVec; use stdx::{impl_from, never}; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 1953eb47957..dfe8fe7e2f7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -12,7 +12,10 @@ pub(crate) mod literal; use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef}; use ide_db::{ - helpers::item_name, imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind, + documentation::{Documentation, HasDocs}, + helpers::item_name, + imports::import_assets::LocatedImport, + RootDatabase, SnippetCap, SymbolKind, }; use syntax::{AstNode, SmolStr, SyntaxKind, TextRange}; @@ -114,7 +117,7 @@ impl<'a> RenderContext<'a> { } // FIXME: remove this - fn docs(&self, def: impl HasAttrs) -> Option<hir::Documentation> { + fn docs(&self, def: impl HasDocs) -> Option<Documentation> { def.docs(self.db()) } } @@ -409,7 +412,7 @@ fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind { } } -fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<hir::Documentation> { +fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<Documentation> { use hir::ModuleDef::*; match resolution { ScopeDef::ModuleDef(Module(it)) => it.docs(db), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index 728d236dff4..b218502f7f0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -1,7 +1,10 @@ //! Renderer for `enum` variants. -use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind}; -use ide_db::SymbolKind; +use hir::{db::HirDatabase, StructKind}; +use ide_db::{ + documentation::{Documentation, HasDocs}, + SymbolKind, +}; use crate::{ context::{CompletionContext, PathCompletionCtx, PathKind}, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index ce7af1d3400..68d175c2bd5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -1,7 +1,7 @@ //! Renderer for macro invocations. -use hir::{Documentation, HirDisplay}; -use ide_db::SymbolKind; +use hir::HirDisplay; +use ide_db::{documentation::Documentation, SymbolKind}; use syntax::SmolStr; use crate::{ diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index d06abc5e91e..6f998119b7c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -1,7 +1,7 @@ //! Renderer for patterns. -use hir::{db::HirDatabase, HasAttrs, Name, StructKind}; -use ide_db::SnippetCap; +use hir::{db::HirDatabase, Name, StructKind}; +use ide_db::{documentation::HasDocs, SnippetCap}; use itertools::Itertools; use syntax::SmolStr; @@ -103,7 +103,7 @@ fn build_completion( label: SmolStr, lookup: SmolStr, pat: String, - def: impl HasAttrs + Copy, + def: impl HasDocs + Copy, adt_ty: hir::Type, // Missing in context of match statement completions is_variant_missing: bool, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 1aaf3958726..d8c134c533b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -67,11 +67,6 @@ struct Foo; } #[test] -fn inside_nested_attr() { - check(r#"#[cfg($0)]"#, expect![[]]) -} - -#[test] fn with_existing_attr() { check( r#"#[no_mangle] #[$0] mcall!();"#, @@ -636,6 +631,32 @@ mod cfg { use super::*; #[test] + fn inside_cfg() { + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg($0)] +"#, + expect![[r#" + ba dbg + ba opt_level + ba test + "#]], + ); + check( + r#" +//- /main.rs cfg:test,dbg=false,opt_level=2 +#[cfg(b$0)] +"#, + expect![[r#" + ba dbg + ba opt_level + ba test + "#]], + ); + } + + #[test] fn cfg_target_endian() { check( r#"#[cfg(target_endian = $0"#, @@ -644,6 +665,13 @@ mod cfg { ba little "#]], ); + check( + r#"#[cfg(target_endian = b$0"#, + expect![[r#" + ba big + ba little + "#]], + ); } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs new file mode 100644 index 00000000000..26f3cd28a27 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs @@ -0,0 +1,281 @@ +//! Documentation attribute related utilties. +use either::Either; +use hir::{ + db::{DefDatabase, HirDatabase}, + resolve_doc_path_on, AttrId, AttrSourceMap, AttrsWithOwner, HasAttrs, InFile, +}; +use itertools::Itertools; +use syntax::{ + ast::{self, IsString}, + AstToken, +}; +use text_edit::{TextRange, TextSize}; + +/// Holds documentation +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Documentation(String); + +impl Documentation { + pub fn new(s: String) -> Self { + Documentation(s) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl From<Documentation> for String { + fn from(Documentation(string): Documentation) -> Self { + string + } +} + +pub trait HasDocs: HasAttrs { + fn docs(self, db: &dyn HirDatabase) -> Option<Documentation>; + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option<hir::Namespace>, + ) -> Option<hir::DocLinkDef>; +} +/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree. +#[derive(Debug)] +pub struct DocsRangeMap { + source_map: AttrSourceMap, + // (docstring-line-range, attr_index, attr-string-range) + // a mapping from the text range of a line of the [`Documentation`] to the attribute index and + // the original (untrimmed) syntax doc line + mapping: Vec<(TextRange, AttrId, TextRange)>, +} + +impl DocsRangeMap { + /// Maps a [`TextRange`] relative to the documentation string back to its AST range + pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> { + let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?; + let (line_docs_range, idx, original_line_src_range) = self.mapping[found]; + if !line_docs_range.contains_range(range) { + return None; + } + + let relative_range = range - line_docs_range.start(); + + let InFile { file_id, value: source } = self.source_map.source_of_id(idx); + match source { + Either::Left(attr) => { + let string = get_doc_string_in_attr(attr)?; + let text_range = string.open_quote_text_range()?; + let range = TextRange::at( + text_range.end() + original_line_src_range.start() + relative_range.start(), + string.syntax().text_range().len().min(range.len()), + ); + Some(InFile { file_id, value: range }) + } + Either::Right(comment) => { + let text_range = comment.syntax().text_range(); + let range = TextRange::at( + text_range.start() + + TextSize::try_from(comment.prefix().len()).ok()? + + original_line_src_range.start() + + relative_range.start(), + text_range.len().min(range.len()), + ); + Some(InFile { file_id, value: range }) + } + } + } +} + +pub fn docs_with_rangemap( + db: &dyn DefDatabase, + attrs: &AttrsWithOwner, +) -> Option<(Documentation, DocsRangeMap)> { + let docs = + attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id))); + let indent = doc_indent(attrs); + let mut buf = String::new(); + let mut mapping = Vec::new(); + for (doc, idx) in docs { + if !doc.is_empty() { + let mut base_offset = 0; + for raw_line in doc.split('\n') { + let line = raw_line.trim_end(); + let line_len = line.len(); + let (offset, line) = match line.char_indices().nth(indent) { + Some((offset, _)) => (offset, &line[offset..]), + None => (0, line), + }; + let buf_offset = buf.len(); + buf.push_str(line); + mapping.push(( + TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?), + idx, + TextRange::at( + (base_offset + offset).try_into().ok()?, + line_len.try_into().ok()?, + ), + )); + buf.push('\n'); + base_offset += raw_line.len() + 1; + } + } else { + buf.push('\n'); + } + } + buf.pop(); + if buf.is_empty() { + None + } else { + Some((Documentation(buf), DocsRangeMap { mapping, source_map: attrs.source_map(db) })) + } +} + +pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option<String> { + let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value()); + let indent = doc_indent(attrs); + let mut buf = String::new(); + for doc in docs { + // str::lines doesn't yield anything for the empty string + if !doc.is_empty() { + buf.extend(Itertools::intersperse( + doc.lines().map(|line| { + line.char_indices() + .nth(indent) + .map_or(line, |(offset, _)| &line[offset..]) + .trim_end() + }), + "\n", + )); + } + buf.push('\n'); + } + buf.pop(); + if buf.is_empty() { + None + } else { + Some(buf) + } +} + +macro_rules! impl_has_docs { + ($($def:ident,)*) => {$( + impl HasDocs for hir::$def { + fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { + docs_from_attrs(&self.attrs(db)).map(Documentation) + } + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option<hir::Namespace> + ) -> Option<hir::DocLinkDef> { + resolve_doc_path_on(db, self, link, ns) + } + } + )*}; +} + +impl_has_docs![ + Variant, Field, Static, Const, Trait, TraitAlias, TypeAlias, Macro, Function, Adt, Module, + Impl, +]; + +macro_rules! impl_has_docs_enum { + ($($variant:ident),* for $enum:ident) => {$( + impl HasDocs for hir::$variant { + fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { + hir::$enum::$variant(self).docs(db) + } + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option<hir::Namespace> + ) -> Option<hir::DocLinkDef> { + hir::$enum::$variant(self).resolve_doc_path(db, link, ns) + } + } + )*}; +} + +impl_has_docs_enum![Struct, Union, Enum for Adt]; + +impl HasDocs for hir::AssocItem { + fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { + match self { + hir::AssocItem::Function(it) => it.docs(db), + hir::AssocItem::Const(it) => it.docs(db), + hir::AssocItem::TypeAlias(it) => it.docs(db), + } + } + + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option<hir::Namespace>, + ) -> Option<hir::DocLinkDef> { + match self { + hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns), + hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns), + hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns), + } + } +} + +impl HasDocs for hir::ExternCrateDecl { + fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { + let crate_docs = + docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)).map(String::from); + let decl_docs = docs_from_attrs(&self.attrs(db)).map(String::from); + match (decl_docs, crate_docs) { + (None, None) => None, + (Some(decl_docs), None) => Some(decl_docs), + (None, Some(crate_docs)) => Some(crate_docs), + (Some(mut decl_docs), Some(crate_docs)) => { + decl_docs.push('\n'); + decl_docs.push('\n'); + decl_docs += &crate_docs; + Some(decl_docs) + } + } + .map(Documentation::new) + } + fn resolve_doc_path( + self, + db: &dyn HirDatabase, + link: &str, + ns: Option<hir::Namespace>, + ) -> Option<hir::DocLinkDef> { + resolve_doc_path_on(db, self, link, ns) + } +} + +fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> { + match it.expr() { + // #[doc = lit] + Some(ast::Expr::Literal(lit)) => match lit.kind() { + ast::LiteralKind::String(it) => Some(it), + _ => None, + }, + // #[cfg_attr(..., doc = "", ...)] + None => { + // FIXME: See highlight injection for what to do here + None + } + _ => None, + } +} + +fn doc_indent(attrs: &hir::Attrs) -> usize { + attrs + .by_key("doc") + .attrs() + .filter_map(|attr| attr.string_value()) + .flat_map(|s| s.lines()) + .filter(|line| !line.chars().all(|c| c.is_whitespace())) + .map(|line| line.chars().take_while(|c| c.is_whitespace()).count()) + .min() + .unwrap_or(0) +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index ac3511ba47b..226def4d526 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -22,6 +22,7 @@ pub mod symbol_index; pub mod traits; pub mod ty_filter; pub mod use_trivial_constructor; +pub mod documentation; pub mod imports { pub mod import_assets; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index aa0bb7cce69..353a9749a37 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -71,12 +71,29 @@ impl Definition { sema: &Semantics<'_, RootDatabase>, new_name: &str, ) -> Result<SourceChange> { + // self.krate() returns None if + // self is a built-in attr, built-in type or tool module. + // it is not allowed for these defs to be renamed. + // cases where self.krate() is None is handled below. + if let Some(krate) = self.krate(sema.db) { + if !krate.origin(sema.db).is_local() { + bail!("Cannot rename a non-local definition.") + } + } + match *self { Definition::Module(module) => rename_mod(sema, module, new_name), + Definition::ToolModule(_) => { + bail!("Cannot rename a tool module") + } Definition::BuiltinType(_) => { bail!("Cannot rename builtin type") } + Definition::BuiltinAttr(_) => { + bail!("Cannot rename a builtin attr.") + } Definition::SelfType(_) => bail!("Cannot rename `Self`"), + Definition::Macro(mac) => rename_reference(sema, Definition::Macro(mac), new_name), def => rename_reference(sema, def, new_name), } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs b/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs index e27e23867a8..ab2a250289c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs @@ -1,5 +1,7 @@ //! Rustdoc specific doc comment handling +use crate::documentation::Documentation; + // stripped down version of https://github.com/rust-lang/rust/blob/392ba2ba1a7d6c542d2459fb8133bebf62a4a423/src/librustdoc/html/markdown.rs#L810-L933 pub fn is_rust_fence(s: &str) -> bool { let mut seen_rust_tags = false; @@ -32,3 +34,170 @@ pub fn is_rust_fence(s: &str) -> bool { !seen_other_tags || seen_rust_tags } + +const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"]; + +pub fn format_docs(src: &Documentation) -> String { + format_docs_(src.as_str()) +} + +fn format_docs_(src: &str) -> String { + let mut processed_lines = Vec::new(); + let mut in_code_block = false; + let mut is_rust = false; + + for mut line in src.lines() { + if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) { + continue; + } + + if let Some(header) = RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence)) + { + in_code_block ^= true; + + if in_code_block { + is_rust = is_rust_fence(header); + + if is_rust { + line = "```rust"; + } + } + } + + if in_code_block { + let trimmed = line.trim_start(); + if is_rust && trimmed.starts_with("##") { + line = &trimmed[1..]; + } + } + + processed_lines.push(line); + } + processed_lines.join("\n") +} + +fn code_line_ignored_by_rustdoc(line: &str) -> bool { + let trimmed = line.trim(); + trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_format_docs_adds_rust() { + let comment = "```\nfn some_rust() {}\n```"; + assert_eq!(format_docs_(comment), "```rust\nfn some_rust() {}\n```"); + } + + #[test] + fn test_format_docs_handles_plain_text() { + let comment = "```text\nthis is plain text\n```"; + assert_eq!(format_docs_(comment), "```text\nthis is plain text\n```"); + } + + #[test] + fn test_format_docs_handles_non_rust() { + let comment = "```sh\nsupposedly shell code\n```"; + assert_eq!(format_docs_(comment), "```sh\nsupposedly shell code\n```"); + } + + #[test] + fn test_format_docs_handles_rust_alias() { + let comment = "```ignore\nlet z = 55;\n```"; + assert_eq!(format_docs_(comment), "```rust\nlet z = 55;\n```"); + } + + #[test] + fn test_format_docs_handles_complex_code_block_attrs() { + let comment = "```rust,no_run\nlet z = 55;\n```"; + assert_eq!(format_docs_(comment), "```rust\nlet z = 55;\n```"); + } + + #[test] + fn test_format_docs_handles_error_codes() { + let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```"; + assert_eq!(format_docs_(comment), "```rust\nlet b = 0 as *const _;\n```"); + } + + #[test] + fn test_format_docs_skips_comments_in_rust_block() { + let comment = + "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; + assert_eq!(format_docs_(comment), "```rust\n#stay1\nstay2\n```"); + } + + #[test] + fn test_format_docs_does_not_skip_lines_if_plain_text() { + let comment = + "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```"; + assert_eq!( + format_docs_(comment), + "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```", + ); + } + + #[test] + fn test_format_docs_keeps_comments_outside_of_rust_block() { + let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t"; + assert_eq!(format_docs_(comment), comment); + } + + #[test] + fn test_format_docs_preserves_newlines() { + let comment = "this\nis\nmultiline"; + assert_eq!(format_docs_(comment), comment); + } + + #[test] + fn test_code_blocks_in_comments_marked_as_rust() { + let comment = r#"```rust +fn main(){} +``` +Some comment. +``` +let a = 1; +```"#; + + assert_eq!( + format_docs_(comment), + "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```" + ); + } + + #[test] + fn test_code_blocks_in_comments_marked_as_text() { + let comment = r#"```text +filler +text +``` +Some comment. +``` +let a = 1; +```"#; + + assert_eq!( + format_docs_(comment), + "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```" + ); + } + + #[test] + fn test_format_docs_handles_escape_double_hashes() { + let comment = r#"```rust +let s = "foo +## bar # baz"; +```"#; + + assert_eq!(format_docs_(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```"); + } + + #[test] + fn test_format_docs_handles_double_hashes_non_rust() { + let comment = r#"```markdown +## A second-level heading +```"#; + assert_eq!(format_docs_(comment), "```markdown\n## A second-level heading\n```"); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 7e00d368652..9c4f0ac8c9f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -6,7 +6,7 @@ use std::mem; -use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; +use base_db::{salsa::Database, FileId, FileRange, SourceDatabase, SourceDatabaseExt}; use hir::{ AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility, }; @@ -221,7 +221,6 @@ impl Definition { } // def is crate root - // FIXME: We don't do searches for crates currently, as a crate does not actually have a single name if let &Definition::Module(module) = self { if module.is_crate_root() { return SearchScope::reverse_dependencies(db, module.krate()); @@ -393,7 +392,10 @@ impl<'a> FindUsages<'a> { let name = match self.def { // special case crate modules as these do not have a proper name Definition::Module(module) if module.is_crate_root() => { - // FIXME: This assumes the crate name is always equal to its display name when it really isn't + // FIXME: This assumes the crate name is always equal to its display name when it + // really isn't + // we should instead look at the dependency edge name and recursively search our way + // up the ancestors module .krate() .display_name(self.sema.db) @@ -468,6 +470,7 @@ impl<'a> FindUsages<'a> { }; for (text, file_id, search_range) in scope_files(sema, &search_scope) { + self.sema.db.unwind_if_cancelled(); let tree = Lazy::new(move || sema.parse(file_id).syntax().clone()); // Search for occurrences of the items name @@ -504,6 +507,7 @@ impl<'a> FindUsages<'a> { let finder = &Finder::new("super"); for (text, file_id, search_range) in scope_files(sema, &scope) { + self.sema.db.unwind_if_cancelled(); let tree = Lazy::new(move || sema.parse(file_id).syntax().clone()); for offset in match_indices(&text, finder, search_range) { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs index acf0a67de4a..8302b015dda 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs @@ -1,10 +1,10 @@ //! Tools to work with format string literals for the `format_args!` family of macros. -use crate::syntax_helpers::node_ext::macro_call_for_string_token; use syntax::{ ast::{self, IsString}, - TextRange, TextSize, + AstNode, AstToken, TextRange, TextSize, }; +// FIXME: This can probably be re-implemented via the HIR? pub fn is_format_string(string: &ast::String) -> bool { // Check if `string` is a format string argument of a macro invocation. // `string` is a string literal, mapped down into the innermost macro expansion. @@ -15,19 +15,9 @@ pub fn is_format_string(string: &ast::String) -> bool { // This setup lets us correctly highlight the components of `concat!("{}", "bla")` format // strings. It still fails for `concat!("{", "}")`, but that is rare. (|| { - let name = macro_call_for_string_token(string)?.path()?.segment()?.name_ref()?; - - if !matches!( - name.text().as_str(), - "format_args" | "format_args_nl" | "const_format_args" | "panic_2015" | "panic_2021" - ) { - return None; - } - - // NB: we match against `panic_2015`/`panic_2021` here because they have a special-cased arm for - // `"{}"`, which otherwise wouldn't get highlighted. - - Some(()) + let lit = string.syntax().parent().and_then(ast::Literal::cast)?; + let fa = lit.syntax().parent().and_then(ast::FormatArgsExpr::cast)?; + (fa.template()? == ast::Expr::Literal(lit)).then_some(|| ()) })() .is_some() } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index 22ced69d81e..e4e735cecd8 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -312,7 +312,6 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) { ast::Expr::ArrayExpr(_) | ast::Expr::AwaitExpr(_) | ast::Expr::BinExpr(_) - | ast::Expr::BoxExpr(_) | ast::Expr::BreakExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::CastExpr(_) @@ -335,7 +334,10 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) { | ast::Expr::LetExpr(_) | ast::Expr::UnderscoreExpr(_) | ast::Expr::YieldExpr(_) - | ast::Expr::YeetExpr(_) => cb(expr), + | ast::Expr::YeetExpr(_) + | ast::Expr::OffsetOfExpr(_) + | ast::Expr::FormatArgsExpr(_) + | ast::Expr::AsmExpr(_) => cb(expr), } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index f54cdd63bbb..7ca0a0eab2b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -157,6 +157,7 @@ struct S; fn macro_diag_builtin() { check_diagnostics( r#" +//- minicore: fmt #[rustc_builtin_macro] macro_rules! env {} @@ -166,9 +167,6 @@ macro_rules! include {} #[rustc_builtin_macro] macro_rules! compile_error {} -#[rustc_builtin_macro] -macro_rules! format_args { () => {} } - fn main() { // Test a handful of built-in (eager) macros: @@ -189,7 +187,7 @@ fn main() { // Lazy: format_args!(); - //^^^^^^^^^^^ error: no rule matches input tokens + //^^^^^^^^^^^ error: Syntax Error in Expansion: expected expression } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs index 6238c7e09e9..8265e0b1c11 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs @@ -1,10 +1,37 @@ +use either::Either; +use hir::InFile; use syntax::{ ast::{self, HasArgList}, - AstNode, TextRange, + AstNode, SyntaxNodePtr, TextRange, }; use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext}; +// Diagnostic: mismatched-tuple-struct-pat-arg-count +// +// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. +pub(crate) fn mismatched_tuple_struct_pat_arg_count( + ctx: &DiagnosticsContext<'_>, + d: &hir::MismatchedTupleStructPatArgCount, +) -> Diagnostic { + let s = if d.found == 1 { "" } else { "s" }; + let s2 = if d.expected == 1 { "" } else { "s" }; + let message = format!( + "this pattern has {} field{s}, but the corresponding tuple struct has {} field{s2}", + d.found, d.expected + ); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0023"), + message, + invalid_args_range( + ctx, + d.expr_or_pat.clone().map(|it| it.either(Into::into, Into::into)), + d.expected, + d.found, + ), + ) +} + // Diagnostic: mismatched-arg-count // // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. @@ -14,31 +41,63 @@ pub(crate) fn mismatched_arg_count( ) -> Diagnostic { let s = if d.expected == 1 { "" } else { "s" }; let message = format!("expected {} argument{s}, found {}", d.expected, d.found); - Diagnostic::new(DiagnosticCode::RustcHardError("E0107"), message, invalid_args_range(ctx, d)) + Diagnostic::new( + DiagnosticCode::RustcHardError("E0107"), + message, + invalid_args_range(ctx, d.call_expr.clone().map(Into::into), d.expected, d.found), + ) } -fn invalid_args_range(ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount) -> TextRange { - adjusted_display_range::<ast::Expr>(ctx, d.call_expr.clone().map(|it| it.into()), &|expr| { - let arg_list = match expr { - ast::Expr::CallExpr(call) => call.arg_list()?, - ast::Expr::MethodCallExpr(call) => call.arg_list()?, +fn invalid_args_range( + ctx: &DiagnosticsContext<'_>, + source: InFile<SyntaxNodePtr>, + expected: usize, + found: usize, +) -> TextRange { + adjusted_display_range::<Either<ast::Expr, ast::TupleStructPat>>(ctx, source, &|expr| { + let (text_range, r_paren_token, expected_arg) = match expr { + Either::Left(ast::Expr::CallExpr(call)) => { + let arg_list = call.arg_list()?; + ( + arg_list.syntax().text_range(), + arg_list.r_paren_token(), + arg_list.args().nth(expected).map(|it| it.syntax().text_range()), + ) + } + Either::Left(ast::Expr::MethodCallExpr(call)) => { + let arg_list = call.arg_list()?; + ( + arg_list.syntax().text_range(), + arg_list.r_paren_token(), + arg_list.args().nth(expected).map(|it| it.syntax().text_range()), + ) + } + Either::Right(pat) => { + let r_paren = pat.r_paren_token()?; + let l_paren = pat.l_paren_token()?; + ( + l_paren.text_range().cover(r_paren.text_range()), + Some(r_paren), + pat.fields().nth(expected).map(|it| it.syntax().text_range()), + ) + } _ => return None, }; - if d.found < d.expected { - if d.found == 0 { - return Some(arg_list.syntax().text_range()); + if found < expected { + if found == 0 { + return Some(text_range); } - if let Some(r_paren) = arg_list.r_paren_token() { + if let Some(r_paren) = r_paren_token { return Some(r_paren.text_range()); } } - if d.expected < d.found { - if d.expected == 0 { - return Some(arg_list.syntax().text_range()); + if expected < found { + if expected == 0 { + return Some(text_range); } - let zip = arg_list.args().nth(d.expected).zip(arg_list.r_paren_token()); + let zip = expected_arg.zip(r_paren_token); if let Some((arg, r_paren)) = zip { - return Some(arg.syntax().text_range().cover(r_paren.text_range())); + return Some(arg.cover(r_paren.text_range())); } } @@ -331,4 +390,21 @@ fn g() { "#, ) } + + #[test] + fn tuple_struct_pat() { + check_diagnostics( + r#" +struct S(u32, u32); +fn f( + S(a, b, c): S, + // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields + S(): S, + // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields + S(e, f, .., g, d): S + // ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields +) {} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 82a9a3bd559..06b03d3d198 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -319,6 +319,7 @@ fn main() { match Either::A { Either::A => (), Either::B() => (), + // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 1 field } } "#, @@ -334,9 +335,11 @@ enum A { B(isize, isize), C } fn main() { match A::B(1, 2) { A::B(_, _, _) => (), + // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields } match A::B(1, 2) { A::C(_) => (), + // ^^^ error: this pattern has 1 field, but the corresponding tuple struct has 0 fields } } "#, @@ -846,6 +849,7 @@ fn main() { struct Foo { } fn main(f: Foo) { match f { Foo { bar } => () } + // ^^^ error: no such field } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index e0c3bedce46..d056e5c85cc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -76,7 +76,7 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Di "variable does not need to be mutable", ast, ) - .experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive. + .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive. .with_fixes(fixes) } @@ -1173,4 +1173,27 @@ fn f() { "#, ); } + + #[test] + fn regression_15623() { + check_diagnostics( + r#" +//- minicore: fn + +struct Foo; + +impl Foo { + fn needs_mut(&mut self) {} +} + +fn foo(mut foo: Foo) { + let mut call_me = || { + let 0 = 1 else { return }; + foo.needs_mut(); + }; + call_me(); +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index a34a5824f29..290c16c9d24 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -1,3 +1,4 @@ +use either::Either; use hir::{db::ExpandDatabase, HasSource, HirDisplay, Semantics}; use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase}; use syntax::{ @@ -12,22 +13,39 @@ use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if created structure does not have field provided in record. pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcHardError("E0559"), - "no such field", - d.field.clone().map(|it| it.into()), - ) - .with_fixes(fixes(ctx, d)) + let node = d.field.clone().map(|it| it.either(Into::into, Into::into)); + if d.private { + // FIXME: quickfix to add required visibility + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0451"), + "field is private", + node, + ) + } else { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0559"), + "no such field", + node, + ) + .with_fixes(fixes(ctx, d)) + } } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> { - let root = ctx.sema.db.parse_or_expand(d.field.file_id); - missing_record_expr_field_fixes( - &ctx.sema, - d.field.file_id.original_file(ctx.sema.db), - &d.field.value.to_node(&root), - ) + // FIXME: quickfix for pattern + match &d.field.value { + Either::Left(ptr) => { + let root = ctx.sema.db.parse_or_expand(d.field.file_id); + missing_record_expr_field_fixes( + &ctx.sema, + d.field.file_id.original_file(ctx.sema.db), + &ptr.to_node(&root), + ) + } + _ => None, + } } fn missing_record_expr_field_fixes( @@ -118,13 +136,34 @@ mod tests { r#" struct S { foo: i32, bar: () } impl S { - fn new() -> S { + fn new( + s@S { + //^ 💡 error: missing structure fields: + //| - bar + foo, + baz: baz2, + //^^^^^^^^^ error: no such field + qux + //^^^ error: no such field + }: S + ) -> S { + S { + //^ 💡 error: missing structure fields: + //| - bar + foo, + baz: baz2, + //^^^^^^^^^ error: no such field + qux + //^^^ error: no such field + } = s; S { //^ 💡 error: missing structure fields: //| - bar foo: 92, baz: 62, //^^^^^^^ 💡 error: no such field + qux + //^^^ error: no such field } } } @@ -295,4 +334,38 @@ fn main() { "#, ) } + + #[test] + fn test_struct_field_private() { + check_diagnostics( + r#" +mod m { + pub struct Struct { + field: u32, + field2: u32, + } +} +fn f(s@m::Struct { + field: f, + //^^^^^^^^ error: field is private + field2 + //^^^^^^ error: field is private +}: m::Struct) { + // assignee expression + m::Struct { + field: 0, + //^^^^^^^^ error: field is private + field2 + //^^^^^^ error: field is private + } = s; + m::Struct { + field: 0, + //^^^^^^^^ error: field is private + field2 + //^^^^^^ error: field is private + }; +} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs index 7de9a9a323e..495ea748776 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -35,6 +35,25 @@ fn foo() { } #[test] + fn while_let_loop_with_label_in_condition() { + check_diagnostics( + r#" +fn foo() { + let mut optional = Some(0); + + 'my_label: while let Some(a) = match optional { + None => break 'my_label, + Some(val) => Some(val), + } { + optional = None; + continue 'my_label; + } +} +"#, + ); + } + + #[test] fn for_loop() { check_diagnostics( r#" diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs index 0aa439f797a..c4ac59ec2a4 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -1,3 +1,4 @@ +use hir::InFile; use ide_db::{base_db::FileId, source_change::SourceChange}; use itertools::Itertools; use syntax::{ast, AstNode, SyntaxNode}; @@ -39,6 +40,7 @@ pub(crate) fn useless_braces( "Unnecessary braces in use statement".to_string(), use_range, ) + .with_main_node(InFile::new(file_id.into(), node.clone())) .with_fixes(Some(vec![fix( "remove_braces", "Remove unnecessary braces", @@ -156,4 +158,23 @@ use a::{c, d::e}; "#, ); } + + #[test] + fn respect_lint_attributes_for_unused_braces() { + check_diagnostics( + r#" +mod b {} +#[allow(unused_braces)] +use {b}; +"#, + ); + check_diagnostics( + r#" +mod b {} +#[deny(unused_braces)] +use {b}; + //^^^ 💡 error: Unnecessary braces in use statement +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index b1b9b4b8ea5..ebe197a6790 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -369,6 +369,7 @@ pub fn diagnostics( AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled), AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d), AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), + AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d), }; res.push(d) } @@ -432,7 +433,8 @@ fn handle_lint_attributes( diagnostics_of_range: &mut FxHashMap<InFile<SyntaxNode>, &mut Diagnostic>, ) { let file_id = sema.hir_file_for(root); - for ev in root.preorder() { + let mut preorder = root.preorder(); + while let Some(ev) = preorder.next() { match ev { syntax::WalkEvent::Enter(node) => { for attr in node.children().filter_map(ast::Attr::cast) { @@ -515,7 +517,7 @@ fn parse_lint_attribute( let Some((tag, args_tt)) = attr.as_simple_call() else { return; }; - let serevity = match tag.as_str() { + let severity = match tag.as_str() { "allow" => Severity::Allow, "warn" => Severity::Warning, "forbid" | "deny" => Severity::Error, @@ -523,12 +525,12 @@ fn parse_lint_attribute( }; for lint in parse_tt_as_comma_sep_paths(args_tt).into_iter().flatten() { if let Some(lint) = lint.as_single_name_ref() { - job(rustc_stack.entry(lint.to_string()).or_default(), serevity); + job(rustc_stack.entry(lint.to_string()).or_default(), severity); } if let Some(tool) = lint.qualifier().and_then(|x| x.as_single_name_ref()) { if let Some(name_ref) = &lint.segment().and_then(|x| x.name_ref()) { if tool.to_string() == "clippy" { - job(clippy_stack.entry(name_ref.to_string()).or_default(), serevity); + job(clippy_stack.entry(name_ref.to_string()).or_default(), severity); } } } diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index f994c284c71..fb79b5dc211 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -94,10 +94,9 @@ pub(crate) fn annotations( enum_ .variants(db) .into_iter() - .map(|variant| { + .filter_map(|variant| { variant.source(db).and_then(|node| name_range(db, node, file_id)) }) - .flatten() .for_each(|range| { let (annotation_range, target_position) = mk_ranges(range); annotations.push(Annotation { diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 901f7a2a79a..37a17762216 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -16,6 +16,7 @@ use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasA use ide_db::{ base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase}, defs::{Definition, NameClass, NameRefClass}, + documentation::{docs_with_rangemap, Documentation, HasDocs}, helpers::pick_best_token, RootDatabase, }; @@ -171,7 +172,7 @@ pub(crate) fn external_docs( /// Extracts all links from a given markdown text returning the definition text range, link-text /// and the namespace if known. pub(crate) fn extract_definitions_from_docs( - docs: &hir::Documentation, + docs: &Documentation, ) -> Vec<(TextRange, String, Option<hir::Namespace>)> { Parser::new_with_broken_link_callback( docs.as_str(), @@ -297,7 +298,7 @@ impl DocCommentToken { let abs_in_expansion_offset = token_start + relative_comment_offset + descended_prefix_len; let (attributes, def) = doc_attributes(sema, &node)?; - let (docs, doc_mapping) = attributes.docs_with_rangemap(sema.db)?; + let (docs, doc_mapping) = docs_with_rangemap(sema.db, &attributes)?; let (in_expansion_range, link, ns) = extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| { let mapped = doc_mapping.map(range)?; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 8036c77072b..9ae70ae66f5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -1,10 +1,11 @@ use std::ffi::OsStr; use expect_test::{expect, Expect}; -use hir::{HasAttrs, Semantics}; +use hir::Semantics; use ide_db::{ base_db::{FilePosition, FileRange}, defs::Definition, + documentation::{Documentation, HasDocs}, RootDatabase, }; use itertools::Itertools; @@ -78,7 +79,7 @@ fn check_doc_links(ra_fixture: &str) { fn def_under_cursor( sema: &Semantics<'_, RootDatabase>, position: &FilePosition, -) -> (Definition, hir::Documentation) { +) -> (Definition, Documentation) { let (docs, def) = sema .parse(position.file_id) .syntax() @@ -96,7 +97,7 @@ fn def_under_cursor( fn node_to_def( sema: &Semantics<'_, RootDatabase>, node: &SyntaxNode, -) -> Option<Option<(Option<hir::Documentation>, Definition)>> { +) -> Option<Option<(Option<Documentation>, Definition)>> { Some(match_ast! { match node { ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Module(def))), diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index a33a6ee1816..f72ce37d1d9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -3,12 +3,13 @@ use std::fmt::Display; use either::Either; use hir::{ - Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Layout, - LayoutError, Semantics, TypeInfo, + Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasSource, HirDisplay, Layout, LayoutError, + Semantics, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, defs::Definition, + documentation::{Documentation, HasDocs}, famous_defs::FamousDefs, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, syntax_helpers::insert_whitespace_into_node, @@ -470,7 +471,7 @@ pub(super) fn definition( Definition::SelfType(impl_def) => { impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))? } - Definition::GenericParam(it) => label_and_docs(db, it), + Definition::GenericParam(it) => (it.display(db).to_string(), None), Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))), Definition::ExternCrateDecl(it) => label_and_docs(db, it), // FIXME: We should be able to show more info about these @@ -616,9 +617,9 @@ fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Mark markup(Some(docs.replace('*', "\\*")), desc, None) } -fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>) +fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<Documentation>) where - D: HasAttrs + HirDisplay, + D: HasDocs + HirDisplay, { let label = def.display(db).to_string(); let docs = def.docs(db); @@ -631,9 +632,9 @@ fn label_and_layout_info_and_docs<D, E, E2>( config: &HoverConfig, layout_extractor: E, layout_offset_extractor: E2, -) -> (String, Option<hir::Documentation>) +) -> (String, Option<Documentation>) where - D: HasAttrs + HirDisplay, + D: HasDocs + HirDisplay, E: Fn(&D) -> Result<Layout, LayoutError>, E2: Fn(&Layout) -> Option<u64>, { @@ -657,9 +658,9 @@ fn label_value_and_layout_info_and_docs<D, E, E2, E3, V>( value_extractor: E, layout_extractor: E2, layout_tag_extractor: E3, -) -> (String, Option<hir::Documentation>) +) -> (String, Option<Documentation>) where - D: HasAttrs + HirDisplay, + D: HasDocs + HirDisplay, E: Fn(&D) -> Option<V>, E2: Fn(&D) -> Result<Layout, LayoutError>, E3: Fn(&Layout) -> Option<usize>, @@ -686,9 +687,9 @@ fn label_value_and_docs<D, E, V>( db: &RootDatabase, def: D, value_extractor: E, -) -> (String, Option<hir::Documentation>) +) -> (String, Option<Documentation>) where - D: HasAttrs + HirDisplay, + D: HasDocs + HirDisplay, E: Fn(&D) -> Option<V>, V: Display, { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index d0f9f7b0e16..81d6db564ff 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -6574,3 +6574,23 @@ fn test() { "#]], ); } + +#[test] +fn format_args_arg() { + check( + r#" +//- minicore: fmt +fn test() { + let foo = 0; + format_args!("{}", foo$0); +} +"#, + expect![[r#" + *foo* + + ```rust + let foo: i32 // size = 4, align = 4 + ``` + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 29259167419..a5d070fe767 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -52,6 +52,28 @@ pub struct InlayHintsConfig { pub closure_style: ClosureStyle, pub max_length: Option<usize>, pub closing_brace_hints_min_lines: Option<usize>, + pub fields_to_resolve: InlayFieldsToResolve, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct InlayFieldsToResolve { + pub resolve_text_edits: bool, + pub resolve_hint_tooltip: bool, + pub resolve_label_tooltip: bool, + pub resolve_label_location: bool, + pub resolve_label_command: bool, +} + +impl InlayFieldsToResolve { + pub const fn empty() -> Self { + Self { + resolve_text_edits: false, + resolve_hint_tooltip: false, + resolve_label_tooltip: false, + resolve_label_location: false, + resolve_label_command: false, + } + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -123,11 +145,13 @@ pub struct InlayHint { pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. pub text_edit: Option<TextEdit>, + pub needs_resolve: bool, } impl InlayHint { fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { + needs_resolve: false, range, kind, label: InlayHintLabel::from(")"), @@ -139,6 +163,7 @@ impl InlayHint { } fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { + needs_resolve: false, range, kind, label: InlayHintLabel::from("("), @@ -196,6 +221,10 @@ impl InlayHintLabel { }), } } + + pub fn needs_resolve(&self) -> bool { + self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some()) + } } impl From<String> for InlayHintLabel { @@ -529,6 +558,7 @@ fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool { #[cfg(test)] mod tests { + use expect_test::Expect; use hir::ClosureStyle; use itertools::Itertools; @@ -538,7 +568,7 @@ mod tests { use crate::DiscriminantHints; use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints}; - use super::ClosureReturnTypeHints; + use super::{ClosureReturnTypeHints, InlayFieldsToResolve}; pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig { discriminant_hints: DiscriminantHints::Never, @@ -559,6 +589,7 @@ mod tests { param_names_for_lifetime_elision_hints: false, max_length: None, closing_brace_hints_min_lines: None, + fields_to_resolve: InlayFieldsToResolve::empty(), }; pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig { type_hints: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 6d6bd315ebb..631807d99a7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -137,21 +137,23 @@ pub(super) fn hints( } _ => continue, }; + let label = InlayHintLabel::simple( + if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, + Some(InlayTooltip::Markdown(format!( + "`{}` → `{}` ({coercion} coercion)", + source.display(sema.db), + target.display(sema.db), + ))), + None, + ); acc.push(InlayHint { + needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), pad_left: false, pad_right: false, position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before }, kind: InlayKind::Adjustment, - label: InlayHintLabel::simple( - if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, - Some(InlayTooltip::Markdown(format!( - "`{}` → `{}` ({coercion} coercion)", - source.display(sema.db), - target.display(sema.db), - ))), - None, - ), + label, text_edit: None, }); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 07b9f9cc1ff..680035c721b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -99,6 +99,7 @@ pub(super) fn hints( None => pat.syntax().text_range(), }; acc.push(InlayHint { + needs_resolve: label.needs_resolve() || text_edit.is_some(), range: match type_ascriptable { Some(Some(t)) => text_range.cover(t.text_range()), _ => text_range, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index 343cf17e50e..35504ffa785 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -50,9 +50,10 @@ pub(super) fn hints( _ => return, }; acc.push(InlayHint { + needs_resolve: false, range, kind: InlayKind::BindingMode, - label: r.to_string().into(), + label: r.into(), text_edit: None, position: InlayHintPosition::Before, pad_left: false, @@ -68,9 +69,10 @@ pub(super) fn hints( hir::BindingMode::Ref(Mutability::Shared) => "ref", }; acc.push(InlayHint { + needs_resolve: false, range: pat.syntax().text_range(), kind: InlayKind::BindingMode, - label: bm.to_string().into(), + label: bm.into(), text_edit: None, position: InlayHintPosition::Before, pad_left: false, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index b621a8dda7e..12e46c0f883 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -57,10 +57,12 @@ pub(super) fn hints( } } } + let label = label_of_ty(famous_defs, config, &ty)?; acc.push(InlayHint { + needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), kind: InlayKind::Chaining, - label: label_of_ty(famous_defs, config, &ty)?, + label, text_edit: None, position: InlayHintPosition::After, pad_left: true, @@ -128,6 +130,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 147..154, @@ -152,6 +155,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -221,6 +225,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 143..179, @@ -245,6 +250,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -298,6 +304,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 143..179, @@ -322,6 +329,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -389,6 +397,7 @@ fn main() { "<i32, bool>>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 246..265, @@ -426,6 +435,7 @@ fn main() { "<i32, bool>>", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -474,7 +484,7 @@ fn main() { file_id: FileId( 1, ), - range: 9289..9297, + range: 10739..10747, }, ), tooltip: "", @@ -487,7 +497,7 @@ fn main() { file_id: FileId( 1, ), - range: 9321..9325, + range: 10771..10775, }, ), tooltip: "", @@ -495,6 +505,7 @@ fn main() { " = ()>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 174..224, @@ -511,7 +522,7 @@ fn main() { file_id: FileId( 1, ), - range: 9289..9297, + range: 10739..10747, }, ), tooltip: "", @@ -524,7 +535,7 @@ fn main() { file_id: FileId( 1, ), - range: 9321..9325, + range: 10771..10775, }, ), tooltip: "", @@ -532,6 +543,7 @@ fn main() { " = ()>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 174..206, @@ -548,7 +560,7 @@ fn main() { file_id: FileId( 1, ), - range: 9289..9297, + range: 10739..10747, }, ), tooltip: "", @@ -561,7 +573,7 @@ fn main() { file_id: FileId( 1, ), - range: 9321..9325, + range: 10771..10775, }, ), tooltip: "", @@ -569,6 +581,7 @@ fn main() { " = ()>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 174..189, @@ -593,6 +606,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -655,6 +669,7 @@ fn main() { ], }, ), + needs_resolve: true, }, InlayHint { range: 145..185, @@ -679,6 +694,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 145..168, @@ -703,6 +719,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 222..228, @@ -725,6 +742,7 @@ fn main() { }, ], text_edit: None, + needs_resolve: true, }, ] "#]], diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 2cefd5acdc2..2b68538c198 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -109,6 +109,7 @@ pub(super) fn hints( let linked_location = name_range.map(|range| FileRange { file_id, range }); acc.push(InlayHint { + needs_resolve: linked_location.is_some(), range: closing_token.text_range(), kind: InlayKind::ClosingBrace, label: InlayHintLabel::simple(label, None, linked_location), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 9d5defcbb71..d691303c18b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -31,9 +31,10 @@ pub(super) fn hints( let range = closure.syntax().first_token()?.prev_token()?.text_range(); let range = TextRange::new(range.end() - TextSize::from(1), range.end()); acc.push(InlayHint { + needs_resolve: false, range, kind: InlayKind::ClosureCapture, - label: InlayHintLabel::simple("move", None, None), + label: InlayHintLabel::from("move"), text_edit: None, position: InlayHintPosition::After, pad_left: false, @@ -43,6 +44,7 @@ pub(super) fn hints( } }; acc.push(InlayHint { + needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("("), @@ -59,23 +61,25 @@ pub(super) fn hints( // force cache the source file, otherwise sema lookup will potentially panic _ = sema.parse_or_expand(source.file()); + let label = InlayHintLabel::simple( + format!( + "{}{}", + match capture.kind() { + hir::CaptureKind::SharedRef => "&", + hir::CaptureKind::UniqueSharedRef => "&unique ", + hir::CaptureKind::MutableRef => "&mut ", + hir::CaptureKind::Move => "", + }, + capture.display_place(sema.db) + ), + None, + source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)), + ); acc.push(InlayHint { + needs_resolve: label.needs_resolve(), range: move_kw_range, kind: InlayKind::ClosureCapture, - label: InlayHintLabel::simple( - format!( - "{}{}", - match capture.kind() { - hir::CaptureKind::SharedRef => "&", - hir::CaptureKind::UniqueSharedRef => "&unique ", - hir::CaptureKind::MutableRef => "&mut ", - hir::CaptureKind::Move => "", - }, - capture.display_place(sema.db) - ), - None, - source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)), - ), + label, text_edit: None, position: InlayHintPosition::After, pad_left: false, @@ -84,9 +88,10 @@ pub(super) fn hints( if idx != last { acc.push(InlayHint { + needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, - label: InlayHintLabel::simple(", ", None, None), + label: InlayHintLabel::from(", "), text_edit: None, position: InlayHintPosition::After, pad_left: false, @@ -95,6 +100,7 @@ pub(super) fn hints( } } acc.push(InlayHint { + needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(")"), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs index 3b41db0f13d..204967cd7ca 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs @@ -64,6 +64,7 @@ pub(super) fn hints( }; acc.push(InlayHint { + needs_resolve: label.needs_resolve() || text_edit.is_some(), range: param_list.syntax().text_range(), kind: InlayKind::Type, label, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index c4d2ac75cfa..26dc6fa8b9c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -79,6 +79,7 @@ fn variant_hints( None, ); acc.push(InlayHint { + needs_resolve: label.needs_resolve(), range: match eq_token { Some(t) => range.cover(t.text_range()), _ => range, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs index 5fce11b785a..7b05e32ad86 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs @@ -22,6 +22,7 @@ pub(super) fn hints( } let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { + needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, label: label.into(), @@ -185,6 +186,7 @@ pub(super) fn hints( let angle_tok = gpl.l_angle_token()?; let is_empty = gpl.generic_params().next().is_none(); acc.push(InlayHint { + needs_resolve: false, range: angle_tok.text_range(), kind: InlayKind::Lifetime, label: format!( @@ -200,6 +202,7 @@ pub(super) fn hints( }); } (None, allocated_lifetimes) => acc.push(InlayHint { + needs_resolve: false, range: func.name()?.syntax().text_range(), kind: InlayKind::GenericParamList, label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs index fc297a8d824..f18e6421cbc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs @@ -31,9 +31,10 @@ pub(super) fn hints( if ty.lifetime().is_none() { let t = ty.amp_token()?; acc.push(InlayHint { + needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, - label: "'static".to_owned().into(), + label: "'static".into(), text_edit: None, position: InlayHintPosition::After, pad_left: false, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index c4f43f41175..b4260d82506 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -57,6 +57,7 @@ pub(super) fn hints( let label = InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location); InlayHint { + needs_resolve: label.needs_resolve(), range, kind: InlayKind::Parameter, label, diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index c9cdbff7d7d..aee03d218ad 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -91,9 +91,9 @@ pub use crate::{ MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, }, inlay_hints::{ - AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint, - InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind, - InlayTooltip, LifetimeElisionHints, + AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, + InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, + InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -111,7 +111,7 @@ pub use crate::{ HighlightConfig, HlRange, }, }; -pub use hir::{Documentation, Semantics}; +pub use hir::Semantics; pub use ide_assists::{ Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve, }; @@ -124,6 +124,7 @@ pub use ide_db::{ Cancelled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, SourceRootId, }, + documentation::Documentation, label::Label, line_index::{LineCol, LineIndex}, search::{ReferenceCategory, SearchScope}, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 0740bfbc7b1..32f211c6b28 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -4,14 +4,15 @@ use std::fmt; use either::Either; use hir::{ - symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource, - HirDisplay, HirFileId, InFile, LocalSource, ModuleSource, + symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasSource, HirDisplay, HirFileId, + InFile, LocalSource, ModuleSource, }; use ide_db::{ base_db::{FileId, FileRange}, - SymbolKind, + defs::Definition, + documentation::{Documentation, HasDocs}, + RootDatabase, SymbolKind, }; -use ide_db::{defs::Definition, RootDatabase}; use stdx::never; use syntax::{ ast::{self, HasName}, @@ -327,7 +328,7 @@ impl ToNavFromAst for hir::TraitAlias { impl<D> TryToNav for D where - D: HasSource + ToNavFromAst + Copy + HasAttrs + HirDisplay, + D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay, D::Ast: ast::HasName, { fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index dae8e71e8a0..ac9df5ed6d1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -2634,4 +2634,33 @@ use qux as frob; // ", // ); } + + #[test] + fn disallow_renaming_for_non_local_definition() { + check( + "Baz", + r#" +//- /lib.rs crate:lib new_source_root:library +pub struct S; +//- /main.rs crate:main deps:lib new_source_root:local +use lib::S$0; +"#, + "error: Cannot rename a non-local definition.", + ); + } + + #[test] + fn disallow_renaming_for_builtin_macros() { + check( + "Baz", + r#" +//- minicore: derive, hash +//- /main.rs crate:main +use core::hash::Hash; +#[derive(H$0ash)] +struct A; + "#, + "error: Cannot rename a non-local definition.", + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 5f87a78551d..2d528c64255 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -7,6 +7,7 @@ use ide_assists::utils::test_related_attribute; use ide_db::{ base_db::{FilePosition, FileRange}, defs::Definition, + documentation::docs_from_attrs, helpers::visit_file_defs, search::SearchScope, FxHashMap, FxHashSet, RootDatabase, SymbolKind, @@ -496,7 +497,7 @@ const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"]; fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { - attrs.docs().map_or(false, |doc| { + docs_from_attrs(attrs).map_or(false, |doc| { let mut in_code_block = false; for line in String::from(doc).lines() { diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 847ab3d21ad..e020b52e171 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -4,12 +4,11 @@ use std::collections::BTreeSet; use either::Either; -use hir::{ - AssocItem, GenericParam, HasAttrs, HirDisplay, ModuleDef, PathResolution, Semantics, Trait, -}; +use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait}; use ide_db::{ active_parameter::{callable_for_node, generic_def_for_node}, base_db::FilePosition, + documentation::{Documentation, HasDocs}, FxIndexMap, }; use stdx::format_to; @@ -28,7 +27,7 @@ use crate::RootDatabase; /// edited. #[derive(Debug)] pub struct SignatureHelp { - pub doc: Option<String>, + pub doc: Option<Documentation>, pub signature: String, pub active_parameter: Option<usize>, parameters: Vec<TextRange>, @@ -179,7 +178,7 @@ fn signature_help_for_call( let mut fn_params = None; match callable.kind() { hir::CallableKind::Function(func) => { - res.doc = func.docs(db).map(|it| it.into()); + res.doc = func.docs(db); format_to!(res.signature, "fn {}", func.name(db).display(db)); fn_params = Some(match callable.receiver_param(db) { Some(_self) => func.params_without_self(db), @@ -187,11 +186,11 @@ fn signature_help_for_call( }); } hir::CallableKind::TupleStruct(strukt) => { - res.doc = strukt.docs(db).map(|it| it.into()); + res.doc = strukt.docs(db); format_to!(res.signature, "struct {}", strukt.name(db).display(db)); } hir::CallableKind::TupleEnumVariant(variant) => { - res.doc = variant.docs(db).map(|it| it.into()); + res.doc = variant.docs(db); format_to!( res.signature, "enum {}::{}", @@ -265,38 +264,38 @@ fn signature_help_for_generics( let db = sema.db; match generics_def { hir::GenericDef::Function(it) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "fn {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Enum(it)) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "enum {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Struct(it)) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "struct {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Union(it)) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "union {}", it.name(db).display(db)); } hir::GenericDef::Trait(it) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "trait {}", it.name(db).display(db)); } hir::GenericDef::TraitAlias(it) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "trait {}", it.name(db).display(db)); } hir::GenericDef::TypeAlias(it) => { - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); format_to!(res.signature, "type {}", it.name(db).display(db)); } hir::GenericDef::Variant(it) => { // In paths, generics of an enum can be specified *after* one of its variants. // eg. `None::<u8>` // We'll use the signature of the enum, but include the docs of the variant. - res.doc = it.docs(db).map(|it| it.into()); + res.doc = it.docs(db); let enum_ = it.parent_enum(db); format_to!(res.signature, "enum {}", enum_.name(db).display(db)); generics_def = enum_.into(); diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index d8696198d3b..aabd26da289 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -12,6 +12,7 @@ use ide_db::{ }; use syntax::{AstNode, SyntaxKind::*, TextRange, T}; +use crate::inlay_hints::InlayFieldsToResolve; use crate::{ hover::hover_for_definition, inlay_hints::AdjustmentHintsMode, @@ -125,6 +126,7 @@ impl StaticIndex<'_> { max_length: Some(25), closure_capture_hints: false, closing_brace_hints_min_lines: Some(25), + fields_to_resolve: InlayFieldsToResolve::empty(), }, file_id, None, diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index d2c77e2dc79..c9ee460a1c2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -66,6 +66,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { None => format!("{}", krate.into_raw()), }; format_to!(buf, "Crate: {}\n", display_crate(krate)); + format_to!(buf, "Enabled cfgs: {:?}\n", crate_graph[krate].cfg_options); let deps = crate_graph[krate] .dependencies .iter() diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs index 2ed57e20130..2ef1315945a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs @@ -17,6 +17,7 @@ pub(super) fn highlight_format_string( return; } + // FIXME: Replace this with the HIR info we have now. lex_format_specifiers(string, &mut |piece_range, kind| { if let Some(highlight) = highlight_format_specifier(kind) { stack.add(HlRange { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 8e96bfa01ad..7d00282fc14 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -617,6 +617,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { CONST => SymbolKind::Const, STATIC => SymbolKind::Static, IDENT_PAT => SymbolKind::Local, + FORMAT_ARGS_ARG => SymbolKind::Local, _ => return default.into(), }; diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 2657a641482..71f4d07245d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -5,8 +5,8 @@ use std::mem; use either::Either; use hir::{InFile, Semantics}; use ide_db::{ - active_parameter::ActiveParameter, base_db::FileId, defs::Definition, rust_doc::is_rust_fence, - SymbolKind, + active_parameter::ActiveParameter, base_db::FileId, defs::Definition, + documentation::docs_with_rangemap, rust_doc::is_rust_fence, SymbolKind, }; use syntax::{ ast::{self, AstNode, IsString, QuoteOffsets}, @@ -118,7 +118,7 @@ pub(super) fn doc_comment( let src_file_id = src_file_id.into(); // Extract intra-doc links and emit highlights for them. - if let Some((docs, doc_mapping)) = attributes.docs_with_rangemap(sema.db) { + if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) { extract_definitions_from_docs(&docs) .into_iter() .filter_map(|(range, link, ns)| { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 3ac8aa9cc9d..64e614cecd2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -45,17 +45,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd </style> <pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">println</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="brace">{</span> - <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span><span class="parenthesis">)</span> <span class="brace">}</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</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">macro_export</span><span class="attribute_bracket attribute">]</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args</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">rustc_builtin_macro</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">macro_export</span><span class="attribute_bracket attribute">]</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">const_format_args</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">rustc_builtin_macro</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">macro_export</span><span class="attribute_bracket attribute">]</span> <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args_nl</span> <span class="brace">{</span><span class="brace">}</span> <span class="keyword">mod</span> <span class="module declaration">panic</span> <span class="brace">{</span> @@ -75,7 +69,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_display<span class="parenthesis">(</span><span class="punctuation">&</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span> <span class="parenthesis">)</span><span class="comma">,</span> <span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span> - <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span> + <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="parenthesis">)</span><span class="comma">,</span> <span class="brace">}</span> <span class="brace">}</span> @@ -92,7 +86,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span> <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> @@ -114,18 +108,18 @@ 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">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "Hello, world!"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </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="comment">// => "The number is 1"</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="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "(3, 4)"</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="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "4"</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="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "4"</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="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="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "1 2"</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="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "0042" with leading zerosV</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="numeric_literal">1</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="format_specifier">{</span><span class="numeric_literal">0</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="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1 1 2"</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="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "test"</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="variable">name</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="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="none macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1"</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="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="none macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="none macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "a 3 b"</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="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "test"</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="variable">name</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="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1"</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="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "a 3 b"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// => "{2}"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</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">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</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">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</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">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</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">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</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">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</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">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</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">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</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">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> @@ -140,10 +134,10 @@ 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">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</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">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</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">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</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">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="none macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</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="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</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="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</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="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</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">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</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="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</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="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</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="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span> @@ -167,16 +161,24 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">c"</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\xFF</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, valid unicodes</span> <span class="keyword">let</span> <span class="variable declaration reference">backslash</span> <span class="operator">=</span> <span class="string_literal">r"\\"</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="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">A</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="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none 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="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</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="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">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">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">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">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">"</span><span class="format_specifier">{</span><span class="format_specifier">}</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="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</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="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> - <span class="macro">format_args</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="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="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="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="none macro">format_args</span><span class="punctuation macro">!</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="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="none macro">toho</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro">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">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more {}"</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">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">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">"{} 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> + <span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span> + <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span> + <span class="string_literal macro">"mov {0}, {1}"</span><span class="comma macro">,</span> + <span class="string_literal macro">"add {0}, 5"</span><span class="comma macro">,</span> + <span class="none macro">out</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">o</span><span class="comma macro">,</span> + <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">i</span><span class="comma macro">,</span> + <span class="parenthesis macro">)</span><span class="semicolon">;</span> + + <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> + <span class="macro default_library library">format_args</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="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="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="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</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="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="brace">}</span></code></pre> \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 8749d355c85..542d8992531 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -401,19 +401,14 @@ fn test_string_highlighting() { // thus, we have to copy the macro definition from `std` check_highlighting( r#" +//- minicore: fmt macro_rules! println { ($($arg:tt)*) => ({ - $crate::io::_print($crate::format_args_nl!($($arg)*)); + $crate::io::_print(format_args_nl!($($arg)*)); }) } #[rustc_builtin_macro] #[macro_export] -macro_rules! format_args {} -#[rustc_builtin_macro] -#[macro_export] -macro_rules! const_format_args {} -#[rustc_builtin_macro] -#[macro_export] macro_rules! format_args_nl {} mod panic { @@ -433,7 +428,7 @@ mod panic { $crate::panicking::panic_display(&$arg) ), ($fmt:expr, $($arg:tt)+) => ( - $crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+)) + $crate::panicking::panic_fmt(const_format_args!($fmt, $($arg)+)) ), } } @@ -450,7 +445,7 @@ macro_rules! concat {} macro_rules! toho { () => ($crate::panic!("not yet implemented")); - ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", $crate::format_args!($($arg)+))); + ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+))); } fn main() { @@ -534,7 +529,15 @@ fn main() { assert!(true, "{}", 1); assert!(true, "{} asdasd", 1); toho!("{}fmt", 0); - asm!("mov eax, {0}"); + let i: u64 = 3; + let o: u64; + asm!( + "mov {0}, {1}", + "add {0}, 5", + out(reg) o, + in(reg) i, + ); + format_args!(concat!("{}"), "{}"); format_args!("{} {} {} {} {} {}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); }"#, diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index c7e403f6b1a..b40509715ba 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -32,7 +32,7 @@ use crate::SourceChange; pub(crate) use on_enter::on_enter; // Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`. -pub(crate) const TRIGGER_CHARS: &str = ".=<>{"; +pub(crate) const TRIGGER_CHARS: &str = ".=<>{("; struct ExtendedTextEdit { edit: TextEdit, @@ -86,45 +86,57 @@ fn on_char_typed_inner( if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) { return None; } - return match char_typed { + let conv = |text_edit: Option<TextEdit>| { + Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false }) + }; + match char_typed { '.' => conv(on_dot_typed(&file.tree(), offset)), '=' => conv(on_eq_typed(&file.tree(), offset)), '<' => on_left_angle_typed(&file.tree(), offset), '>' => conv(on_right_angle_typed(&file.tree(), offset)), - '{' => conv(on_opening_brace_typed(file, offset)), - _ => return None, - }; - - fn conv(text_edit: Option<TextEdit>) -> Option<ExtendedTextEdit> { - Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false }) + '{' => conv(on_opening_bracket_typed(file, offset, '{')), + '(' => conv(on_opening_bracket_typed(file, offset, '(')), + _ => None, } } -/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a -/// block, or a part of a `use` item. -fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> { - if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { +/// Inserts a closing bracket when the user types an opening bracket, wrapping an existing expression in a +/// block, or a part of a `use` item (for `{`). +fn on_opening_bracket_typed( + file: &Parse<SourceFile>, + offset: TextSize, + opening_bracket: char, +) -> Option<TextEdit> { + let (closing_bracket, expected_ast_bracket) = match opening_bracket { + '{' => ('}', SyntaxKind::L_CURLY), + '(' => (')', SyntaxKind::L_PAREN), + _ => return None, + }; + + if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some(opening_bracket)) { return None; } let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?; - if brace_token.kind() != SyntaxKind::L_CURLY { + if brace_token.kind() != expected_ast_bracket { return None; } - // Remove the `{` to get a better parse tree, and reparse. + // Remove the opening bracket to get a better parse tree, and reparse. let range = brace_token.text_range(); - if !stdx::always!(range.len() == TextSize::of('{')) { + if !stdx::always!(range.len() == TextSize::of(opening_bracket)) { return None; } let file = file.reparse(&Indel::delete(range)); - if let Some(edit) = brace_expr(&file.tree(), offset) { + if let Some(edit) = bracket_expr(&file.tree(), offset, opening_bracket, closing_bracket) { return Some(edit); } - if let Some(edit) = brace_use_path(&file.tree(), offset) { - return Some(edit); + if closing_bracket == '}' { + if let Some(edit) = brace_use_path(&file.tree(), offset) { + return Some(edit); + } } return None; @@ -143,7 +155,12 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option< )) } - fn brace_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { + fn bracket_expr( + file: &SourceFile, + offset: TextSize, + opening_bracket: char, + closing_bracket: char, + ) -> Option<TextEdit> { let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?; if expr.syntax().text_range().start() != offset { return None; @@ -166,10 +183,10 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option< return None; } - // Insert `}` right after the expression. + // Insert the closing bracket right after the expression. Some(TextEdit::insert( - expr.syntax().text_range().end() + TextSize::of("{"), - "}".to_string(), + expr.syntax().text_range().end() + TextSize::of(opening_bracket), + closing_bracket.to_string(), )) } } @@ -938,6 +955,193 @@ use some::pa$0th::to::Item; } #[test] + fn adds_closing_parenthesis_for_expr() { + type_char( + '(', + r#" +fn f() { match () { _ => $0() } } + "#, + r#" +fn f() { match () { _ => (()) } } + "#, + ); + type_char( + '(', + r#" +fn f() { $0() } + "#, + r#" +fn f() { (()) } + "#, + ); + type_char( + '(', + r#" +fn f() { let x = $0(); } + "#, + r#" +fn f() { let x = (()); } + "#, + ); + type_char( + '(', + r#" +fn f() { let x = $0a.b(); } + "#, + r#" +fn f() { let x = (a.b()); } + "#, + ); + type_char( + '(', + r#" +const S: () = $0(); +fn f() {} + "#, + r#" +const S: () = (()); +fn f() {} + "#, + ); + type_char( + '(', + r#" +const S: () = $0a.b(); +fn f() {} + "#, + r#" +const S: () = (a.b()); +fn f() {} + "#, + ); + type_char( + '(', + r#" +fn f() { + match x { + 0 => $0(), + 1 => (), + } +} + "#, + r#" +fn f() { + match x { + 0 => (()), + 1 => (), + } +} + "#, + ); + type_char( + '(', + r#" + fn f() { + let z = Some($03); + } + "#, + r#" + fn f() { + let z = Some((3)); + } + "#, + ); + } + + #[test] + fn parenthesis_noop_in_string_literal() { + // Regression test for #9351 + type_char_noop( + '(', + r##" +fn check_with(ra_fixture: &str, expect: Expect) { + let base = r#" +enum E { T(), R$0, C } +use self::E::X; +const Z: E = E::C; +mod m {} +asdasdasdasdasdasda +sdasdasdasdasdasda +sdasdasdasdasd +"#; + let actual = completion_list(&format!("{}\n{}", base, ra_fixture)); + expect.assert_eq(&actual) +} + "##, + ); + } + + #[test] + fn parenthesis_noop_in_item_position_with_macro() { + type_char_noop('(', r#"$0println!();"#); + type_char_noop( + '(', + r#" +fn main() $0println!("hello"); +}"#, + ); + } + + #[test] + fn parenthesis_noop_in_use_tree() { + type_char_noop( + '(', + r#" +use some::$0Path; + "#, + ); + type_char_noop( + '(', + r#" +use some::{Path, $0Other}; + "#, + ); + type_char_noop( + '(', + r#" +use some::{$0Path, Other}; + "#, + ); + type_char_noop( + '(', + r#" +use some::path::$0to::Item; + "#, + ); + type_char_noop( + '(', + r#" +use some::$0path::to::Item; + "#, + ); + type_char_noop( + '(', + r#" +use $0some::path::to::Item; + "#, + ); + type_char_noop( + '(', + r#" +use some::path::$0to::{Item}; + "#, + ); + type_char_noop( + '(', + r#" +use $0Thing as _; + "#, + ); + + type_char_noop( + '(', + r#" +use some::pa$0th::to::Item; + "#, + ); + } + + #[test] fn adds_closing_angle_bracket_for_generic_args() { type_char( '<', diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index 4d56c771960..89b302c796b 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -16,6 +16,5 @@ doctest = false # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=5.4.0", features = ["raw-api"] } hashbrown.workspace = true -once_cell = "1.17.0" rustc-hash = "1.1.0" triomphe.workspace = true diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs index dabbf3a38b5..2934d26674d 100644 --- a/src/tools/rust-analyzer/crates/intern/src/lib.rs +++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs @@ -6,11 +6,11 @@ use std::{ fmt::{self, Debug, Display}, hash::{BuildHasherDefault, Hash, Hasher}, ops::Deref, + sync::OnceLock, }; use dashmap::{DashMap, SharedValue}; use hashbrown::{hash_map::RawEntryMut, HashMap}; -use once_cell::sync::OnceCell; use rustc_hash::FxHasher; use triomphe::Arc; @@ -177,12 +177,12 @@ impl<T: Display + Internable + ?Sized> Display for Interned<T> { } pub struct InternStorage<T: ?Sized> { - map: OnceCell<InternMap<T>>, + map: OnceLock<InternMap<T>>, } impl<T: ?Sized> InternStorage<T> { pub const fn new() -> Self { - Self { map: OnceCell::new() } + Self { map: OnceLock::new() } } } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index d8553d3f953..4197f248e0a 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -1,3 +1,5 @@ +use crate::grammar::types::type_; + use super::*; // test expr_literals @@ -73,6 +75,9 @@ pub(super) fn atom_expr( if let Some(m) = literal(p) { return Some((m, BlockLike::NotBlock)); } + if p.at_contextual_kw(T![builtin]) && p.nth_at(1, T![#]) { + return Some((builtin_expr(p)?, BlockLike::NotBlock)); + } if paths::is_path_start(p) { return Some(path_expr(p, r)); } @@ -93,7 +98,6 @@ pub(super) fn atom_expr( m.complete(p, UNDERSCORE_EXPR) } T![loop] => loop_expr(p, None), - T![box] => box_expr(p, None), T![while] => while_expr(p, None), T![try] => try_block_expr(p, None), T![match] => match_expr(p), @@ -212,6 +216,72 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, if saw_expr && !saw_comma { PAREN_EXPR } else { TUPLE_EXPR }) } +// test builtin_expr +// fn foo() { +// builtin#asm(0); +// builtin#format_args("", 0, 1, a = 2 + 3, a + b); +// builtin#offset_of(Foo, bar.baz.0); +// } +fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> { + let m = p.start(); + p.bump_remap(T![builtin]); + p.bump(T![#]); + if p.at_contextual_kw(T![offset_of]) { + p.bump_remap(T![offset_of]); + p.expect(T!['(']); + type_(p); + p.expect(T![,]); + while !p.at(EOF) && !p.at(T![')']) { + if p.at(IDENT) || p.at(INT_NUMBER) { + name_ref_or_index(p); + // } else if p.at(FLOAT_NUMBER) { + // FIXME: needs float hack + } else { + p.err_and_bump("expected field name or number"); + } + if !p.at(T![')']) { + p.expect(T![.]); + } + } + p.expect(T![')']); + Some(m.complete(p, OFFSET_OF_EXPR)) + } else if p.at_contextual_kw(T![format_args]) { + p.bump_remap(T![format_args]); + p.expect(T!['(']); + expr(p); + if p.eat(T![,]) { + while !p.at(EOF) && !p.at(T![')']) { + let m = p.start(); + if p.at(IDENT) && p.nth_at(1, T![=]) { + name(p); + p.bump(T![=]); + } + if expr(p).is_none() { + m.abandon(p); + break; + } + m.complete(p, FORMAT_ARGS_ARG); + + if !p.at(T![')']) { + p.expect(T![,]); + } + } + } + p.expect(T![')']); + Some(m.complete(p, FORMAT_ARGS_EXPR)) + } else if p.at_contextual_kw(T![asm]) { + p.bump_remap(T![asm]); + p.expect(T!['(']); + // FIXME: We just put expression here so highlighting kind of keeps working + expr(p); + p.expect(T![')']); + Some(m.complete(p, ASM_EXPR)) + } else { + m.abandon(p); + None + } +} + // test array_expr // fn foo() { // []; @@ -662,19 +732,3 @@ fn try_block_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker { } m.complete(p, BLOCK_EXPR) } - -// test box_expr -// fn foo() { -// let x = box 1i32; -// let y = (box 1i32, box 2i32); -// let z = Foo(box 1i32, box 2i32); -// } -fn box_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker { - assert!(p.at(T![box])); - let m = m.unwrap_or_else(|| p.start()); - p.bump(T![box]); - if p.at_ts(EXPR_FIRST) { - expr(p); - } - m.complete(p, BOX_EXPR) -} diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index e4dce21f32a..36c52953a02 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -221,6 +221,7 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::Caret => T![^], rustc_lexer::TokenKind::Percent => T![%], rustc_lexer::TokenKind::Unknown => ERROR, + rustc_lexer::TokenKind::UnknownPrefix if token_text == "builtin" => IDENT, rustc_lexer::TokenKind::UnknownPrefix => { err = "unknown literal prefix"; IDENT diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 48f407623d8..db5278f89d5 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -105,12 +105,16 @@ pub enum SyntaxKind { WHILE_KW, YIELD_KW, AUTO_KW, + BUILTIN_KW, DEFAULT_KW, EXISTENTIAL_KW, UNION_KW, RAW_KW, MACRO_RULES_KW, YEET_KW, + OFFSET_OF_KW, + ASM_KW, + FORMAT_ARGS_KW, INT_NUMBER, FLOAT_NUMBER, CHAR, @@ -203,7 +207,10 @@ pub enum SyntaxKind { RECORD_EXPR, RECORD_EXPR_FIELD_LIST, RECORD_EXPR_FIELD, - BOX_EXPR, + OFFSET_OF_EXPR, + ASM_EXPR, + FORMAT_ARGS_EXPR, + FORMAT_ARGS_ARG, CALL_EXPR, INDEX_EXPR, METHOD_CALL_EXPR, @@ -315,12 +322,16 @@ impl SyntaxKind { | WHILE_KW | YIELD_KW | AUTO_KW + | BUILTIN_KW | DEFAULT_KW | EXISTENTIAL_KW | UNION_KW | RAW_KW | MACRO_RULES_KW | YEET_KW + | OFFSET_OF_KW + | ASM_KW + | FORMAT_ARGS_KW ) } pub fn is_punct(self) -> bool { @@ -435,12 +446,16 @@ impl SyntaxKind { pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> { let kw = match ident { "auto" => AUTO_KW, + "builtin" => BUILTIN_KW, "default" => DEFAULT_KW, "existential" => EXISTENTIAL_KW, "union" => UNION_KW, "raw" => RAW_KW, "macro_rules" => MACRO_RULES_KW, "yeet" => YEET_KW, + "offset_of" => OFFSET_OF_KW, + "asm" => ASM_KW, + "format_args" => FORMAT_ARGS_KW, _ => return None, }; Some(kw) @@ -481,5 +496,5 @@ impl SyntaxKind { } } #[macro_export] -macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; } +macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; } pub use T; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast deleted file mode 100644 index b21f37cd85a..00000000000 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast +++ /dev/null @@ -1,90 +0,0 @@ -SOURCE_FILE - FN - FN_KW "fn" - WHITESPACE " " - NAME - IDENT "foo" - PARAM_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE "\n " - LET_STMT - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "x" - WHITESPACE " " - EQ "=" - WHITESPACE " " - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "1i32" - SEMICOLON ";" - WHITESPACE "\n " - LET_STMT - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "y" - WHITESPACE " " - EQ "=" - WHITESPACE " " - TUPLE_EXPR - L_PAREN "(" - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "1i32" - COMMA "," - WHITESPACE " " - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "2i32" - R_PAREN ")" - SEMICOLON ";" - WHITESPACE "\n " - LET_STMT - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "z" - WHITESPACE " " - EQ "=" - WHITESPACE " " - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "Foo" - ARG_LIST - L_PAREN "(" - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "1i32" - COMMA "," - WHITESPACE " " - BOX_EXPR - BOX_KW "box" - WHITESPACE " " - LITERAL - INT_NUMBER "2i32" - R_PAREN ")" - SEMICOLON ";" - WHITESPACE "\n" - R_CURLY "}" - WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs deleted file mode 100644 index fc9923b7137..00000000000 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn foo() { - let x = box 1i32; - let y = (box 1i32, box 2i32); - let z = Foo(box 1i32, box 2i32); -} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast new file mode 100644 index 00000000000..361900b6d3e --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast @@ -0,0 +1,105 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + ASM_EXPR + BUILTIN_KW "builtin" + POUND "#" + ASM_KW "asm" + L_PAREN "(" + LITERAL + INT_NUMBER "0" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + FORMAT_ARGS_EXPR + BUILTIN_KW "builtin" + POUND "#" + FORMAT_ARGS_KW "format_args" + L_PAREN "(" + LITERAL + STRING "\"\"" + COMMA "," + WHITESPACE " " + FORMAT_ARGS_ARG + LITERAL + INT_NUMBER "0" + COMMA "," + WHITESPACE " " + FORMAT_ARGS_ARG + LITERAL + INT_NUMBER "1" + COMMA "," + WHITESPACE " " + FORMAT_ARGS_ARG + NAME + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + LITERAL + INT_NUMBER "2" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + LITERAL + INT_NUMBER "3" + COMMA "," + WHITESPACE " " + FORMAT_ARGS_ARG + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + OFFSET_OF_EXPR + BUILTIN_KW "builtin" + POUND "#" + OFFSET_OF_KW "offset_of" + L_PAREN "(" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + COMMA "," + WHITESPACE " " + NAME_REF + IDENT "bar" + DOT "." + NAME_REF + IDENT "baz" + DOT "." + NAME_REF + INT_NUMBER "0" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs new file mode 100644 index 00000000000..14431b0210e --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs @@ -0,0 +1,5 @@ +fn foo() { + builtin#asm(0); + builtin#format_args("", 0, 1, a = 2 + 3, a + b); + builtin#offset_of(Foo, bar.baz.0); +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-test/build.rs index 19a5caa4ccd..7827157865a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-test/build.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-test/build.rs @@ -71,6 +71,10 @@ fn main() { .arg("--target-dir") .arg(&target_dir); + if let Ok(target) = std::env::var("TARGET") { + cmd.args(["--target", &target]); + } + println!("Running {cmd:?}"); let output = cmd.output().unwrap(); diff --git a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs index 8392718b227..c5d55f7d217 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs @@ -2,14 +2,29 @@ use std::process::Command; +use anyhow::Context; use rustc_hash::FxHashMap; -use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath}; +use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot}; + +/// Determines how `rustc --print cfg` is discovered and invoked. +/// +/// There options are supported: +/// - [`RustcCfgConfig::Cargo`], which relies on `cargo rustc --print cfg` +/// and `RUSTC_BOOTSTRAP`. +/// - [`RustcCfgConfig::Explicit`], which uses an explicit path to the `rustc` +/// binary in the sysroot. +/// - [`RustcCfgConfig::Discover`], which uses [`toolchain::rustc`]. +pub(crate) enum RustcCfgConfig<'a> { + Cargo(&'a ManifestPath), + Explicit(&'a Sysroot), + Discover, +} pub(crate) fn get( - cargo_toml: Option<&ManifestPath>, target: Option<&str>, extra_env: &FxHashMap<String, String>, + config: RustcCfgConfig<'_>, ) -> Vec<CfgFlag> { let _p = profile::span("rustc_cfg::get"); let mut res = Vec::with_capacity(6 * 2 + 1); @@ -25,49 +40,67 @@ pub(crate) fn get( // Add miri cfg, which is useful for mir eval in stdlib res.push(CfgFlag::Atom("miri".into())); - match get_rust_cfgs(cargo_toml, target, extra_env) { + let rustc_cfgs = get_rust_cfgs(target, extra_env, config); + + let rustc_cfgs = match rustc_cfgs { + Ok(cfgs) => cfgs, + Err(e) => { + tracing::error!(?e, "failed to get rustc cfgs"); + return res; + } + }; + + let rustc_cfgs = + rustc_cfgs.lines().map(|it| it.parse::<CfgFlag>()).collect::<Result<Vec<_>, _>>(); + + match rustc_cfgs { Ok(rustc_cfgs) => { - tracing::debug!( - "rustc cfgs found: {:?}", - rustc_cfgs - .lines() - .map(|it| it.parse::<CfgFlag>().map(|it| it.to_string())) - .collect::<Vec<_>>() - ); - res.extend(rustc_cfgs.lines().filter_map(|it| it.parse().ok())); + tracing::debug!(?rustc_cfgs, "rustc cfgs found"); + res.extend(rustc_cfgs); + } + Err(e) => { + tracing::error!(?e, "failed to get rustc cfgs") } - Err(e) => tracing::error!("failed to get rustc cfgs: {e:?}"), } res } fn get_rust_cfgs( - cargo_toml: Option<&ManifestPath>, target: Option<&str>, extra_env: &FxHashMap<String, String>, + config: RustcCfgConfig<'_>, ) -> anyhow::Result<String> { - if let Some(cargo_toml) = cargo_toml { - let mut cargo_config = Command::new(toolchain::cargo()); - cargo_config.envs(extra_env); - cargo_config - .current_dir(cargo_toml.parent()) - .args(["rustc", "-Z", "unstable-options", "--print", "cfg"]) - .env("RUSTC_BOOTSTRAP", "1"); - if let Some(target) = target { - cargo_config.args(["--target", target]); + let mut cmd = match config { + RustcCfgConfig::Cargo(cargo_toml) => { + let mut cmd = Command::new(toolchain::cargo()); + cmd.envs(extra_env); + cmd.current_dir(cargo_toml.parent()) + .args(["rustc", "-Z", "unstable-options", "--print", "cfg"]) + .env("RUSTC_BOOTSTRAP", "1"); + if let Some(target) = target { + cmd.args(["--target", target]); + } + + return utf8_stdout(cmd).context("Unable to run `cargo rustc`"); } - match utf8_stdout(cargo_config) { - Ok(it) => return Ok(it), - Err(e) => tracing::debug!("{e:?}: falling back to querying rustc for cfgs"), + RustcCfgConfig::Explicit(sysroot) => { + let rustc: std::path::PathBuf = sysroot.discover_rustc()?.into(); + tracing::debug!(?rustc, "using explicit rustc from sysroot"); + Command::new(rustc) } - } - // using unstable cargo features failed, fall back to using plain rustc - let mut cmd = Command::new(toolchain::rustc()); + RustcCfgConfig::Discover => { + let rustc = toolchain::rustc(); + tracing::debug!(?rustc, "using rustc from env"); + Command::new(rustc) + } + }; + cmd.envs(extra_env); cmd.args(["--print", "cfg", "-O"]); if let Some(target) = target { cmd.args(["--target", target]); } - utf8_stdout(cmd) + + utf8_stdout(cmd).context("Unable to run `rustc`") } diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index da862c9e87f..fe046dd1463 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -115,10 +115,19 @@ impl Sysroot { Ok(Sysroot::load(sysroot_dir, src)) } - pub fn discover_rustc(&self) -> Option<ManifestPath> { + pub fn discover_rustc_src(&self) -> Option<ManifestPath> { get_rustc_src(&self.root) } + pub fn discover_rustc(&self) -> Result<AbsPathBuf, std::io::Error> { + let rustc = self.root.join("bin/rustc"); + tracing::debug!(?rustc, "checking for rustc binary at location"); + match fs::metadata(&rustc) { + Ok(_) => Ok(rustc), + Err(e) => Err(e), + } + } + pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result<Sysroot> { let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| { format_err!("can't load standard library from sysroot path {sysroot_dir}") diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 13463e9f72e..e0209ca15a5 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -2,7 +2,7 @@ //! metadata` or `rust-project.json`) into representation stored in the salsa //! database -- `CrateGraph`. -use std::{collections::VecDeque, fmt, fs, process::Command, sync}; +use std::{collections::VecDeque, fmt, fs, iter, process::Command, str::FromStr, sync}; use anyhow::{format_err, Context}; use base_db::{ @@ -21,7 +21,7 @@ use crate::{ cargo_workspace::{DepKind, PackageData, RustLibSource}, cfg_flag::CfgFlag, project_json::Crate, - rustc_cfg, + rustc_cfg::{self, RustcCfgConfig}, sysroot::SysrootCrate, target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, @@ -240,9 +240,9 @@ impl ProjectWorkspace { Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone()) .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))), Some(RustLibSource::Discover) => { - sysroot.as_ref().ok().and_then(Sysroot::discover_rustc).ok_or_else(|| { - Some(format!("Failed to discover rustc source for sysroot.")) - }) + sysroot.as_ref().ok().and_then(Sysroot::discover_rustc_src).ok_or_else( + || Some(format!("Failed to discover rustc source for sysroot.")), + ) } None => Err(None), }; @@ -279,8 +279,11 @@ impl ProjectWorkspace { } }); - let rustc_cfg = - rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env); + let rustc_cfg = rustc_cfg::get( + config.target.as_deref(), + &config.extra_env, + RustcCfgConfig::Cargo(cargo_toml), + ); let cfg_overrides = config.cfg_overrides.clone(); let data_layout = target_data_layout::get( @@ -331,11 +334,18 @@ impl ProjectWorkspace { } (None, None) => Err(None), }; - if let Ok(sysroot) = &sysroot { - tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); - } + let config = match &sysroot { + Ok(sysroot) => { + tracing::debug!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); + RustcCfgConfig::Explicit(sysroot) + } + Err(_) => { + tracing::debug!("discovering sysroot"); + RustcCfgConfig::Discover + } + }; - let rustc_cfg = rustc_cfg::get(None, target, extra_env); + let rustc_cfg = rustc_cfg::get(target, extra_env, config); ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg, toolchain } } @@ -357,10 +367,18 @@ impl ProjectWorkspace { } None => Err(None), }; - if let Ok(sysroot) = &sysroot { - tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); - } - let rustc_cfg = rustc_cfg::get(None, None, &Default::default()); + let rustc_config = match &sysroot { + Ok(sysroot) => { + tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot"); + RustcCfgConfig::Explicit(sysroot) + } + Err(_) => { + tracing::info!("discovering sysroot"); + RustcCfgConfig::Discover + } + }; + + let rustc_cfg = rustc_cfg::get(None, &FxHashMap::default(), rustc_config); Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg }) } @@ -730,6 +748,7 @@ fn project_json_to_crate_graph( ) }); + let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned()); let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default(); let crates: FxHashMap<CrateId, CrateId> = project .crates() @@ -754,9 +773,14 @@ fn project_json_to_crate_graph( let env = env.clone().into_iter().collect(); let target_cfgs = match target.as_deref() { - Some(target) => cfg_cache - .entry(target) - .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)), + Some(target) => cfg_cache.entry(target).or_insert_with(|| { + let rustc_cfg = match sysroot { + Some(sysroot) => RustcCfgConfig::Explicit(sysroot), + None => RustcCfgConfig::Discover, + }; + + rustc_cfg::get(Some(target), extra_env, rustc_cfg) + }), None => &rustc_cfg, }; @@ -765,7 +789,12 @@ fn project_json_to_crate_graph( *edition, display_name.clone(), version.clone(), - target_cfgs.iter().chain(cfg.iter()).cloned().collect(), + target_cfgs + .iter() + .chain(cfg.iter()) + .chain(iter::once(&r_a_cfg_flag)) + .cloned() + .collect(), None, env, *is_proc_macro, @@ -820,7 +849,7 @@ fn cargo_to_crate_graph( sysroot: Option<&Sysroot>, rustc_cfg: Vec<CfgFlag>, override_cfg: &CfgOverrides, - // Don't compute cfg and use this if present + // Don't compute cfg and use this if present, only used for the sysroot experiment hack forced_cfg: Option<CfgOptions>, build_scripts: &WorkspaceBuildScripts, target_layout: TargetLayoutLoadResult, @@ -842,12 +871,7 @@ fn cargo_to_crate_graph( None => (SysrootPublicDeps::default(), None), }; - let cfg_options = { - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg); - cfg_options.insert_atom("debug_assertions".into()); - cfg_options - }; + let cfg_options = create_cfg_options(rustc_cfg); // Mapping of a package to its library target let mut pkg_to_lib_crate = FxHashMap::default(); @@ -866,6 +890,9 @@ fn cargo_to_crate_graph( if cargo[pkg].is_local { cfg_options.insert_atom("test".into()); } + if cargo[pkg].is_member { + cfg_options.insert_atom("rust_analyzer".into()); + } if !override_cfg.global.is_empty() { cfg_options.apply_diff(override_cfg.global.clone()); @@ -1029,8 +1056,8 @@ fn detached_files_to_crate_graph( None => (SysrootPublicDeps::default(), None), }; - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg); + let mut cfg_options = create_cfg_options(rustc_cfg); + cfg_options.insert_atom("rust_analyzer".into()); for detached_file in detached_files { let file_id = match load(detached_file) { @@ -1228,6 +1255,10 @@ fn add_target_crate_root( let mut env = Env::default(); inject_cargo_env(pkg, &mut env); + if let Ok(cname) = String::from_str(cargo_name) { + // CARGO_CRATE_NAME is the name of the Cargo target with - converted to _, such as the name of the library, binary, example, integration test, or benchmark. + env.set("CARGO_CRATE_NAME", cname.replace("-", "_")); + } if let Some(envs) = build_data.map(|it| &it.envs) { for (k, v) in envs { @@ -1291,8 +1322,7 @@ fn sysroot_to_crate_graph( channel: Option<ReleaseChannel>, ) -> (SysrootPublicDeps, Option<CrateId>) { let _p = profile::span("sysroot_to_crate_graph"); - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg.clone()); + let cfg_options = create_cfg_options(rustc_cfg.clone()); let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = match &sysroot.hack_cargo_workspace { Some(cargo) => handle_hack_cargo_workspace( load, @@ -1471,3 +1501,10 @@ fn inject_cargo_env(package: &PackageData, env: &mut Env) { env.set("CARGO_PKG_LICENSE_FILE", String::new()); } + +fn create_cfg_options(rustc_cfg: Vec<CfgFlag>) -> CfgOptions { + let mut cfg_options = CfgOptions::default(); + cfg_options.extend(rustc_cfg); + cfg_options.insert_atom("debug_assertions".into()); + cfg_options +} diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index e595cd82729..727d39a3077 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -18,6 +18,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -81,6 +82,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -151,6 +153,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -162,7 +165,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "an_example", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", @@ -221,6 +224,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -232,7 +236,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "it", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index e595cd82729..727d39a3077 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -18,6 +18,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -81,6 +82,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -151,6 +153,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -162,7 +165,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "an_example", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", @@ -221,6 +224,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", "test", ], ), @@ -232,7 +236,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "it", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index f10c55d0462..89728babd82 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -18,6 +18,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", ], ), potential_cfg_options: None, @@ -80,6 +81,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", ], ), potential_cfg_options: None, @@ -149,6 +151,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", ], ), potential_cfg_options: None, @@ -159,7 +162,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "an_example", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", @@ -218,6 +221,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "rust_analyzer", ], ), potential_cfg_options: None, @@ -228,7 +232,7 @@ "CARGO_MANIFEST_DIR": "$ROOT$hello-world", "CARGO_PKG_VERSION": "0.1.0", "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", + "CARGO_CRATE_NAME": "it", "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_HOMEPAGE": "", "CARGO_PKG_DESCRIPTION": "", diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt index fb3f5933b17..b7bf6cb2774 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt @@ -14,7 +14,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + ], ), potential_cfg_options: None, env: Env { @@ -53,7 +55,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + ], ), potential_cfg_options: None, env: Env { @@ -84,7 +88,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + ], ), potential_cfg_options: None, env: Env { @@ -115,7 +121,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + ], ), potential_cfg_options: None, env: Env { @@ -146,7 +154,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + ], ), potential_cfg_options: None, env: Env { @@ -192,7 +202,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + ], ), potential_cfg_options: None, env: Env { @@ -223,7 +235,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + ], ), potential_cfg_options: None, env: Env { @@ -311,7 +325,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + ], ), potential_cfg_options: None, env: Env { @@ -342,7 +358,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + ], ), potential_cfg_options: None, env: Env { @@ -373,7 +391,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "debug_assertions", + ], ), potential_cfg_options: None, env: Env { @@ -404,7 +424,9 @@ }, ), cfg_options: CfgOptions( - [], + [ + "rust_analyzer", + ], ), potential_cfg_options: None, env: Env { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 1f9d6db9314..7410f0a3a66 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -50,7 +50,6 @@ always-assert = "0.1.2" # These 3 deps are not used by r-a directly, but we list them here to lock in their versions # in our transitive deps to prevent them from pulling in windows-sys 0.45.0 mio = "=0.8.5" -filetime = "=0.2.19" parking_lot_core = "=0.9.6" cfg.workspace = true diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs index ab06b96814a..8c9261ab05e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs @@ -16,10 +16,12 @@ use lsp_types::{ }; use serde_json::json; -use crate::config::{Config, RustfmtConfig}; -use crate::line_index::PositionEncoding; -use crate::lsp_ext::negotiated_encoding; -use crate::semantic_tokens; +use crate::{ + config::{Config, RustfmtConfig}, + line_index::PositionEncoding, + lsp::semantic_tokens, + lsp_ext::negotiated_encoding, +}; pub fn server_capabilities(config: &Config) -> ServerCapabilities { ServerCapabilities { @@ -218,7 +220,7 @@ fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProvi } fn more_trigger_character(config: &Config) -> Vec<String> { - let mut res = vec![".".to_string(), ">".to_string(), "{".to_string()]; + let mut res = vec![".".to_string(), ">".to_string(), "{".to_string(), "(".to_string()]; if config.snippet_cap() { res.push("<".to_string()); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 4a03be1893c..dcb3ca6581c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -15,7 +15,10 @@ use hir_def::{ hir::{ExprId, PatId}, }; use hir_ty::{Interner, Substitution, TyExt, TypeFlags}; -use ide::{Analysis, AnnotationConfig, DiagnosticsConfig, InlayHintsConfig, LineCol, RootDatabase}; +use ide::{ + Analysis, AnnotationConfig, DiagnosticsConfig, InlayFieldsToResolve, InlayHintsConfig, LineCol, + RootDatabase, +}; use ide_db::{ base_db::{ salsa::{self, debug::DebugQueryTable, ParallelDatabase}, @@ -317,9 +320,13 @@ impl flags::AnalysisStats { fn run_mir_lowering(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) { let mut sw = self.stop_watch(); - let all = bodies.len() as u64; + let mut all = 0; let mut fail = 0; for &body in bodies { + if matches!(body, DefWithBody::Variant(_)) { + continue; + } + all += 1; let Err(e) = db.mir_body(body.into()) else { continue; }; @@ -782,6 +789,7 @@ impl flags::AnalysisStats { closure_style: hir::ClosureStyle::ImplFn, max_length: Some(25), closing_brace_hints_min_lines: Some(20), + fields_to_resolve: InlayFieldsToResolve::empty(), }, file_id, None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index 42d180114e5..d6a45ce06f2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -21,7 +21,7 @@ use vfs::{AbsPathBuf, Vfs}; use crate::{ cli::flags, line_index::{LineEndings, LineIndex, PositionEncoding}, - to_proto, + lsp::to_proto, version::version, }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index 44337f955e5..8c056fff000 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -51,7 +51,7 @@ impl flags::Scip { version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(), tool_info: Some(scip_types::ToolInfo { name: "rust-analyzer".to_owned(), - version: "0.1".to_owned(), + version: format!("{}", crate::version::version()), arguments: vec![], special_fields: Default::default(), }) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 40c50f6d176..ea3a21241cb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -13,8 +13,9 @@ use cfg::{CfgAtom, CfgDiff}; use flycheck::FlycheckConfig; use ide::{ AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, - HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig, - JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, + HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, + InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + Snippet, SnippetScope, }; use ide_db::{ imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, @@ -1335,6 +1336,18 @@ impl Config { } pub fn inlay_hints(&self) -> InlayHintsConfig { + let client_capability_fields = self + .caps + .text_document + .as_ref() + .and_then(|text| text.inlay_hint.as_ref()) + .and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref()) + .map(|inlay_resolve| inlay_resolve.properties.iter()) + .into_iter() + .flatten() + .cloned() + .collect::<FxHashSet<_>>(); + InlayHintsConfig { render_colons: self.data.inlayHints_renderColons, type_hints: self.data.inlayHints_typeHints_enable, @@ -1395,6 +1408,13 @@ impl Config { } else { None }, + fields_to_resolve: InlayFieldsToResolve { + resolve_text_edits: client_capability_fields.contains("textEdits"), + resolve_hint_tooltip: client_capability_fields.contains("tooltip"), + resolve_label_tooltip: client_capability_fields.contains("label.tooltip"), + resolve_label_location: client_capability_fields.contains("label.location"), + resolve_label_command: client_capability_fields.contains("label.command"), + }, } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index b65f38a0c71..71701ef1617 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -9,7 +9,7 @@ use nohash_hasher::{IntMap, IntSet}; use rustc_hash::FxHashSet; use triomphe::Arc; -use crate::lsp_ext; +use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext}; pub(crate) type CheckFixes = Arc<IntMap<usize, IntMap<FileId, Vec<Fix>>>>; @@ -122,3 +122,41 @@ fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagno && left.range == right.range && left.message == right.message } + +pub(crate) fn fetch_native_diagnostics( + snapshot: GlobalStateSnapshot, + subscriptions: Vec<FileId>, +) -> Vec<(FileId, Vec<lsp_types::Diagnostic>)> { + let _p = profile::span("fetch_native_diagnostics"); + let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned()); + subscriptions + .into_iter() + .filter_map(|file_id| { + let line_index = snapshot.file_line_index(file_id).ok()?; + let diagnostics = snapshot + .analysis + .diagnostics( + &snapshot.config.diagnostics(), + ide::AssistResolveStrategy::None, + file_id, + ) + .ok()? + .into_iter() + .map(move |d| lsp_types::Diagnostic { + range: lsp::to_proto::range(&line_index, d.range), + severity: Some(lsp::to_proto::diagnostic_severity(d.severity)), + code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_string())), + code_description: Some(lsp_types::CodeDescription { + href: lsp_types::Url::parse(&d.code.url()).unwrap(), + }), + source: Some("rust-analyzer".to_string()), + message: d.message, + related_information: None, + tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]), + data: None, + }) + .collect::<Vec<_>>(); + Some((file_id, diagnostics)) + }) + .collect() +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs index 06564578d80..731580557c2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -8,8 +8,8 @@ use stdx::format_to; use vfs::{AbsPath, AbsPathBuf}; use crate::{ - global_state::GlobalStateSnapshot, line_index::PositionEncoding, lsp_ext, - to_proto::url_from_abs_path, + global_state::GlobalStateSnapshot, line_index::PositionEncoding, + lsp::to_proto::url_from_abs_path, lsp_ext, }; use super::{DiagnosticsMapConfig, Fix}; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs index 5e5cd9a0269..7da43118881 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs @@ -8,9 +8,9 @@ use stdx::thread::ThreadIntent; use crate::{ global_state::{GlobalState, GlobalStateSnapshot}, + lsp::LspError, main_loop::Task, version::version, - LspError, }; /// A visitor for routing a raw JSON request to an appropriate handler function. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index ea8a6975195..c09f57252ce 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -12,25 +12,27 @@ use ide_db::base_db::{CrateId, FileLoader, ProcMacroPaths, SourceDatabase}; use load_cargo::SourceRootConfig; use lsp_types::{SemanticTokens, Url}; use nohash_hasher::IntMap; -use parking_lot::{Mutex, RwLock}; +use parking_lot::{ + MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, + RwLockWriteGuard, +}; use proc_macro_api::ProcMacroServer; use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts}; use rustc_hash::{FxHashMap, FxHashSet}; use triomphe::Arc; -use vfs::AnchoredPathBuf; +use vfs::{AnchoredPathBuf, Vfs}; use crate::{ config::{Config, ConfigError}, diagnostics::{CheckFixes, DiagnosticCollection}, - from_proto, line_index::{LineEndings, LineIndex}, + lsp::{from_proto, to_proto::url_from_abs_path}, lsp_ext, main_loop::Task, mem_docs::MemDocs, op_queue::OpQueue, reload, task_pool::TaskPool, - to_proto::url_from_abs_path, }; // Enforces drop order @@ -40,7 +42,7 @@ pub(crate) struct Handle<H, C> { } pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response); -pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; +type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; /// `GlobalState` is the primary mutable state of the language server /// @@ -49,6 +51,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; /// incremental salsa database. /// /// Note that this struct has more than one impl in various modules! +#[doc(alias = "GlobalMess")] pub(crate) struct GlobalState { sender: Sender<lsp_server::Message>, req_queue: ReqQueue, @@ -66,6 +69,7 @@ pub(crate) struct GlobalState { // status pub(crate) shutdown_requested: bool, + pub(crate) send_hint_refresh_query: bool, pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>, // proc macros @@ -177,6 +181,7 @@ impl GlobalState { mem_docs: MemDocs::default(), semantic_tokens_cache: Arc::new(Default::default()), shutdown_requested: false, + send_hint_refresh_query: false, last_reported_status: None, source_root_config: SourceRootConfig::default(), config_errors: Default::default(), @@ -216,12 +221,15 @@ impl GlobalState { let mut file_changes = FxHashMap::default(); let (change, changed_files, workspace_structure_change) = { let mut change = Change::new(); - let (vfs, line_endings_map) = &mut *self.vfs.write(); - let changed_files = vfs.take_changes(); + let mut guard = self.vfs.write(); + let changed_files = guard.0.take_changes(); if changed_files.is_empty() { return false; } + // 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. @@ -272,6 +280,7 @@ impl GlobalState { let mut workspace_structure_change = None; // A file was added or deleted let mut has_structure_changes = false; + let mut bytes = vec![]; for file in &changed_files { let vfs_path = &vfs.file_path(file.file_id); if let Some(path) = vfs_path.as_path() { @@ -293,16 +302,28 @@ impl GlobalState { let text = if file.exists() { let bytes = vfs.file_contents(file.file_id).to_vec(); + String::from_utf8(bytes).ok().and_then(|text| { + // FIXME: Consider doing normalization in the `vfs` instead? That allows + // getting rid of some locking let (text, line_endings) = LineEndings::normalize(text); - line_endings_map.insert(file.file_id, line_endings); - Some(Arc::from(text)) + Some((Arc::from(text), line_endings)) }) } else { None }; - change.change_file(file.file_id, text); + // 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)); } + let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard); + bytes.into_iter().for_each(|(file_id, text)| match text { + None => change.change_file(file_id, None), + Some((text, line_endings)) => { + line_endings_map.insert(file_id, line_endings); + change.change_file(file_id, Some(text)); + } + }); if has_structure_changes { let roots = self.source_root_config.partition(vfs); change.set_roots(roots); @@ -422,12 +443,16 @@ impl Drop for GlobalState { } impl GlobalStateSnapshot { + fn vfs_read(&self) -> MappedRwLockReadGuard<'_, vfs::Vfs> { + RwLockReadGuard::map(self.vfs.read(), |(it, _)| it) + } + pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result<FileId> { - url_to_file_id(&self.vfs.read().0, url) + url_to_file_id(&self.vfs_read(), url) } pub(crate) fn file_id_to_url(&self, id: FileId) -> Url { - file_id_to_url(&self.vfs.read().0, id) + file_id_to_url(&self.vfs_read(), id) } pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> { @@ -443,7 +468,7 @@ impl GlobalStateSnapshot { } pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url { - let mut base = self.vfs.read().0.file_path(path.anchor); + let mut base = self.vfs_read().file_path(path.anchor); base.pop(); let path = base.join(&path.path).unwrap(); let path = path.as_path().unwrap(); @@ -451,7 +476,7 @@ impl GlobalStateSnapshot { } pub(crate) fn file_id_to_file_path(&self, file_id: FileId) -> vfs::VfsPath { - self.vfs.read().0.file_path(file_id) + self.vfs_read().file_path(file_id) } pub(crate) fn cargo_target_for_crate_root( @@ -459,7 +484,7 @@ impl GlobalStateSnapshot { crate_id: CrateId, ) -> Option<(&CargoWorkspace, Target)> { let file_id = self.analysis.crate_root(crate_id).ok()?; - let path = self.vfs.read().0.file_path(file_id); + let path = self.vfs_read().file_path(file_id); let path = path.as_path()?; self.workspaces.iter().find_map(|ws| match ws { ProjectWorkspace::Cargo { cargo, .. } => { @@ -471,7 +496,11 @@ impl GlobalStateSnapshot { } pub(crate) fn vfs_memory_usage(&self) -> usize { - self.vfs.read().0.memory_usage() + self.vfs_read().memory_usage() + } + + pub(crate) fn file_exists(&self, file_id: FileId) -> bool { + self.vfs.read().0.exists(file_id) } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index e830e5e9a64..f9070d27353 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -13,8 +13,12 @@ use triomphe::Arc; use vfs::{AbsPathBuf, ChangeKind, VfsPath}; use crate::{ - config::Config, from_proto, global_state::GlobalState, lsp_ext::RunFlycheckParams, - lsp_utils::apply_document_changes, mem_docs::DocumentData, reload, + config::Config, + global_state::GlobalState, + lsp::{from_proto, utils::apply_document_changes}, + lsp_ext::RunFlycheckParams, + mem_docs::DocumentData, + reload, }; pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> anyhow::Result<()> { @@ -84,15 +88,16 @@ pub(crate) fn handle_did_change_text_document( } }; - let vfs = &mut state.vfs.write().0; - let file_id = vfs.file_id(&path).unwrap(); let text = apply_document_changes( state.config.position_encoding(), - || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(), + || { + let vfs = &state.vfs.read().0; + let file_id = vfs.file_id(&path).unwrap(); + std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into() + }, params.content_changes, ); - - vfs.set_file_contents(path, Some(text.into_bytes())); + state.vfs.write().0.set_file_contents(path, Some(text.into_bytes())); } Ok(()) } @@ -108,6 +113,10 @@ pub(crate) fn handle_did_close_text_document( tracing::error!("orphan DidCloseTextDocument: {}", path); } + if let Some(file_id) = state.vfs.read().0.file_id(&path) { + state.diagnostics.clear_native_for(file_id); + } + state.semantic_tokens_cache.lock().remove(¶ms.text_document.uri); if let Some(path) = path.as_path() { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 5f1f731cffb..b8a1a39be19 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -11,8 +11,8 @@ use anyhow::Context; use ide::{ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange, - HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, - SingleResolve, SourceChange, TextEdit, + HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, + Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, }; use ide_db::SymbolKind; use lsp_server::ErrorCode; @@ -30,21 +30,23 @@ use serde_json::json; use stdx::{format_to, never}; use syntax::{algo, ast, AstNode, TextRange, TextSize}; use triomphe::Arc; -use vfs::{AbsPath, AbsPathBuf, VfsPath}; +use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath}; use crate::{ cargo_target_spec::CargoTargetSpec, config::{Config, RustfmtConfig, WorkspaceSymbolConfig}, diff::diff, - from_proto, global_state::{GlobalState, GlobalStateSnapshot}, line_index::LineEndings, + lsp::{ + from_proto, to_proto, + utils::{all_edits_are_disjoint, invalid_params_error}, + LspError, + }, lsp_ext::{ self, CrateInfoResult, ExternalDocsPair, ExternalDocsResponse, FetchDependencyListParams, FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams, }, - lsp_utils::{all_edits_are_disjoint, invalid_params_error}, - to_proto, LspError, }; pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { @@ -354,7 +356,7 @@ pub(crate) fn handle_on_type_formatting( // This should be a single-file edit let (_, (text_edit, snippet_edit)) = edit.source_file_edits.into_iter().next().unwrap(); - stdx::never!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets"); + stdx::always!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets"); let change = to_proto::snippet_text_edit_vec(&line_index, edit.is_snippet, text_edit); Ok(Some(change)) @@ -972,7 +974,7 @@ pub(crate) fn handle_hover( PositionOrRange::Range(range) => range, }; - let file_range = from_proto::file_range(&snap, params.text_document, range)?; + let file_range = from_proto::file_range(&snap, ¶ms.text_document, range)?; let info = match snap.analysis.hover(&snap.config.hover(), file_range)? { None => return Ok(None), Some(info) => info, @@ -1128,7 +1130,7 @@ pub(crate) fn handle_code_action( let line_index = snap.file_line_index(from_proto::file_id(&snap, ¶ms.text_document.uri)?)?; - let frange = from_proto::file_range(&snap, params.text_document.clone(), params.range)?; + let frange = from_proto::file_range(&snap, ¶ms.text_document, params.range)?; let mut assists_config = snap.config.assist(); assists_config.allowed = params @@ -1381,7 +1383,7 @@ pub(crate) fn handle_ssr( let selections = params .selections .iter() - .map(|range| from_proto::file_range(&snap, params.position.text_document.clone(), *range)) + .map(|range| from_proto::file_range(&snap, ¶ms.position.text_document, *range)) .collect::<Result<Vec<_>, _>>()?; let position = from_proto::file_position(&snap, params.position)?; let source_change = snap.analysis.structural_search_replace( @@ -1401,7 +1403,7 @@ pub(crate) fn handle_inlay_hints( let document_uri = ¶ms.text_document.uri; let FileRange { file_id, range } = from_proto::file_range( &snap, - TextDocumentIdentifier::new(document_uri.to_owned()), + &TextDocumentIdentifier::new(document_uri.to_owned()), params.range, )?; let line_index = snap.file_line_index(file_id)?; @@ -1410,17 +1412,73 @@ pub(crate) fn handle_inlay_hints( snap.analysis .inlay_hints(&inlay_hints_config, file_id, Some(range))? .into_iter() - .map(|it| to_proto::inlay_hint(&snap, &line_index, it)) + .map(|it| { + to_proto::inlay_hint( + &snap, + &inlay_hints_config.fields_to_resolve, + &line_index, + file_id, + it, + ) + }) .collect::<Cancellable<Vec<_>>>()?, )) } pub(crate) fn handle_inlay_hints_resolve( - _snap: GlobalStateSnapshot, - hint: InlayHint, + snap: GlobalStateSnapshot, + mut original_hint: InlayHint, ) -> anyhow::Result<InlayHint> { let _p = profile::span("handle_inlay_hints_resolve"); - Ok(hint) + + let data = match original_hint.data.take() { + Some(it) => it, + None => return Ok(original_hint), + }; + + let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?; + let file_id = FileId(resolve_data.file_id); + anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data"); + + let line_index = snap.file_line_index(file_id)?; + let range = from_proto::text_range( + &line_index, + lsp_types::Range { start: original_hint.position, end: original_hint.position }, + )?; + let range_start = range.start(); + let range_end = range.end(); + let large_range = TextRange::new( + range_start.checked_sub(1.into()).unwrap_or(range_start), + range_end.checked_add(1.into()).unwrap_or(range_end), + ); + let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(); + forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty(); + let resolve_hints = snap.analysis.inlay_hints( + &forced_resolve_inlay_hints_config, + file_id, + Some(large_range), + )?; + + let mut resolved_hints = resolve_hints + .into_iter() + .filter_map(|it| { + to_proto::inlay_hint( + &snap, + &forced_resolve_inlay_hints_config.fields_to_resolve, + &line_index, + file_id, + it, + ) + .ok() + }) + .filter(|hint| hint.position == original_hint.position) + .filter(|hint| hint.kind == original_hint.kind); + if let Some(resolved_hint) = resolved_hints.next() { + if resolved_hints.next().is_none() { + return Ok(resolved_hint); + } + } + Ok(original_hint) } pub(crate) fn handle_call_hierarchy_prepare( @@ -1453,7 +1511,7 @@ pub(crate) fn handle_call_hierarchy_incoming( let item = params.item; let doc = TextDocumentIdentifier::new(item.uri); - let frange = from_proto::file_range(&snap, doc, item.selection_range)?; + let frange = from_proto::file_range(&snap, &doc, item.selection_range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; let call_items = match snap.analysis.incoming_calls(fpos)? { @@ -1488,7 +1546,7 @@ pub(crate) fn handle_call_hierarchy_outgoing( let item = params.item; let doc = TextDocumentIdentifier::new(item.uri); - let frange = from_proto::file_range(&snap, doc, item.selection_range)?; + let frange = from_proto::file_range(&snap, &doc, item.selection_range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; let call_items = match snap.analysis.outgoing_calls(fpos)? { @@ -1569,18 +1627,21 @@ pub(crate) fn handle_semantic_tokens_full_delta( snap.config.highlighting_non_standard_tokens(), ); - let mut cache = snap.semantic_tokens_cache.lock(); - let cached_tokens = cache.entry(params.text_document.uri).or_default(); + let cached_tokens = snap.semantic_tokens_cache.lock().remove(¶ms.text_document.uri); - if let Some(prev_id) = &cached_tokens.result_id { + if let Some(cached_tokens @ lsp_types::SemanticTokens { result_id: Some(prev_id), .. }) = + &cached_tokens + { if *prev_id == params.previous_result_id { let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens); - *cached_tokens = semantic_tokens; + snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens); return Ok(Some(delta.into())); } } - *cached_tokens = semantic_tokens.clone(); + // Clone first to keep the lock short + let semantic_tokens_clone = semantic_tokens.clone(); + snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens_clone); Ok(Some(semantic_tokens.into())) } @@ -1591,7 +1652,7 @@ pub(crate) fn handle_semantic_tokens_range( ) -> anyhow::Result<Option<SemanticTokensRangeResult>> { let _p = profile::span("handle_semantic_tokens_range"); - let frange = from_proto::file_range(&snap, params.text_document, params.range)?; + let frange = from_proto::file_range(&snap, ¶ms.text_document, params.range)?; let text = snap.analysis.file_text(frange.file_id)?; let line_index = snap.file_line_index(frange.file_id)?; @@ -1674,7 +1735,7 @@ pub(crate) fn handle_move_item( ) -> anyhow::Result<Vec<lsp_ext::SnippetTextEdit>> { let _p = profile::span("handle_move_item"); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; - let range = from_proto::file_range(&snap, params.text_document, params.range)?; + let range = from_proto::file_range(&snap, ¶ms.text_document, params.range)?; let direction = match params.direction { lsp_ext::MoveItemDirection::Up => ide::Direction::Up, @@ -1879,12 +1940,15 @@ fn run_rustfmt( // Determine the edition of the crate the file belongs to (if there's multiple, we pick the // highest edition). - let editions = snap + let Ok(editions) = snap .analysis .relevant_crates_for(file_id)? .into_iter() .map(|crate_id| snap.analysis.crate_edition(crate_id)) - .collect::<Result<Vec<_>, _>>()?; + .collect::<Result<Vec<_>, _>>() + else { + return Ok(None); + }; let edition = editions.iter().copied().max(); let line_index = snap.file_line_index(file_id)?; @@ -1894,23 +1958,7 @@ fn run_rustfmt( let mut cmd = process::Command::new(toolchain::rustfmt()); cmd.envs(snap.config.extra_env()); cmd.args(extra_args); - // try to chdir to the file so we can respect `rustfmt.toml` - // FIXME: use `rustfmt --config-path` once - // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed - match text_document.uri.to_file_path() { - Ok(mut path) => { - // pop off file name - if path.pop() && path.is_dir() { - cmd.current_dir(path); - } - } - Err(_) => { - tracing::error!( - "Unable to get file path for {}, rustfmt.toml might be ignored", - text_document.uri - ); - } - } + if let Some(edition) = edition { cmd.arg("--edition"); cmd.arg(edition.to_string()); @@ -1929,7 +1977,7 @@ fn run_rustfmt( .into()); } - let frange = from_proto::file_range(snap, text_document, range)?; + let frange = from_proto::file_range(snap, &text_document, range)?; let start_line = line_index.index.line_col(frange.range.start()).line; let end_line = line_index.index.line_col(frange.range.end()).line; @@ -1948,12 +1996,31 @@ fn run_rustfmt( } RustfmtConfig::CustomCommand { command, args } => { let mut cmd = process::Command::new(command); + cmd.envs(snap.config.extra_env()); cmd.args(args); cmd } }; + // try to chdir to the file so we can respect `rustfmt.toml` + // FIXME: use `rustfmt --config-path` once + // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed + match text_document.uri.to_file_path() { + Ok(mut path) => { + // pop off file name + if path.pop() && path.is_dir() { + command.current_dir(path); + } + } + Err(_) => { + tracing::error!( + text_document = ?text_document.uri, + "Unable to get path, rustfmt.toml might be ignored" + ); + } + } + let mut rustfmt = command .stdin(Stdio::piped()) .stdout(Stdio::piped()) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 57e26c241bb..6c62577f696 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -23,18 +23,13 @@ mod cargo_target_spec; mod diagnostics; mod diff; mod dispatch; -mod from_proto; mod global_state; mod line_index; -mod lsp_utils; mod main_loop; -mod markdown; mod mem_docs; mod op_queue; mod reload; -mod semantic_tokens; mod task_pool; -mod to_proto; mod version; mod handlers { @@ -43,13 +38,12 @@ mod handlers { } pub mod config; -pub mod lsp_ext; +pub mod lsp; +use self::lsp::ext as lsp_ext; #[cfg(test)] mod integrated_benchmarks; -use std::fmt; - use serde::de::DeserializeOwned; pub use crate::{caps::server_capabilities, main_loop::main_loop, version::version}; @@ -61,23 +55,3 @@ pub fn from_json<T: DeserializeOwned>( serde_json::from_value(json.clone()) .map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}")) } - -#[derive(Debug)] -struct LspError { - code: i32, - message: String, -} - -impl LspError { - fn new(code: i32, message: String) -> LspError { - LspError { code, message } - } -} - -impl fmt::Display for LspError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Language Server request failed with {}. ({})", self.code, self.message) - } -} - -impl std::error::Error for LspError {} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs new file mode 100644 index 00000000000..ac7e1a95e62 --- /dev/null +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs @@ -0,0 +1,29 @@ +//! Custom LSP definitions and protocol conversions. + +use core::fmt; + +pub(crate) mod utils; +pub(crate) mod semantic_tokens; +pub mod ext; +pub(crate) mod from_proto; +pub(crate) mod to_proto; + +#[derive(Debug)] +pub(crate) struct LspError { + pub(crate) code: i32, + pub(crate) message: String, +} + +impl LspError { + pub(crate) fn new(code: i32, message: String) -> LspError { + LspError { code, message } + } +} + +impl fmt::Display for LspError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Language Server request failed with {}. ({})", self.code, self.message) + } +} + +impl std::error::Error for LspError {} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index d0989b3230d..ad56899163d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -682,7 +682,9 @@ pub struct CompletionResolveData { } #[derive(Debug, Serialize, Deserialize)] -pub struct InlayHintResolveData {} +pub struct InlayHintResolveData { + pub file_id: u32, +} #[derive(Debug, Serialize, Deserialize)] pub struct CompletionImport { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs index c247e1bb229..69d6aba94c3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs @@ -12,8 +12,8 @@ use crate::{ from_json, global_state::GlobalStateSnapshot, line_index::{LineIndex, PositionEncoding}, + lsp::utils::invalid_params_error, lsp_ext, - lsp_utils::invalid_params_error, }; pub(crate) fn abs_path(url: &lsp_types::Url) -> anyhow::Result<AbsPathBuf> { @@ -72,7 +72,7 @@ pub(crate) fn file_position( pub(crate) fn file_range( snap: &GlobalStateSnapshot, - text_document_identifier: lsp_types::TextDocumentIdentifier, + text_document_identifier: &lsp_types::TextDocumentIdentifier, range: lsp_types::Range, ) -> anyhow::Result<FileRange> { file_range_uri(snap, &text_document_identifier.uri, range) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs index 1fe02fc7ead..1fe02fc7ead 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 7b32180e3eb..23074493aee 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -8,11 +8,12 @@ use std::{ use ide::{ Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, - Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, - InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, NavigationTarget, ReferenceCategory, - RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, - SymbolKind, TextEdit, TextRange, TextSize, + Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, + InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, + NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, + SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; +use ide_db::rust_doc::format_docs; use itertools::Itertools; use serde_json::to_value; use vfs::AbsPath; @@ -22,9 +23,12 @@ use crate::{ config::{CallInfoConfig, Config}, global_state::GlobalStateSnapshot, line_index::{LineEndings, LineIndex, PositionEncoding}, + lsp::{ + semantic_tokens::{self, standard_fallback_type}, + utils::invalid_params_error, + LspError, + }, lsp_ext::{self, SnippetTextEdit}, - lsp_utils::invalid_params_error, - semantic_tokens::{self, standard_fallback_type}, }; pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { @@ -102,7 +106,7 @@ pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSe } pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation { - let value = crate::markdown::format_docs(documentation.as_str()); + let value = format_docs(&documentation); let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }; lsp_types::Documentation::MarkupContent(markup_content) } @@ -413,7 +417,7 @@ pub(crate) fn signature_help( let documentation = call_info.doc.filter(|_| config.docs).map(|doc| { lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, - value: crate::markdown::format_docs(&doc), + value: format_docs(&doc), }) }); @@ -434,10 +438,25 @@ pub(crate) fn signature_help( pub(crate) fn inlay_hint( snap: &GlobalStateSnapshot, + fields_to_resolve: &InlayFieldsToResolve, line_index: &LineIndex, + file_id: FileId, inlay_hint: InlayHint, ) -> Cancellable<lsp_types::InlayHint> { - let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?; + let needs_resolve = inlay_hint.needs_resolve; + let (label, tooltip, mut something_to_resolve) = + inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?; + let text_edits = if needs_resolve && fields_to_resolve.resolve_text_edits { + something_to_resolve |= inlay_hint.text_edit.is_some(); + None + } else { + inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) + }; + let data = if needs_resolve && something_to_resolve { + Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.0 }).unwrap()) + } else { + None + }; Ok(lsp_types::InlayHint { position: match inlay_hint.position { @@ -451,8 +470,8 @@ pub(crate) fn inlay_hint( InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE), _ => None, }, - text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)), - data: None, + text_edits, + data, tooltip, label, }) @@ -460,13 +479,18 @@ pub(crate) fn inlay_hint( fn inlay_hint_label( snap: &GlobalStateSnapshot, + fields_to_resolve: &InlayFieldsToResolve, + needs_resolve: bool, mut label: InlayHintLabel, -) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>)> { - let res = match &*label.parts { +) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>, bool)> { + let mut something_to_resolve = false; + let (label, tooltip) = match &*label.parts { [InlayHintLabelPart { linked_location: None, .. }] => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); - ( - lsp_types::InlayHintLabel::String(text), + let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { + something_to_resolve |= tooltip.is_some(); + None + } else { match tooltip { Some(ide::InlayTooltip::String(s)) => { Some(lsp_types::InlayHintTooltip::String(s)) @@ -478,41 +502,52 @@ fn inlay_hint_label( })) } None => None, - }, - ) + } + }; + (lsp_types::InlayHintLabel::String(text), hint_tooltip) } _ => { let parts = label .parts .into_iter() .map(|part| { - part.linked_location.map(|range| location(snap, range)).transpose().map( - |location| lsp_types::InlayHintLabelPart { - value: part.text, - tooltip: match part.tooltip { - Some(ide::InlayTooltip::String(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::String(s)) - } - Some(ide::InlayTooltip::Markdown(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( - lsp_types::MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: s, - }, - )) - } - None => None, - }, - location, - command: None, - }, - ) + let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { + something_to_resolve |= part.tooltip.is_some(); + None + } else { + match part.tooltip { + Some(ide::InlayTooltip::String(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::String(s)) + } + Some(ide::InlayTooltip::Markdown(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( + lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: s, + }, + )) + } + None => None, + } + }; + let location = if needs_resolve && fields_to_resolve.resolve_label_location { + something_to_resolve |= part.linked_location.is_some(); + None + } else { + part.linked_location.map(|range| location(snap, range)).transpose()? + }; + Ok(lsp_types::InlayHintLabelPart { + value: part.text, + tooltip, + location, + command: None, + }) }) .collect::<Cancellable<_>>()?; (lsp_types::InlayHintLabel::LabelParts(parts), None) } }; - Ok(res) + Ok((label, tooltip, something_to_resolve)) } static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1); @@ -1323,17 +1358,18 @@ pub(crate) fn code_lens( }) } } - AnnotationKind::HasImpls { pos: file_range, data } => { + AnnotationKind::HasImpls { pos, data } => { if !client_commands_config.show_reference { return Ok(()); } - let line_index = snap.file_line_index(file_range.file_id)?; + let line_index = snap.file_line_index(pos.file_id)?; let annotation_range = range(&line_index, annotation.range); - let url = url(snap, file_range.file_id); + let url = url(snap, pos.file_id); + let pos = position(&line_index, pos.offset); let id = lsp_types::TextDocumentIdentifier { uri: url.clone() }; - let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start); + let doc_pos = lsp_types::TextDocumentPositionParams::new(id, pos); let goto_params = lsp_types::request::GotoImplementationParams { text_document_position_params: doc_pos, @@ -1356,7 +1392,7 @@ pub(crate) fn code_lens( command::show_references( implementation_title(locations.len()), &url, - annotation_range.start, + pos, locations, ) }); @@ -1376,28 +1412,24 @@ pub(crate) fn code_lens( })(), }) } - AnnotationKind::HasReferences { pos: file_range, data } => { + AnnotationKind::HasReferences { pos, data } => { if !client_commands_config.show_reference { return Ok(()); } - let line_index = snap.file_line_index(file_range.file_id)?; + let line_index = snap.file_line_index(pos.file_id)?; let annotation_range = range(&line_index, annotation.range); - let url = url(snap, file_range.file_id); + let url = url(snap, pos.file_id); + let pos = position(&line_index, pos.offset); let id = lsp_types::TextDocumentIdentifier { uri: url.clone() }; - let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start); + let doc_pos = lsp_types::TextDocumentPositionParams::new(id, pos); let command = data.map(|ranges| { let locations: Vec<lsp_types::Location> = ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect(); - command::show_references( - reference_title(locations.len()), - &url, - annotation_range.start, - locations, - ) + command::show_references(reference_title(locations.len()), &url, pos, locations) }); acc.push(lsp_types::CodeLens { @@ -1425,8 +1457,8 @@ pub(crate) mod command { use crate::{ global_state::GlobalStateSnapshot, + lsp::to_proto::{location, location_link}, lsp_ext, - to_proto::{location, location_link}, }; pub(crate) fn show_references( @@ -1528,11 +1560,11 @@ pub(crate) fn markup_content( ide::HoverDocFormat::Markdown => lsp_types::MarkupKind::Markdown, ide::HoverDocFormat::PlainText => lsp_types::MarkupKind::PlainText, }; - let value = crate::markdown::format_docs(markup.as_str()); + let value = format_docs(&Documentation::new(markup.into())); lsp_types::MarkupContent { kind, value } } -pub(crate) fn rename_error(err: RenameError) -> crate::LspError { +pub(crate) fn rename_error(err: RenameError) -> LspError { // This is wrong, but we don't have a better alternative I suppose? // https://github.com/microsoft/language-server-protocol/issues/1341 invalid_params_error(err.to_string()) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs index 74e79e8e605..b388b317599 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs @@ -6,10 +6,10 @@ use lsp_types::request::Request; use triomphe::Arc; use crate::{ - from_proto, global_state::GlobalState, line_index::{LineEndings, LineIndex, PositionEncoding}, - lsp_ext, LspError, + lsp::{from_proto, LspError}, + lsp_ext, }; pub(crate) fn invalid_params_error(message: String) -> LspError { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 74036710fa3..cdf41c955d2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -17,11 +17,14 @@ use vfs::FileId; use crate::{ config::Config, + diagnostics::fetch_native_diagnostics, dispatch::{NotificationDispatcher, RequestDispatcher}, - from_proto, global_state::{file_id_to_url, url_to_file_id, GlobalState}, + lsp::{ + from_proto, + utils::{notification_is, Progress}, + }, lsp_ext, - lsp_utils::{notification_is, Progress}, reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress}, }; @@ -317,8 +320,11 @@ impl GlobalState { } // Refresh inlay hints if the client supports it. - if self.config.inlay_hints_refresh() { + if (self.send_hint_refresh_query || self.proc_macro_changed) + && self.config.inlay_hints_refresh() + { self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ()); + self.send_hint_refresh_query = false; } } @@ -420,6 +426,32 @@ impl GlobalState { }); } + fn update_diagnostics(&mut self) { + let db = self.analysis_host.raw_database(); + let subscriptions = self + .mem_docs + .iter() + .map(|path| self.vfs.read().0.file_id(path).unwrap()) + .filter(|&file_id| { + let source_root = db.file_source_root(file_id); + // Only publish diagnostics for files in the workspace, not from crates.io deps + // or the sysroot. + // While theoretically these should never have errors, we have quite a few false + // positives particularly in the stdlib, and those diagnostics would stay around + // forever if we emitted them here. + !db.source_root(source_root).is_library + }) + .collect::<Vec<_>>(); + tracing::trace!("updating notifications for {:?}", subscriptions); + + // Diagnostics are triggered by the user typing + // so we run them on a latency sensitive thread. + self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, { + let snapshot = self.snapshot(); + move || Task::Diagnostics(fetch_native_diagnostics(snapshot, subscriptions)) + }); + } + fn update_status_or_notify(&mut self) { let status = self.current_status(); if self.last_reported_status.as_ref() != Some(&status) { @@ -509,6 +541,7 @@ impl GlobalState { } self.switch_workspaces("fetched build data".to_string()); + self.send_hint_refresh_query = true; (Some(Progress::End), None) } @@ -525,7 +558,7 @@ impl GlobalState { ProcMacroProgress::End(proc_macro_load_result) => { self.fetch_proc_macros_queue.op_completed(true); self.set_proc_macros(proc_macro_load_result); - + self.send_hint_refresh_query = true; (Some(Progress::End), None) } }; @@ -670,6 +703,7 @@ impl GlobalState { } use crate::handlers::request as handlers; + use lsp_types::request as lsp_request; dispatcher // Request handlers that must run on the main thread @@ -682,30 +716,30 @@ impl GlobalState { // are run on the main thread to reduce latency: .on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines) .on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter) - .on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range) + .on_sync::<lsp_request::SelectionRangeRequest>(handlers::handle_selection_range) .on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace) .on_sync::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting) // Formatting should be done immediately as the editor might wait on it, but we can't // put it on the main thread as we do not want the main thread to block on rustfmt. // So we have an extra thread just for formatting requests to make sure it gets handled // as fast as possible. - .on_fmt_thread::<lsp_types::request::Formatting>(handlers::handle_formatting) - .on_fmt_thread::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting) + .on_fmt_thread::<lsp_request::Formatting>(handlers::handle_formatting) + .on_fmt_thread::<lsp_request::RangeFormatting>(handlers::handle_range_formatting) // We can’t run latency-sensitive request handlers which do semantic // 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_types::request::Completion>(handlers::handle_completion) - .on_latency_sensitive::<lsp_types::request::ResolveCompletionItem>( + .on_latency_sensitive::<lsp_request::Completion>(handlers::handle_completion) + .on_latency_sensitive::<lsp_request::ResolveCompletionItem>( handlers::handle_completion_resolve, ) - .on_latency_sensitive::<lsp_types::request::SemanticTokensFullRequest>( + .on_latency_sensitive::<lsp_request::SemanticTokensFullRequest>( handlers::handle_semantic_tokens_full, ) - .on_latency_sensitive::<lsp_types::request::SemanticTokensFullDeltaRequest>( + .on_latency_sensitive::<lsp_request::SemanticTokensFullDeltaRequest>( handlers::handle_semantic_tokens_full_delta, ) - .on_latency_sensitive::<lsp_types::request::SemanticTokensRangeRequest>( + .on_latency_sensitive::<lsp_request::SemanticTokensRangeRequest>( handlers::handle_semantic_tokens_range, ) // All other request handlers @@ -729,29 +763,25 @@ impl GlobalState { .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_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol) - .on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition) - .on::<lsp_types::request::GotoDeclaration>(handlers::handle_goto_declaration) - .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation) - .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition) - .on_no_retry::<lsp_types::request::InlayHintRequest>(handlers::handle_inlay_hints) - .on::<lsp_types::request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve) - .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens) - .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve) - .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range) - .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help) - .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename) - .on::<lsp_types::request::Rename>(handlers::handle_rename) - .on::<lsp_types::request::References>(handlers::handle_references) - .on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight) - .on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare) - .on::<lsp_types::request::CallHierarchyIncomingCalls>( - handlers::handle_call_hierarchy_incoming, - ) - .on::<lsp_types::request::CallHierarchyOutgoingCalls>( - handlers::handle_call_hierarchy_outgoing, - ) - .on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files) + .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) .finish(); @@ -788,77 +818,4 @@ impl GlobalState { .finish(); Ok(()) } - - fn update_diagnostics(&mut self) { - let db = self.analysis_host.raw_database(); - let subscriptions = self - .mem_docs - .iter() - .map(|path| self.vfs.read().0.file_id(path).unwrap()) - .filter(|&file_id| { - let source_root = db.file_source_root(file_id); - // Only publish diagnostics for files in the workspace, not from crates.io deps - // or the sysroot. - // While theoretically these should never have errors, we have quite a few false - // positives particularly in the stdlib, and those diagnostics would stay around - // forever if we emitted them here. - !db.source_root(source_root).is_library - }) - .collect::<Vec<_>>(); - - tracing::trace!("updating notifications for {:?}", subscriptions); - - let snapshot = self.snapshot(); - - // Diagnostics are triggered by the user typing - // so we run them on a latency sensitive thread. - self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, move || { - let _p = profile::span("publish_diagnostics"); - let _ctx = stdx::panic_context::enter("publish_diagnostics".to_owned()); - let diagnostics = subscriptions - .into_iter() - .filter_map(|file_id| { - let line_index = snapshot.file_line_index(file_id).ok()?; - Some(( - file_id, - line_index, - snapshot - .analysis - .diagnostics( - &snapshot.config.diagnostics(), - ide::AssistResolveStrategy::None, - file_id, - ) - .ok()?, - )) - }) - .map(|(file_id, line_index, it)| { - ( - file_id, - it.into_iter() - .map(move |d| lsp_types::Diagnostic { - range: crate::to_proto::range(&line_index, d.range), - severity: Some(crate::to_proto::diagnostic_severity(d.severity)), - code: Some(lsp_types::NumberOrString::String( - d.code.as_str().to_string(), - )), - code_description: Some(lsp_types::CodeDescription { - href: lsp_types::Url::parse(&d.code.url()).unwrap(), - }), - source: Some("rust-analyzer".to_string()), - message: d.message, - related_information: None, - tags: if d.unused { - Some(vec![lsp_types::DiagnosticTag::UNNECESSARY]) - } else { - None - }, - data: None, - }) - .collect::<Vec<_>>(), - ) - }); - Task::Diagnostics(diagnostics.collect()) - }); - } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs deleted file mode 100644 index 58426c66a85..00000000000 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs +++ /dev/null @@ -1,165 +0,0 @@ -//! Transforms markdown -use ide_db::rust_doc::is_rust_fence; - -const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"]; - -pub(crate) fn format_docs(src: &str) -> String { - let mut processed_lines = Vec::new(); - let mut in_code_block = false; - let mut is_rust = false; - - for mut line in src.lines() { - if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) { - continue; - } - - if let Some(header) = RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence)) - { - in_code_block ^= true; - - if in_code_block { - is_rust = is_rust_fence(header); - - if is_rust { - line = "```rust"; - } - } - } - - if in_code_block { - let trimmed = line.trim_start(); - if is_rust && trimmed.starts_with("##") { - line = &trimmed[1..]; - } - } - - processed_lines.push(line); - } - processed_lines.join("\n") -} - -fn code_line_ignored_by_rustdoc(line: &str) -> bool { - let trimmed = line.trim(); - trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t") -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_format_docs_adds_rust() { - let comment = "```\nfn some_rust() {}\n```"; - assert_eq!(format_docs(comment), "```rust\nfn some_rust() {}\n```"); - } - - #[test] - fn test_format_docs_handles_plain_text() { - let comment = "```text\nthis is plain text\n```"; - assert_eq!(format_docs(comment), "```text\nthis is plain text\n```"); - } - - #[test] - fn test_format_docs_handles_non_rust() { - let comment = "```sh\nsupposedly shell code\n```"; - assert_eq!(format_docs(comment), "```sh\nsupposedly shell code\n```"); - } - - #[test] - fn test_format_docs_handles_rust_alias() { - let comment = "```ignore\nlet z = 55;\n```"; - assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```"); - } - - #[test] - fn test_format_docs_handles_complex_code_block_attrs() { - let comment = "```rust,no_run\nlet z = 55;\n```"; - assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```"); - } - - #[test] - fn test_format_docs_handles_error_codes() { - let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```"; - assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```"); - } - - #[test] - fn test_format_docs_skips_comments_in_rust_block() { - let comment = - "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; - assert_eq!(format_docs(comment), "```rust\n#stay1\nstay2\n```"); - } - - #[test] - fn test_format_docs_does_not_skip_lines_if_plain_text() { - let comment = - "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```"; - assert_eq!( - format_docs(comment), - "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```", - ); - } - - #[test] - fn test_format_docs_keeps_comments_outside_of_rust_block() { - let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t"; - assert_eq!(format_docs(comment), comment); - } - - #[test] - fn test_format_docs_preserves_newlines() { - let comment = "this\nis\nmultiline"; - assert_eq!(format_docs(comment), comment); - } - - #[test] - fn test_code_blocks_in_comments_marked_as_rust() { - let comment = r#"```rust -fn main(){} -``` -Some comment. -``` -let a = 1; -```"#; - - assert_eq!( - format_docs(comment), - "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```" - ); - } - - #[test] - fn test_code_blocks_in_comments_marked_as_text() { - let comment = r#"```text -filler -text -``` -Some comment. -``` -let a = 1; -```"#; - - assert_eq!( - format_docs(comment), - "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```" - ); - } - - #[test] - fn test_format_docs_handles_escape_double_hashes() { - let comment = r#"```rust -let s = "foo -## bar # baz"; -```"#; - - assert_eq!(format_docs(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```"); - } - - #[test] - fn test_format_docs_handles_double_hashes_non_rust() { - let comment = r#"```markdown -## A second-level heading -```"#; - assert_eq!(format_docs(comment), "```markdown\n## A second-level heading\n```"); - } -} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 0a2bb822475..3fae08b82e2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -12,6 +12,7 @@ //! correct. Instead, we try to provide a best-effort service. Even if the //! project is currently loading and we don't have a full project model, we //! still want to respond to various requests. +// FIXME: This is a mess that needs some untangling work use std::{iter, mem}; use flycheck::{FlycheckConfig, FlycheckHandle}; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 0bb29e7080f..d5991429899 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -29,7 +29,7 @@ use lsp_types::{ PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams, }; -use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams}; +use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams}; use serde_json::json; use test_utils::skip_slow_tests; @@ -861,6 +861,7 @@ edition = "2021" bar = {path = "../bar"} //- /foo/src/main.rs +#![allow(internal_features)] #![feature(rustc_attrs, decl_macro)] use bar::Bar; @@ -938,7 +939,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { let res = server.send_request::<HoverRequest>(HoverParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("foo/src/main.rs"), - Position::new(11, 9), + Position::new(12, 9), ), work_done_progress_params: Default::default(), }); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 3c52ef5ef7f..e49b5768fa4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -9,7 +9,7 @@ use std::{ use crossbeam_channel::{after, select, Receiver}; use lsp_server::{Connection, Message, Notification, Request}; use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url}; -use rust_analyzer::{config::Config, lsp_ext, main_loop}; +use rust_analyzer::{config::Config, lsp, main_loop}; use serde::Serialize; use serde_json::{json, to_string_pretty, Value}; use test_utils::FixtureWithProjectMeta; @@ -260,9 +260,9 @@ impl Server { Message::Notification(n) if n.method == "experimental/serverStatus" => { let status = n .clone() - .extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus") + .extract::<lsp::ext::ServerStatusParams>("experimental/serverStatus") .unwrap(); - if status.health != lsp_ext::Health::Ok { + if status.health != lsp::ext::Health::Ok { panic!("server errored/warned while loading workspace: {:?}", status.message); } status.quiescent diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs index f230cba2bf8..8b5c92c6602 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -35,7 +35,7 @@ fn check_lsp_extensions_docs() { let expected_hash = { let lsp_ext_rs = sh - .read_file(sourcegen::project_root().join("crates/rust-analyzer/src/lsp_ext.rs")) + .read_file(sourcegen::project_root().join("crates/rust-analyzer/src/lsp/ext.rs")) .unwrap(); stable_hash(lsp_ext_rs.as_str()) }; @@ -45,7 +45,7 @@ fn check_lsp_extensions_docs() { sh.read_file(sourcegen::project_root().join("docs/dev/lsp-extensions.md")).unwrap(); let text = lsp_extensions_md .lines() - .find_map(|line| line.strip_prefix("lsp_ext.rs hash:")) + .find_map(|line| line.strip_prefix("lsp/ext.rs hash:")) .unwrap() .trim(); u64::from_str_radix(text, 16).unwrap() @@ -54,7 +54,7 @@ fn check_lsp_extensions_docs() { if actual_hash != expected_hash { panic!( " -lsp_ext.rs was changed without touching lsp-extensions.md. +lsp/ext.rs was changed without touching lsp-extensions.md. Expected hash: {expected_hash:x} Actual hash: {actual_hash:x} @@ -158,7 +158,7 @@ Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT Apache-2.0/MIT BSD-3-Clause BlueOak-1.0.0 OR MIT OR Apache-2.0 -CC0-1.0 OR Artistic-2.0 +CC0-1.0 ISC MIT MIT / Apache-2.0 diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index ea7ebd85b33..3603560d35c 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -340,10 +340,10 @@ ExprStmt = Expr = ArrayExpr +| AsmExpr | AwaitExpr | BinExpr | BlockExpr -| BoxExpr | BreakExpr | CallExpr | CastExpr @@ -351,6 +351,7 @@ Expr = | ContinueExpr | FieldExpr | ForExpr +| FormatArgsExpr | IfExpr | IndexExpr | Literal @@ -358,6 +359,7 @@ Expr = | MacroExpr | MatchExpr | MethodCallExpr +| OffsetOfExpr | ParenExpr | PathExpr | PrefixExpr @@ -373,6 +375,21 @@ Expr = | LetExpr | UnderscoreExpr +OffsetOfExpr = + Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')' + +AsmExpr = + Attr* 'builtin' '#' 'asm' '(' Expr ')' + +FormatArgsExpr = + Attr* 'builtin' '#' 'format_args' '(' + template:Expr + (',' args:(FormatArgsArg (',' FormatArgsArg)* ','?)? )? + ')' + +FormatArgsArg = + (Name '=')? Expr + MacroExpr = MacroCall @@ -526,9 +543,6 @@ UnderscoreExpr = AwaitExpr = Attr* Expr '.' 'await' -BoxExpr = - Attr* 'box' Expr - //*************************// // Types // //*************************// diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 16448db04f8..7ba0d4dc656 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -805,6 +805,20 @@ impl ArrayExpr { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for AsmExpr {} +impl AsmExpr { + pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) } + pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) } + pub fn asm_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![asm]) } + pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } + pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AwaitExpr { pub(crate) syntax: SyntaxNode, } @@ -823,16 +837,6 @@ impl ast::HasAttrs for BinExpr {} impl BinExpr {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct BoxExpr { - pub(crate) syntax: SyntaxNode, -} -impl ast::HasAttrs for BoxExpr {} -impl BoxExpr { - pub fn box_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![box]) } - pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct BreakExpr { pub(crate) syntax: SyntaxNode, } @@ -916,6 +920,24 @@ impl ForExpr { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FormatArgsExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for FormatArgsExpr {} +impl FormatArgsExpr { + pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) } + pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) } + pub fn format_args_token(&self) -> Option<SyntaxToken> { + support::token(&self.syntax, T![format_args]) + } + pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + pub fn template(&self) -> Option<Expr> { support::child(&self.syntax) } + pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) } + pub fn args(&self) -> AstChildren<FormatArgsArg> { support::children(&self.syntax) } + pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct IfExpr { pub(crate) syntax: SyntaxNode, } @@ -985,6 +1007,24 @@ impl MethodCallExpr { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct OffsetOfExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for OffsetOfExpr {} +impl OffsetOfExpr { + pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) } + pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) } + pub fn offset_of_token(&self) -> Option<SyntaxToken> { + support::token(&self.syntax, T![offset_of]) + } + pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } + pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } + pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) } + pub fn fields(&self) -> AstChildren<NameRef> { support::children(&self.syntax) } + pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ParenExpr { pub(crate) syntax: SyntaxNode, } @@ -1127,6 +1167,16 @@ impl UnderscoreExpr { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FormatArgsArg { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasName for FormatArgsArg {} +impl FormatArgsArg { + pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } + pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StmtList { pub(crate) syntax: SyntaxNode, } @@ -1555,10 +1605,10 @@ pub enum Type { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Expr { ArrayExpr(ArrayExpr), + AsmExpr(AsmExpr), AwaitExpr(AwaitExpr), BinExpr(BinExpr), BlockExpr(BlockExpr), - BoxExpr(BoxExpr), BreakExpr(BreakExpr), CallExpr(CallExpr), CastExpr(CastExpr), @@ -1566,6 +1616,7 @@ pub enum Expr { ContinueExpr(ContinueExpr), FieldExpr(FieldExpr), ForExpr(ForExpr), + FormatArgsExpr(FormatArgsExpr), IfExpr(IfExpr), IndexExpr(IndexExpr), Literal(Literal), @@ -1573,6 +1624,7 @@ pub enum Expr { MacroExpr(MacroExpr), MatchExpr(MatchExpr), MethodCallExpr(MethodCallExpr), + OffsetOfExpr(OffsetOfExpr), ParenExpr(ParenExpr), PathExpr(PathExpr), PrefixExpr(PrefixExpr), @@ -2453,8 +2505,8 @@ impl AstNode for ArrayExpr { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl AstNode for AwaitExpr { - fn can_cast(kind: SyntaxKind) -> bool { kind == AWAIT_EXPR } +impl AstNode for AsmExpr { + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR } fn cast(syntax: SyntaxNode) -> Option<Self> { if Self::can_cast(syntax.kind()) { Some(Self { syntax }) @@ -2464,8 +2516,8 @@ impl AstNode for AwaitExpr { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl AstNode for BinExpr { - fn can_cast(kind: SyntaxKind) -> bool { kind == BIN_EXPR } +impl AstNode for AwaitExpr { + fn can_cast(kind: SyntaxKind) -> bool { kind == AWAIT_EXPR } fn cast(syntax: SyntaxNode) -> Option<Self> { if Self::can_cast(syntax.kind()) { Some(Self { syntax }) @@ -2475,8 +2527,8 @@ impl AstNode for BinExpr { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl AstNode for BoxExpr { - fn can_cast(kind: SyntaxKind) -> bool { kind == BOX_EXPR } +impl AstNode for BinExpr { + fn can_cast(kind: SyntaxKind) -> bool { kind == BIN_EXPR } fn cast(syntax: SyntaxNode) -> Option<Self> { if Self::can_cast(syntax.kind()) { Some(Self { syntax }) @@ -2563,6 +2615,17 @@ impl AstNode for ForExpr { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for FormatArgsExpr { + fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_EXPR } + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for IfExpr { fn can_cast(kind: SyntaxKind) -> bool { kind == IF_EXPR } fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2640,6 +2703,17 @@ impl AstNode for MethodCallExpr { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for OffsetOfExpr { + fn can_cast(kind: SyntaxKind) -> bool { kind == OFFSET_OF_EXPR } + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for ParenExpr { fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_EXPR } fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -2794,6 +2868,17 @@ impl AstNode for UnderscoreExpr { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for FormatArgsArg { + fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG } + fn cast(syntax: SyntaxNode) -> Option<Self> { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for StmtList { fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST } fn cast(syntax: SyntaxNode) -> Option<Self> { @@ -3373,6 +3458,9 @@ impl AstNode for Type { impl From<ArrayExpr> for Expr { fn from(node: ArrayExpr) -> Expr { Expr::ArrayExpr(node) } } +impl From<AsmExpr> for Expr { + fn from(node: AsmExpr) -> Expr { Expr::AsmExpr(node) } +} impl From<AwaitExpr> for Expr { fn from(node: AwaitExpr) -> Expr { Expr::AwaitExpr(node) } } @@ -3382,9 +3470,6 @@ impl From<BinExpr> for Expr { impl From<BlockExpr> for Expr { fn from(node: BlockExpr) -> Expr { Expr::BlockExpr(node) } } -impl From<BoxExpr> for Expr { - fn from(node: BoxExpr) -> Expr { Expr::BoxExpr(node) } -} impl From<BreakExpr> for Expr { fn from(node: BreakExpr) -> Expr { Expr::BreakExpr(node) } } @@ -3406,6 +3491,9 @@ impl From<FieldExpr> for Expr { impl From<ForExpr> for Expr { fn from(node: ForExpr) -> Expr { Expr::ForExpr(node) } } +impl From<FormatArgsExpr> for Expr { + fn from(node: FormatArgsExpr) -> Expr { Expr::FormatArgsExpr(node) } +} impl From<IfExpr> for Expr { fn from(node: IfExpr) -> Expr { Expr::IfExpr(node) } } @@ -3427,6 +3515,9 @@ impl From<MatchExpr> for Expr { impl From<MethodCallExpr> for Expr { fn from(node: MethodCallExpr) -> Expr { Expr::MethodCallExpr(node) } } +impl From<OffsetOfExpr> for Expr { + fn from(node: OffsetOfExpr) -> Expr { Expr::OffsetOfExpr(node) } +} impl From<ParenExpr> for Expr { fn from(node: ParenExpr) -> Expr { Expr::ParenExpr(node) } } @@ -3474,10 +3565,10 @@ impl AstNode for Expr { matches!( kind, ARRAY_EXPR + | ASM_EXPR | AWAIT_EXPR | BIN_EXPR | BLOCK_EXPR - | BOX_EXPR | BREAK_EXPR | CALL_EXPR | CAST_EXPR @@ -3485,6 +3576,7 @@ impl AstNode for Expr { | CONTINUE_EXPR | FIELD_EXPR | FOR_EXPR + | FORMAT_ARGS_EXPR | IF_EXPR | INDEX_EXPR | LITERAL @@ -3492,6 +3584,7 @@ impl AstNode for Expr { | MACRO_EXPR | MATCH_EXPR | METHOD_CALL_EXPR + | OFFSET_OF_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR @@ -3511,10 +3604,10 @@ impl AstNode for Expr { fn cast(syntax: SyntaxNode) -> Option<Self> { let res = match syntax.kind() { ARRAY_EXPR => Expr::ArrayExpr(ArrayExpr { syntax }), + ASM_EXPR => Expr::AsmExpr(AsmExpr { syntax }), AWAIT_EXPR => Expr::AwaitExpr(AwaitExpr { syntax }), BIN_EXPR => Expr::BinExpr(BinExpr { syntax }), BLOCK_EXPR => Expr::BlockExpr(BlockExpr { syntax }), - BOX_EXPR => Expr::BoxExpr(BoxExpr { syntax }), BREAK_EXPR => Expr::BreakExpr(BreakExpr { syntax }), CALL_EXPR => Expr::CallExpr(CallExpr { syntax }), CAST_EXPR => Expr::CastExpr(CastExpr { syntax }), @@ -3522,6 +3615,7 @@ impl AstNode for Expr { CONTINUE_EXPR => Expr::ContinueExpr(ContinueExpr { syntax }), FIELD_EXPR => Expr::FieldExpr(FieldExpr { syntax }), FOR_EXPR => Expr::ForExpr(ForExpr { syntax }), + FORMAT_ARGS_EXPR => Expr::FormatArgsExpr(FormatArgsExpr { syntax }), IF_EXPR => Expr::IfExpr(IfExpr { syntax }), INDEX_EXPR => Expr::IndexExpr(IndexExpr { syntax }), LITERAL => Expr::Literal(Literal { syntax }), @@ -3529,6 +3623,7 @@ impl AstNode for Expr { MACRO_EXPR => Expr::MacroExpr(MacroExpr { syntax }), MATCH_EXPR => Expr::MatchExpr(MatchExpr { syntax }), METHOD_CALL_EXPR => Expr::MethodCallExpr(MethodCallExpr { syntax }), + OFFSET_OF_EXPR => Expr::OffsetOfExpr(OffsetOfExpr { syntax }), PAREN_EXPR => Expr::ParenExpr(ParenExpr { syntax }), PATH_EXPR => Expr::PathExpr(PathExpr { syntax }), PREFIX_EXPR => Expr::PrefixExpr(PrefixExpr { syntax }), @@ -3550,10 +3645,10 @@ impl AstNode for Expr { fn syntax(&self) -> &SyntaxNode { match self { Expr::ArrayExpr(it) => &it.syntax, + Expr::AsmExpr(it) => &it.syntax, Expr::AwaitExpr(it) => &it.syntax, Expr::BinExpr(it) => &it.syntax, Expr::BlockExpr(it) => &it.syntax, - Expr::BoxExpr(it) => &it.syntax, Expr::BreakExpr(it) => &it.syntax, Expr::CallExpr(it) => &it.syntax, Expr::CastExpr(it) => &it.syntax, @@ -3561,6 +3656,7 @@ impl AstNode for Expr { Expr::ContinueExpr(it) => &it.syntax, Expr::FieldExpr(it) => &it.syntax, Expr::ForExpr(it) => &it.syntax, + Expr::FormatArgsExpr(it) => &it.syntax, Expr::IfExpr(it) => &it.syntax, Expr::IndexExpr(it) => &it.syntax, Expr::Literal(it) => &it.syntax, @@ -3568,6 +3664,7 @@ impl AstNode for Expr { Expr::MacroExpr(it) => &it.syntax, Expr::MatchExpr(it) => &it.syntax, Expr::MethodCallExpr(it) => &it.syntax, + Expr::OffsetOfExpr(it) => &it.syntax, Expr::ParenExpr(it) => &it.syntax, Expr::PathExpr(it) => &it.syntax, Expr::PrefixExpr(it) => &it.syntax, @@ -4028,9 +4125,9 @@ impl AstNode for AnyHasAttrs { | TYPE_PARAM | LET_STMT | ARRAY_EXPR + | ASM_EXPR | AWAIT_EXPR | BIN_EXPR - | BOX_EXPR | BREAK_EXPR | CALL_EXPR | CAST_EXPR @@ -4038,12 +4135,14 @@ impl AstNode for AnyHasAttrs { | CONTINUE_EXPR | FIELD_EXPR | FOR_EXPR + | FORMAT_ARGS_EXPR | IF_EXPR | INDEX_EXPR | LITERAL | LOOP_EXPR | MATCH_EXPR | METHOD_CALL_EXPR + | OFFSET_OF_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR @@ -4179,6 +4278,7 @@ impl AstNode for AnyHasName { | VARIANT | CONST_PARAM | TYPE_PARAM + | FORMAT_ARGS_ARG | IDENT_PAT ) } @@ -4620,17 +4720,17 @@ impl std::fmt::Display for ArrayExpr { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for AwaitExpr { +impl std::fmt::Display for AsmExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for BinExpr { +impl std::fmt::Display for AwaitExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for BoxExpr { +impl std::fmt::Display for BinExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } @@ -4670,6 +4770,11 @@ impl std::fmt::Display for ForExpr { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for FormatArgsExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for IfExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -4705,6 +4810,11 @@ impl std::fmt::Display for MethodCallExpr { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for OffsetOfExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ParenExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -4775,6 +4885,11 @@ impl std::fmt::Display for UnderscoreExpr { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for FormatArgsArg { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for StmtList { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs index 4ec388914e6..8e04ab8bedc 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs @@ -130,7 +130,8 @@ impl Expr { // ContinueExpr(_) => (0, 0), - ClosureExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) | BreakExpr(_) => (0, 1), + ClosureExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) | BreakExpr(_) + | OffsetOfExpr(_) | FormatArgsExpr(_) | AsmExpr(_) => (0, 1), RangeExpr(_) => (5, 5), @@ -160,7 +161,7 @@ impl Expr { CastExpr(_) => (25, 26), - BoxExpr(_) | RefExpr(_) | LetExpr(_) | PrefixExpr(_) => (0, 27), + RefExpr(_) | LetExpr(_) | PrefixExpr(_) => (0, 27), AwaitExpr(_) | CallExpr(_) | MethodCallExpr(_) | IndexExpr(_) | TryExpr(_) | MacroExpr(_) => (29, 0), @@ -202,7 +203,6 @@ impl Expr { let rhs = match self { RefExpr(e) => e.expr(), BinExpr(e) => e.rhs(), - BoxExpr(e) => e.expr(), BreakExpr(e) => e.expr(), LetExpr(e) => e.expr(), RangeExpr(e) => e.end(), @@ -279,7 +279,6 @@ impl Expr { CastExpr(e) => e.as_token(), FieldExpr(e) => e.dot_token(), AwaitExpr(e) => e.dot_token(), - BoxExpr(e) => e.box_token(), BreakExpr(e) => e.break_token(), CallExpr(e) => e.arg_list().and_then(|args| args.l_paren_token()), ClosureExpr(e) => e.param_list().and_then(|params| params.l_paren_token()), @@ -293,7 +292,9 @@ impl Expr { YieldExpr(e) => e.yield_token(), YeetExpr(e) => e.do_token(), LetExpr(e) => e.let_token(), - + OffsetOfExpr(e) => e.builtin_token(), + FormatArgsExpr(e) => e.builtin_token(), + AsmExpr(e) => e.builtin_token(), ArrayExpr(_) | TupleExpr(_) | Literal(_) | PathExpr(_) | ParenExpr(_) | IfExpr(_) | WhileExpr(_) | ForExpr(_) | LoopExpr(_) | MatchExpr(_) | BlockExpr(_) | RecordExpr(_) | UnderscoreExpr(_) | MacroExpr(_) => None, @@ -310,12 +311,12 @@ impl Expr { ArrayExpr(_) | AwaitExpr(_) | BlockExpr(_) | CallExpr(_) | CastExpr(_) | ClosureExpr(_) | FieldExpr(_) | IndexExpr(_) | Literal(_) | LoopExpr(_) | MacroExpr(_) | MethodCallExpr(_) | ParenExpr(_) | PathExpr(_) | RecordExpr(_) - | TryExpr(_) | TupleExpr(_) | UnderscoreExpr(_) => false, + | TryExpr(_) | TupleExpr(_) | UnderscoreExpr(_) | OffsetOfExpr(_) + | FormatArgsExpr(_) | AsmExpr(_) => false, // For BinExpr and RangeExpr this is technically wrong -- the child can be on the left... - BinExpr(_) | RangeExpr(_) | BoxExpr(_) | BreakExpr(_) | ContinueExpr(_) - | PrefixExpr(_) | RefExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) - | LetExpr(_) => self + BinExpr(_) | RangeExpr(_) | BreakExpr(_) | ContinueExpr(_) | PrefixExpr(_) + | RefExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) | LetExpr(_) => self .syntax() .parent() .and_then(Expr::cast) diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs index e4db33f1c69..341bda892ba 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs @@ -70,7 +70,19 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield", ], - contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules", "yeet"], + contextual_keywords: &[ + "auto", + "builtin", + "default", + "existential", + "union", + "raw", + "macro_rules", + "yeet", + "offset_of", + "asm", + "format_args", + ], literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING", "C_STRING"], tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"], nodes: &[ @@ -154,7 +166,10 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "RECORD_EXPR", "RECORD_EXPR_FIELD_LIST", "RECORD_EXPR_FIELD", - "BOX_EXPR", + "OFFSET_OF_EXPR", + "ASM_EXPR", + "FORMAT_ARGS_EXPR", + "FORMAT_ARGS_ARG", // postfix "CALL_EXPR", "INDEX_EXPR", diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs index c49c5fa108b..56227fce9b5 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs @@ -623,7 +623,7 @@ fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> { } fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, rule: &Rule) { - if lower_comma_list(acc, grammar, label, rule) { + if lower_seperated_list(acc, grammar, label, rule) { return; } @@ -689,7 +689,7 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r } // (T (',' T)* ','?) -fn lower_comma_list( +fn lower_seperated_list( acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, @@ -699,19 +699,23 @@ fn lower_comma_list( Rule::Seq(it) => it, _ => return false, }; - let (node, repeat, trailing_comma) = match rule.as_slice() { - [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => { - (node, repeat, trailing_comma) + let (node, repeat, trailing_sep) = match rule.as_slice() { + [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => { + (node, repeat, Some(trailing_sep)) } + [Rule::Node(node), Rule::Rep(repeat)] => (node, repeat, None), _ => return false, }; let repeat = match &**repeat { Rule::Seq(it) => it, _ => return false, }; - match repeat.as_slice() { - [comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (), - _ => return false, + if !matches!( + repeat.as_slice(), + [comma, Rule::Node(n)] + if trailing_sep.map_or(true, |it| comma == &**it) && n == node + ) { + return false; } let ty = grammar[*node].name.clone(); let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty))); diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index c765f42447a..573f56b003a 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -899,32 +899,90 @@ pub mod fmt { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } - extern "C" { - type Opaque; - } + mod rt { - #[lang = "format_argument"] - pub struct Argument<'a> { - value: &'a Opaque, - formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, - } + extern "C" { + type Opaque; + } + + #[lang = "format_argument"] + pub struct Argument<'a> { + value: &'a Opaque, + formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, + } + + impl<'a> Argument<'a> { + pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> { + use crate::mem::transmute; + unsafe { Argument { formatter: transmute(f), value: transmute(x) } } + } + } + + #[lang = "format_alignment"] + pub enum Alignment { + Left, + Right, + Center, + Unknown, + } + + #[lang = "format_count"] + pub enum Count { + Is(usize), + Param(usize), + Implied, + } + + #[lang = "format_placeholder"] + pub struct Placeholder { + pub position: usize, + pub fill: char, + pub align: Alignment, + pub flags: u32, + pub precision: Count, + pub width: Count, + } + + impl Placeholder { + pub const fn new( + position: usize, + fill: char, + align: Alignment, + flags: u32, + precision: Count, + width: Count, + ) -> Self; + } + + #[lang = "format_unsafe_arg"] + pub struct UnsafeArg { + _private: (), + } - impl<'a> Argument<'a> { - pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> { - use crate::mem::transmute; - unsafe { Argument { formatter: transmute(f), value: transmute(x) } } + impl UnsafeArg { + pub unsafe fn new() -> Self; } } #[lang = "format_arguments"] pub struct Arguments<'a> { pieces: &'a [&'static str], - args: &'a [Argument<'a>], + fmt: Option<&'a [rt::Placeholder]>, + args: &'a [rt::Argument<'a>], } impl<'a> Arguments<'a> { pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> { - Arguments { pieces, args } + Arguments { pieces, fmt: None, args } + } + + pub fn new_v1_formatted( + pieces: &'a [&'static str], + args: &'a [rt::Argument<'a>], + fmt: &'a [rt::Placeholder], + _unsafe_arg: rt::UnsafeArg, + ) -> Arguments<'a> { + Arguments { pieces, fmt: Some(fmt), args } } } @@ -1294,8 +1352,6 @@ mod macros { /* compiler built-in */ }; } - - pub(crate) use panic; // endregion:panic // region:fmt @@ -1306,7 +1362,20 @@ mod macros { ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; } - pub(crate) use const_format_args; + #[macro_export] + #[rustc_builtin_macro] + macro_rules! format_args { + ($fmt:expr) => {{ /* compiler built-in */ }}; + ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; + } + + #[macro_export] + macro_rules! print { + ($($arg:tt)*) => {{ + $crate::io::_print($crate::format_args!($($arg)*)); + }}; + } + // endregion:fmt // region:derive diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index 95c5142517a..11055f0280a 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -16,7 +16,7 @@ tracing = "0.1.35" walkdir = "2.3.2" crossbeam-channel = "0.5.5" # We demand 5.1.0 as any higher version pulls in a new windows-sys dupe -notify = "=5.1.0" +notify = "6.1.1" stdx.workspace = true vfs.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs index fe3dfe61968..06004adad34 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs @@ -184,6 +184,11 @@ impl Vfs { mem::take(&mut self.changes) } + /// Provides a panic-less way to verify file_id validity. + pub fn exists(&self, file_id: FileId) -> bool { + self.get(file_id).is_some() + } + /// Returns the id associated with `path` /// /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a diff --git a/src/tools/rust-analyzer/docs/dev/architecture.md b/src/tools/rust-analyzer/docs/dev/architecture.md index 895de5798ac..b7d585cafb3 100644 --- a/src/tools/rust-analyzer/docs/dev/architecture.md +++ b/src/tools/rust-analyzer/docs/dev/architecture.md @@ -268,7 +268,7 @@ They are independent from the rest of the code. And it also handles the actual parsing and expansion of declarative macro (a-la "Macros By Example" or mbe). For proc macros, the client-server model are used. -We pass an argument `--proc-macro` to `rust-analyzer` binary to start a separate process (`proc_macro_srv`). +We start a separate process (`proc_macro_srv`) which loads and runs the proc-macros for us. And the client (`proc_macro_api`) provides an interface to talk to that server separately. And then token trees are passed from client, and the server will load the corresponding dynamic library (which built by `cargo`). diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 024acb87709..0801e988f5c 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ <!--- -lsp_ext.rs hash: 149a5be3c5e469d1 +lsp/ext.rs hash: 121482ee911854da 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: @@ -322,7 +322,7 @@ Position[] ```rust fn main() { - let x: Vec<()>/*cursor here*/ = vec![] + let x: Vec<()>/*cursor here*/ = vec![]; } ``` @@ -362,7 +362,7 @@ interface RunnablesParams { ```typescript interface Runnable { label: string; - /// If this Runnable is associated with a specific function/module, etc, the location of this item + /// If this Runnable is associated with a specific function/module, etc., the location of this item location?: LocationLink; /// Running things is necessary technology specific, `kind` needs to be advertised via server capabilities, // the type of `args` is specific to `kind`. The actual running is handled by the client. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 44f1b81675a..233e7bf44b1 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -471,16 +471,13 @@ "default": false, "type": "boolean" }, - "rust-analyzer.discoverProjectCommand": { - "markdownDescription": "Sets the command that rust-analyzer uses to generate `rust-project.json` files. This command should only be used\n if a build system like Buck or Bazel is also in use. The command must accept files as arguments and return \n a rust-project.json over stdout.", + "rust-analyzer.discoverProjectRunner": { + "markdownDescription": "Sets the extension responsible for determining which extension the rust-analyzer extension uses to generate `rust-project.json` files. This should should only be used\n if a build system like Buck or Bazel is also in use.", "default": null, "type": [ "null", - "array" - ], - "items": { - "type": "string" - } + "string" + ] }, "rust-analyzer.showUnlinkedFileNotification": { "markdownDescription": "Whether to show a notification for unlinked files asking the user to add the corresponding Cargo.toml to the linked projects setting.", diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index aba37bac27d..245557b1e88 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -3,7 +3,7 @@ import * as lc from "vscode-languageclient"; import * as ra from "./lsp_ext"; import * as path from "path"; -import { type Ctx, type Cmd, type CtxInit, discoverWorkspace } from "./ctx"; +import type { Ctx, Cmd, CtxInit } from "./ctx"; import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets"; import { spawnSync } from "child_process"; import { type RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run"; @@ -871,22 +871,16 @@ export function rebuildProcMacros(ctx: CtxInit): Cmd { export function addProject(ctx: CtxInit): Cmd { return async () => { - const discoverProjectCommand = ctx.config.discoverProjectCommand; - if (!discoverProjectCommand) { + const extensionName = ctx.config.discoverProjectRunner; + // this command shouldn't be enabled in the first place if this isn't set. + if (!extensionName) { return; } - const workspaces: JsonProject[] = await Promise.all( - vscode.workspace.textDocuments - .filter(isRustDocument) - .map(async (file): Promise<JsonProject> => { - return discoverWorkspace([file], discoverProjectCommand, { - cwd: path.dirname(file.uri.fsPath), - }); - }), - ); + const command = `${extensionName}.discoverWorkspaceCommand`; + const project: JsonProject = await vscode.commands.executeCommand(command); - ctx.addToDiscoveredWorkspaces(workspaces); + ctx.addToDiscoveredWorkspaces([project]); // this is a workaround to avoid needing writing the `rust-project.json` into // a workspace-level VS Code-specific settings folder. We'd like to keep the diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index 39e2f767c76..9821aee6f92 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -253,8 +253,8 @@ export class Config { return this.get<boolean>("trace.extension"); } - get discoverProjectCommand() { - return this.get<string[] | undefined>("discoverProjectCommand"); + get discoverProjectRunner(): string | undefined { + return this.get<string | undefined>("discoverProjectRunner"); } get problemMatcher(): string[] { diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 363a7a82e68..904efa4d5eb 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -1,12 +1,10 @@ import * as vscode from "vscode"; import type * as lc from "vscode-languageclient/node"; import * as ra from "./lsp_ext"; -import * as path from "path"; import { Config, prepareVSCodeConfig } from "./config"; import { createClient } from "./client"; import { - executeDiscoverProject, isDocumentInWorkspace, isRustDocument, isRustEditor, @@ -24,7 +22,6 @@ import { import { execRevealDependency } from "./commands"; import { PersistentState } from "./persistent_state"; import { bootstrap } from "./bootstrap"; -import type { ExecOptions } from "child_process"; // We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if // only those are in use. We use "Empty" to represent these scenarios @@ -58,17 +55,6 @@ export function fetchWorkspace(): Workspace { : { kind: "Workspace Folder" }; } -export async function discoverWorkspace( - files: readonly vscode.TextDocument[], - command: string[], - options: ExecOptions, -): Promise<JsonProject> { - const paths = files.map((f) => `"${f.uri.fsPath}"`).join(" "); - const joinedCommand = command.join(" "); - const data = await executeDiscoverProject(`${joinedCommand} ${paths}`, options); - return JSON.parse(data) as JsonProject; -} - export type CommandFactory = { enabled: (ctx: CtxInit) => Cmd; disabled?: (ctx: Ctx) => Cmd; @@ -200,6 +186,12 @@ export class Ctx { }; let rawInitializationOptions = vscode.workspace.getConfiguration("rust-analyzer"); + if (this.config.discoverProjectRunner) { + const command = `${this.config.discoverProjectRunner}.discoverWorkspaceCommand`; + log.info(`running command: ${command}`); + const project: JsonProject = await vscode.commands.executeCommand(command); + this.addToDiscoveredWorkspaces([project]); + } if (this.workspace.kind === "Detached Files") { rawInitializationOptions = { @@ -208,21 +200,6 @@ export class Ctx { }; } - const discoverProjectCommand = this.config.discoverProjectCommand; - if (discoverProjectCommand) { - const workspaces: JsonProject[] = await Promise.all( - vscode.workspace.textDocuments - .filter(isRustDocument) - .map(async (file): Promise<JsonProject> => { - return discoverWorkspace([file], discoverProjectCommand, { - cwd: path.dirname(file.uri.fsPath), - }); - }), - ); - - this.addToDiscoveredWorkspaces(workspaces); - } - const initializationOptions = prepareVSCodeConfig( rawInitializationOptions, (key, obj) => { diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts index 0414ea0f806..51f921a2962 100644 --- a/src/tools/rust-analyzer/editors/code/src/util.ts +++ b/src/tools/rust-analyzer/editors/code/src/util.ts @@ -154,22 +154,6 @@ export function execute(command: string, options: ExecOptions): Promise<string> }); } -export function executeDiscoverProject(command: string, options: ExecOptions): Promise<string> { - options = Object.assign({ maxBuffer: 10 * 1024 * 1024 }, options); - log.info(`running command: ${command}`); - return new Promise((resolve, reject) => { - exec(command, options, (err, stdout, _) => { - if (err) { - log.error(err); - reject(err); - return; - } - - resolve(stdout.trimEnd()); - }); - }); -} - export class LazyOutputChannel implements vscode.OutputChannel { constructor(name: string) { this.name = name; diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index e1c49db39d5..7ec3247e943 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.7.3" +version = "0.7.4" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" @@ -9,8 +9,7 @@ edition = "2021" [dependencies] log = "0.4.17" serde_json = "1.0.96" -# See https://github.com/serde-rs/serde/issues/2538#issuecomment-1684517372 for why we pin serde -serde = { version = "1.0.156, < 1.0.172", features = ["derive"] } +serde = { version = "1.0.156", features = ["derive"] } crossbeam-channel = "0.5.6" [dev-dependencies] diff --git a/src/tools/rust-analyzer/xtask/src/flags.rs b/src/tools/rust-analyzer/xtask/src/flags.rs index 7720ad69fe0..e52cbfca3e6 100644 --- a/src/tools/rust-analyzer/xtask/src/flags.rs +++ b/src/tools/rust-analyzer/xtask/src/flags.rs @@ -114,6 +114,7 @@ pub enum MeasurementType { AnalyzeRipgrep, AnalyzeWebRender, AnalyzeDiesel, + AnalyzeHyper, } impl FromStr for MeasurementType { @@ -122,13 +123,26 @@ impl FromStr for MeasurementType { match s { "build" => Ok(Self::Build), "self" => Ok(Self::AnalyzeSelf), - "ripgrep" => Ok(Self::AnalyzeRipgrep), - "webrender" => Ok(Self::AnalyzeWebRender), - "diesel" => Ok(Self::AnalyzeDiesel), + "ripgrep-13.0.0" => Ok(Self::AnalyzeRipgrep), + "webrender-2022" => Ok(Self::AnalyzeWebRender), + "diesel-1.4.8" => Ok(Self::AnalyzeDiesel), + "hyper-0.14.18" => Ok(Self::AnalyzeHyper), _ => Err("Invalid option".to_string()), } } } +impl AsRef<str> for MeasurementType { + fn as_ref(&self) -> &str { + match self { + Self::Build => "build", + Self::AnalyzeSelf => "self", + Self::AnalyzeRipgrep => "ripgrep-13.0.0", + Self::AnalyzeWebRender => "webrender-2022", + Self::AnalyzeDiesel => "diesel-1.4.8", + Self::AnalyzeHyper => "hyper-0.14.18", + } + } +} #[derive(Debug)] pub struct Metrics { diff --git a/src/tools/rust-analyzer/xtask/src/metrics.rs b/src/tools/rust-analyzer/xtask/src/metrics.rs index e4710260409..59d41d8e4b8 100644 --- a/src/tools/rust-analyzer/xtask/src/metrics.rs +++ b/src/tools/rust-analyzer/xtask/src/metrics.rs @@ -29,46 +29,38 @@ impl flags::Metrics { let _env = sh.push_env("RA_METRICS", "1"); - let filename = match self.measurement_type { - Some(ms) => match ms { - MeasurementType::Build => { - metrics.measure_build(sh)?; - "build.json" - } - MeasurementType::AnalyzeSelf => { - metrics.measure_analysis_stats_self(sh)?; - "self.json" - } - MeasurementType::AnalyzeRipgrep => { - metrics.measure_analysis_stats(sh, "ripgrep")?; - "ripgrep.json" - } - MeasurementType::AnalyzeWebRender => { - { - // https://github.com/rust-lang/rust-analyzer/issues/9997 - let _d = sh.push_dir("target/rustc-perf/collector/benchmarks/webrender"); - cmd!(sh, "cargo update -p url --precise 1.6.1").run()?; + let name = match &self.measurement_type { + Some(ms) => { + let name = ms.as_ref(); + match ms { + MeasurementType::Build => { + metrics.measure_build(sh)?; } - metrics.measure_analysis_stats(sh, "webrender")?; - "webrender.json" - } - MeasurementType::AnalyzeDiesel => { - metrics.measure_analysis_stats(sh, "diesel/diesel")?; - "diesel.json" - } - }, + MeasurementType::AnalyzeSelf => { + metrics.measure_analysis_stats_self(sh)?; + } + MeasurementType::AnalyzeRipgrep + | MeasurementType::AnalyzeWebRender + | MeasurementType::AnalyzeDiesel + | MeasurementType::AnalyzeHyper => { + metrics.measure_analysis_stats(sh, name)?; + } + }; + name + } None => { metrics.measure_build(sh)?; metrics.measure_analysis_stats_self(sh)?; - metrics.measure_analysis_stats(sh, "ripgrep")?; - metrics.measure_analysis_stats(sh, "webrender")?; - metrics.measure_analysis_stats(sh, "diesel/diesel")?; - "all.json" + metrics.measure_analysis_stats(sh, MeasurementType::AnalyzeRipgrep.as_ref())?; + metrics.measure_analysis_stats(sh, MeasurementType::AnalyzeWebRender.as_ref())?; + metrics.measure_analysis_stats(sh, MeasurementType::AnalyzeDiesel.as_ref())?; + metrics.measure_analysis_stats(sh, MeasurementType::AnalyzeHyper.as_ref())?; + "all" } }; let mut file = - fs::File::options().write(true).create(true).open(format!("target/{}", filename))?; + fs::File::options().write(true).create(true).open(format!("target/{}.json", name))?; writeln!(file, "{}", metrics.json())?; eprintln!("{metrics:#?}"); Ok(()) @@ -93,7 +85,7 @@ impl Metrics { self.measure_analysis_stats_path( sh, bench, - &format!("./target/rustc-perf/collector/benchmarks/{bench}"), + &format!("./target/rustc-perf/collector/compile-benchmarks/{bench}"), ) } fn measure_analysis_stats_path( @@ -102,6 +94,7 @@ impl Metrics { name: &str, path: &str, ) -> anyhow::Result<()> { + assert!(Path::new(path).exists(), "unable to find bench in {path}"); eprintln!("\nMeasuring analysis-stats/{name}"); let output = cmd!(sh, "./target/release/rust-analyzer -q analysis-stats {path}").read()?; for (metric, value, unit) in parse_metrics(&output) { @@ -145,7 +138,7 @@ impl Metrics { let host = Host::new(sh)?; let timestamp = SystemTime::now(); let revision = cmd!(sh, "git rev-parse HEAD").read()?; - let perf_revision = "c52ee623e231e7690a93be88d943016968c1036b".into(); + let perf_revision = "a584462e145a0c04760fd9391daefb4f6bd13a99".into(); Ok(Metrics { host, timestamp, revision, perf_revision, metrics: BTreeMap::new() }) } |
